1 /** 2 * --------- 3 * |.##> <##.| Open Smart Card Development Platform (www.openscdp.org) 4 * |# #| 5 * |# #| Copyright (c) 1999-2009 CardContact Software & System Consulting 6 * |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de) 7 * --------- 8 * 9 * This file is part of OpenSCDP. 10 * 11 * OpenSCDP is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 * 15 * OpenSCDP is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with OpenSCDP; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 * 24 * @fileoverview Implementation of the Extended Access Control protocol in version 2.0 25 */ 26 27 // Imports 28 var CVC = require('scsh/eac/CVC').CVC; 29 var PublicKeyReference = require('scsh/eac/PublicKeyReference').PublicKeyReference; 30 31 var PACEDomainParameterInfo = require('scsh/eac/PACE').PACEDomainParameterInfo; 32 var PACEInfo = require('scsh/eac/PACE').PACEInfo; 33 var PACE = require('scsh/eac/PACE').PACE; 34 35 var ChipAuthenticationInfo = require('scsh/eac/ChipAuthentication').ChipAuthenticationInfo; 36 var ChipAuthenticationDomainParameterInfo = require('scsh/eac/ChipAuthentication').ChipAuthenticationDomainParameterInfo; 37 var ChipAuthenticationPublicKeyInfo = require('scsh/eac/ChipAuthentication').ChipAuthenticationPublicKeyInfo; 38 var ChipAuthentication = require('scsh/eac/ChipAuthentication').ChipAuthentication; 39 40 var RestrictedIdentification = require('scsh/eac/RestrictedIdentification').RestrictedIdentification; 41 var RestrictedIdentificationInfo = require('scsh/eac/RestrictedIdentification').RestrictedIdentificationInfo; 42 var RestrictedIdentificationDomainParameterInfo = require('scsh/eac/RestrictedIdentification').RestrictedIdentificationDomainParameterInfo; 43 44 45 46 /** 47 * Create a protocol object for EAC 48 * 49 * @class Class implementing support for Extended Access Control V2 50 * @constructor 51 * @param {Crypto} crypto the crypto provider 52 * @param {Card} card the card object 53 */ 54 function EAC20(crypto, card) { 55 this.crypto = crypto; 56 this.card = card; 57 this.sm = null; 58 this.includeDPinAuthToken = false; // Standard for PACE version >= 2 59 60 this.PACEInfos = new Array(); 61 this.PACEDPs = new Array(); 62 63 this.CAInfos = new Array(); 64 this.CADPs = new Array(); 65 this.CAPublicKeys = new Array(); 66 67 this.RIInfos = new Array(); 68 this.maxRData = 0; 69 this.maxCData = 239; // Used for update binary 70 this.useFID = false; // Use FIDs rather than SFIs 71 this.verbose = false; 72 this.selectADFwithoutSM = false; // Send SELECT ADF without SM (for applets) 73 } 74 75 exports.EAC20 = EAC20; 76 77 78 /** PACE PWD is the hashed MRZ */ 79 EAC20.ID_MRZ = 1; 80 /** PACE PWD is the CAN */ 81 EAC20.ID_CAN = 2; 82 /** PACE PWD is the PIN */ 83 EAC20.ID_PIN = 3; 84 /** PACE PWD is the PUK */ 85 EAC20.ID_PUK = 4; 86 87 EAC20.AID_LDS = new ByteString("A0000002471001", HEX); 88 EAC20.AID_eID = new ByteString("E80704007F00070302", HEX); 89 EAC20.AID_eSign = new ByteString("A000000167455349474E", HEX); 90 91 EAC20.SFI_CVCA = 0x1C; 92 EAC20.SFI_ChipSecurity = 0x1B; 93 EAC20.SFI_CardAccess = 0x1C; 94 EAC20.SFI_CardSecurity = 0x1D; 95 EAC20.SFI_COM = 0x1E; 96 97 98 EAC20.prototype.log = function(str) { 99 if (this.verbose) { 100 GPSystem.trace(str); 101 } 102 } 103 104 105 106 /** 107 * Process a list of security infos from EF.CardInfo, EF.CardSecurity or EF.ChipSecurity 108 * 109 * @param {ASN1} si the security info ASN Sequence 110 * @param {boolean} fromCardSecurity true if security infos are taken from EF.CardSecurity, EF.ChipSecurity or EF.DG14 111 */ 112 EAC20.prototype.processSecurityInfos = function(si, fromCardSecurity) { 113 this.log("SecurityInfos:"); 114 this.log(si); 115 116 var id_PACE = new ByteString("id-PACE", OID); 117 var id_PACE_DH_GM = new ByteString("id-PACE-DH-GM", OID); 118 var id_PACE_ECDH_GM = new ByteString("id-PACE-ECDH-GM", OID); 119 var id_PACE_DH_IM = new ByteString("id-PACE-DH-IM", OID); 120 var id_PACE_ECDH_IM = new ByteString("id-PACE-ECDH-IM", OID); 121 var id_PK_ECDH = new ByteString("id-PK-ECDH", OID); 122 var id_CA = new ByteString("id-CA", OID); 123 var id_CA_DH = new ByteString("id-CA-DH", OID); 124 var id_CA_ECDH = new ByteString("id-CA-ECDH", OID); 125 var id_TA = new ByteString("id-TA", OID); 126 var id_PT = new ByteString("id-PT", OID); 127 128 for (var i = 0; i < si.elements; i++) { 129 var o = si.get(i); 130 assert((o.elements >= 1) && (o.elements <= 3)); 131 132 var oid = o.get(0); 133 assert(oid.tag == ASN1.OBJECT_IDENTIFIER); 134 135 if (oid.value.startsWith(id_TA) == id_TA.length) { 136 this.log("TA : " + o); 137 } else if (oid.value.startsWith(id_PACE) == id_PACE.length) { 138 if (oid.value.equals(id_PACE_DH_GM) || 139 oid.value.equals(id_PACE_ECDH_GM) || 140 oid.value.equals(id_PACE_DH_IM) || 141 oid.value.equals(id_PACE_ECDH_GM)) { 142 this.log("PaceDomainParameterInfo : " + o); 143 144 var pdpi = new PACEDomainParameterInfo(o); 145 this.log(pdpi); 146 147 var id = pdpi.parameterId; 148 149 if (typeof(id) == "undefined") { 150 id = 0; 151 } 152 153 if (!fromCardSecurity && (typeof(this.PACEDPs[id]) != "undefined")) { 154 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate parameterId " + id + " for PACEDomainParameter"); 155 } 156 157 this.PACEDPs[id] = pdpi; 158 } else { 159 this.log("PaceInfo : " + o); 160 161 var pi = new PACEInfo(o); 162 this.log(pi); 163 164 var id = pi.parameterId; 165 166 if (typeof(id) == "undefined") { 167 id = 0; 168 } 169 170 if (pi.version == 1) { 171 if (!fromCardSecurity && (typeof(this.PACEInfos[id]) != "undefined")) { 172 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate parameterId " + id + " for PACEInfo"); 173 } 174 } else { 175 id = 0; 176 } 177 this.PACEInfos[id] = pi; 178 } 179 } else if (oid.value.equals(id_PK_ECDH)) { 180 this.log("ChipAuthenticationPublicKeyInfo : " + o); 181 182 var capki = new ChipAuthenticationPublicKeyInfo(o); 183 this.log(capki); 184 185 var id = capki.keyId; 186 187 if (typeof(id) == "undefined") { 188 this.log("Using default key id 0"); 189 id = 0; 190 } 191 192 if (!fromCardSecurity && (typeof(this.CAPublicKeys[id]) != "undefined")) { 193 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate keyId " + id + " for ChipAuthenticationPublicKeyInfo"); 194 } 195 196 this.CAPublicKeys[id] = capki; 197 } else if (oid.value.startsWith(id_CA) == id_CA.length) { 198 if (oid.value.equals(id_CA_DH) || 199 oid.value.equals(id_CA_ECDH)) { 200 this.log("ChipAuthenticationDomainParameterInfo : " + o); 201 202 var cadpi = new ChipAuthenticationDomainParameterInfo(o); 203 this.log(cadpi); 204 205 var id = cadpi.keyId; 206 207 if (typeof(id) == "undefined") { 208 this.log("Using default key id 0"); 209 id = 0; 210 } 211 212 if (!fromCardSecurity && (typeof(this.CADPs[id]) != "undefined")) { 213 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate keyId " + id + " for ChipAuthenticationDomainParameter"); 214 } 215 216 this.CADPs[id] = cadpi; 217 } else { 218 this.log("ChipAuthenticationInfo : " + o); 219 220 var cai = new ChipAuthenticationInfo(o); 221 this.log(cai); 222 223 var id = cai.keyId; 224 225 if (typeof(id) == "undefined") { 226 this.log("Using default key id 0"); 227 id = 0; 228 } 229 230 if (!fromCardSecurity && (typeof(this.CAInfos[id]) != "undefined")) { 231 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate keyId " + id + " for ChipAuthenticationInfo"); 232 } 233 234 this.CAInfos[id] = cai; 235 } 236 } else if (oid.value.startsWith(RestrictedIdentification.id_RI) == RestrictedIdentification.id_RI.length) { 237 if (oid.value.equals(RestrictedIdentification.id_RI_DH) || 238 oid.value.equals(RestrictedIdentification.id_RI_ECDH)) { 239 this.log("RestrictedIdentificationDomainParameterInfo : " + o); 240 241 var ridpi = new RestrictedIdentificationDomainParameterInfo(o); 242 this.log(ridpi); 243 244 if (!fromCardSecurity && (typeof(this.RIDP) != "undefined")) { 245 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate RestrictedIdentificationDomainParameter"); 246 this.RIDP = ridpi; 247 } 248 } else { 249 this.log("RestrictedIdentificationInfo : " + o); 250 251 var rii = new RestrictedIdentificationInfo(o); 252 this.log(rii); 253 254 var id = rii.keyId; 255 256 if (typeof(id) == "undefined") { 257 this.log("Using default key id 0"); 258 id = 0; 259 } 260 261 if (!fromCardSecurity && (typeof(this.RIInfos[id]) != "undefined")) { 262 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate keyId " + id + " for RestrictedIdentificationInfo"); 263 } 264 265 this.RIInfos[id] = rii; 266 } 267 } else if (oid.value.equals(id_PT)) { 268 this.log("PrivilegedTerminalInfo : " + o); 269 this.processSecurityInfos(o.get(1), fromCardSecurity); 270 } 271 } 272 } 273 274 275 276 /** 277 * Select EF using FID and read elementary file 278 * 279 * @param {ByteString} fid 2 byte file identifier 280 * @type ByteString 281 * @return the content of the EF 282 */ 283 EAC20.prototype.readEFwithFID = function(fid) { 284 assert(fid.length == 2, "Length of fid must be 2 bytes"); 285 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xA4, 0x02, 0x0C, fid, [0x9000]); 286 287 var bb = new ByteBuffer(); 288 var offset = 0; 289 do { 290 var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xB0, offset >> 8, offset & 0xFF, this.maxRData); 291 bb.append(rsp); 292 offset += rsp.length; 293 } while ((this.card.SW == 0x9000) && (rsp.length > 0)); 294 295 return bb.toByteString(); 296 } 297 298 299 300 /** 301 * Select EF using FID and update content 302 * 303 * @param {ByteString} fid 2 byte file identifier 304 * @param {ByteString} data data to be written 305 */ 306 EAC20.prototype.updateEFwithFID = function(fid, data) { 307 assert(fid.length == 2, "Length of fid must be 2 bytes"); 308 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xA4, 0x02, 0x0C, fid, [0x9000]); 309 310 var offset = 0; 311 while (offset < data.length) { 312 var len = data.length - offset; 313 len = this.maxCData < len ? this.maxCData : len; 314 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xD6, offset >> 8, offset & 0xFF, data.bytes(offset, len), [0x9000]); 315 offset += len; 316 } 317 } 318 319 320 321 /** 322 * Select and read EF using SFI 323 * 324 * @param {Number} short file identifier 325 * @type ByteString 326 * @return the content of the EF 327 */ 328 EAC20.prototype.readEFwithSFI = function(sfi) { 329 assert(typeof(sfi) == "number", "Parameter must be a number"); 330 331 if (this.useFID) { 332 var fid = ByteString.valueOf(0x0100 + sfi, 2); 333 return this.readEFwithFID(fid); 334 } 335 336 var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xB0, 0x80 | sfi, 0x00, this.maxRData, [0x9000]); 337 338 var bb = new ByteBuffer(rsp); 339 var offset = bb.length; 340 do { 341 var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xB0, offset >> 8, offset & 0xFF, this.maxRData); 342 bb.append(rsp); 343 offset += rsp.length; 344 } while ((this.card.SW == 0x9000) && (rsp.length > 0)); 345 346 return bb.toByteString(); 347 } 348 349 350 351 /** 352 * Select and read a TLV encoded EF using SFI 353 * 354 * @param {Number} short file identifier 355 * @type ByteString 356 * @return the TLV content of the EF 357 */ 358 EAC20.prototype.readTLVEFwithSFI = function(sfi) { 359 assert(typeof(sfi) == "number", "Parameter must be a number"); 360 361 if (this.useFID) { 362 var fid = ByteString.valueOf(0x0100 + sfi, 2); 363 return this.readEFwithFID(fid); 364 } 365 366 var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xB0, 0x80 | sfi, 0x00, this.maxRData, [0x9000]); 367 368 var bb = new ByteBuffer(rsp); 369 var offset = bb.length; 370 do { 371 var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xB0, offset >> 8, offset & 0xFF, this.maxRData); 372 bb.append(rsp); 373 offset += rsp.length; 374 } while ((this.card.SW == 0x9000) && (rsp.length > 0)); 375 376 return bb.toByteString(); 377 } 378 379 380 381 /** 382 * Select EF using SFI and update content 383 * 384 * @param {Number} short file identifier 385 * @param {ByteString} data data to be written 386 */ 387 EAC20.prototype.updateEFwithSFI = function(sfi, data) { 388 assert(typeof(sfi) == "number", "Parameter must be a number"); 389 390 if (this.useFID) { 391 var fid = ByteString.valueOf(0x0100 + sfi, 2); 392 return this.updateEFwithFID(fid, data); 393 } 394 395 var offset = 0; 396 var p1 = 0x80 | sfi; 397 while (offset < data.length) { 398 var len = data.length - offset; 399 len = this.maxCData < len ? this.maxCData : len; 400 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xD6, p1, offset & 0xFF, data.bytes(offset, len), [0x9000]); 401 offset += len; 402 p1 = offset >> 8; 403 } 404 } 405 406 407 408 /** 409 * Select application DF 410 * 411 * @param {ByteString} aid the application identifier 412 */ 413 EAC20.prototype.selectADF = function(aid) { 414 if (this.selectADFwithoutSM) { 415 this.card.sendApdu(0x00, 0xA4, 0x04, 0x0C, aid, [0x9000]); 416 } else { 417 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xA4, 0x04, 0x0C, aid, [0x9000]); 418 } 419 } 420 421 422 423 /** 424 * Select ePass LDS Application 425 */ 426 EAC20.prototype.selectLDS = function() { 427 this.selectADF(EAC20.AID_LDS); 428 } 429 430 431 432 /** 433 * Select eID Application 434 */ 435 EAC20.prototype.select_eID = function() { 436 this.selectADF(EAC20.AID_eID); 437 } 438 439 440 441 /** 442 * Select eSign Application 443 */ 444 EAC20.prototype.select_eSign = function() { 445 this.selectADF(EAC20.AID_eSign); 446 } 447 448 449 450 /** 451 * Read EF.DG14 and process security infos 452 * 453 */ 454 EAC20.prototype.readDG14 = function() { 455 var cibin = this.readEFwithSFI(14); 456 var citlv = new ASN1(cibin); 457 this.log(citlv); 458 459 this.processSecurityInfos(citlv.get(0), true); 460 } 461 462 463 464 /** 465 * Read EF.CVCA and process contained CARs 466 * 467 */ 468 EAC20.prototype.readCVCA = function() { 469 var cvcabin = this.readEFwithSFI(EAC20.SFI_CVCA); 470 assert(cvcabin.byteAt(0) == 0x42); 471 472 var cvca = new ASN1(cvcabin); 473 this.lastCAR = new PublicKeyReference(cvca.value); 474 475 if (cvcabin.byteAt(cvca.size) == 0x42) { 476 var cvca = new ASN1(cvcabin.bytes(cvca.size)); 477 this.previousCAR = new PublicKeyReference(cvca.value); 478 } 479 } 480 481 482 483 /** 484 * Read EF.CardAccess and process security infos 485 * 486 */ 487 EAC20.prototype.readCardAccess = function() { 488 var cibin = this.readEFwithSFI(EAC20.SFI_CardAccess); 489 var citlv = new ASN1(cibin); 490 this.log(citlv); 491 492 this.processSecurityInfos(citlv, false); 493 } 494 495 // Deprecated 496 EAC20.prototype.readCardInfo = EAC20.prototype.readCardAccess; 497 498 499 500 /** 501 * Read EF.CardSecurity and process security infos 502 */ 503 EAC20.prototype.readCardSecurity = function() { 504 var csbin = this.readEFwithSFI(EAC20.SFI_CardSecurity); 505 var cstlv = new ASN1(csbin); 506 this.log("EF.CardSecurity:"); 507 this.log(cstlv); 508 509 var cms = new CMSSignedData(csbin); 510 511 var certs = cms.getSignedDataCertificates(); 512 513 this.log("EF.CardSecurity Certificates:"); 514 for (var i = 0; i < certs.length; i++) { 515 this.log(certs[i]); 516 } 517 518 this.log("DocSigner Signature is " + (cms.isSignerInfoSignatureValid(0) ? "valid" : "not valid")); 519 520 var data = cms.getSignedContent(); 521 522 this.log(data); 523 524 var cstlv = new ASN1(data); 525 526 this.log(cstlv); 527 528 this.processSecurityInfos(cstlv, true); 529 } 530 531 532 533 /** 534 * Read EF.ChipSecurity and process security infos 535 */ 536 EAC20.prototype.readChipSecurity = function() { 537 var csbin = this.readEFwithSFI(EAC20.SFI_ChipSecurity); 538 var cstlv = new ASN1(csbin); 539 this.log("EF.ChipSecurity:"); 540 this.log(cstlv); 541 542 var cms = new CMSSignedData(csbin); 543 544 var certs = cms.getSignedDataCertificates(); 545 546 this.log("EF.ChipSecurity Certificates:"); 547 for (var i = 0; i < certs.length; i++) { 548 this.log(certs[i]); 549 } 550 551 this.log("DocSigner Signature is " + (cms.isSignerInfoSignatureValid(0) ? "valid" : "not valid")); 552 553 var data = cms.getSignedContent(); 554 555 this.log(data); 556 557 var cstlv = new ASN1(data); 558 559 this.log(cstlv); 560 561 this.processSecurityInfos(cstlv, true); 562 } 563 564 565 566 /** 567 * Return the list of PACEInfo objects 568 * 569 * @return the list of PACEInfo objects read from the card, indexed by the parameterId 570 * @type PACEInfo[] 571 */ 572 EAC20.prototype.getPACEInfos = function() { 573 return this.PACEInfos; 574 } 575 576 577 578 /** 579 * Return the list of PACEDomainParameterInfo objects 580 * 581 * @return the list of PACEDomainParameterInfo objects read from the card, indexed by the parameterId 582 * @type PACEDomainParameterInfo[] 583 */ 584 EAC20.prototype.getPACEDomainParameterInfos = function() { 585 return this.PACEDPs; 586 } 587 588 589 590 /** 591 * Return the list of ChipAuthenticationInfo objects 592 * 593 * @return the list of ChipAuthenticationInfo objects read from the card, indexed by the keyId 594 * @type ChipAuthenticationInfo[] 595 */ 596 EAC20.prototype.getCAInfos = function() { 597 return this.CAInfos; 598 } 599 600 601 602 /** 603 * Return the list of ChipAuthenticationDomainParameterInfo objects 604 * 605 * @return the list of ChipAuthenticationDomainParameterInfo objects read from the card, indexed by the keyId 606 * @type ChipAuthenticationDomainParameterInfo[] 607 */ 608 EAC20.prototype.getCADomainParameterInfos = function() { 609 return this.CADPs; 610 } 611 612 613 614 /** 615 * Return the key id of the chip authentication key 616 * 617 * @return the key id 618 * @type 619 */ 620 EAC20.prototype.getCAKeyId = function(privileged) { 621 for (var i in this.CAInfos) { // Locate first entry in list 622 if (this.CAInfos[i].keyId) { 623 if (privileged) { 624 privileged = false; // Select second key if privileged key requested 625 } else { 626 return this.CAInfos[i].keyId; 627 } 628 } 629 } 630 return 0; 631 } 632 633 634 635 /** 636 * Return the key id of the restricted identification key 637 * 638 * @param {boolean} authOnly return the RI key available after authentication only (to calculate the pseudonym) 639 * @return the key id 640 * @type 641 */ 642 EAC20.prototype.getRIKeyId = function(authOnly) { 643 for each (var rii in this.RIInfos) { 644 if (!authOnly == !rii.authorizedOnly) { 645 return rii.keyId; 646 } 647 } 648 return 0; 649 } 650 651 652 653 /** 654 * Decode document number from 2 or 3 line MRZ 655 * 656 * <p>This method supports a document number in a three line MRZ longer than 10 digits.</p> 657 * 658 * @param {String} mrz the concatenation of the MRZ lines 659 * @type String 660 * @return the document number 661 */ 662 EAC20.decodeDocumentNumber = function(mrz) { 663 if (mrz.length == 88) { // Two line MRZ 664 var docno = mrz.substr(44, 10); 665 } else { // Three line MRZ 666 if (mrz.charAt(14) == "<") { 667 var sep = mrz.indexOf("<", 15); 668 var docno = mrz.substr(5,9).concat(mrz.substring(15,sep)); 669 } else { 670 var docno = mrz.substr(5, 10); 671 } 672 } 673 return docno; 674 } 675 676 677 /** 678 * Calculate the hash over document number, date of birth and date of expiration from 2 or 3 line MRZ 679 * 680 * <pre> 681 * 2 line MRZ of Silver Data Set 682 * P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<< 683 * L898902C<3UTO6908061F9406236ZE184226B<<<<<14 684 * '-DocNo--' '-DoB-' '-DoE-' 685 * 686 * 3 line MRZ of Silver Data Set 687 * I<UTOL898902C<3<<<<<<<<<<<<<<< 688 * '-DocNo--' 689 * 6908061F9406236UTO<<<<<<<<<<<1 690 * '-DoB-' '-DoE-' 691 * ERIKSON<<ANNA<MARIA<<<<<<<<<<< 692 * </pre> 693 * 694 * @param {String} mrz 2 line or 3 line machine readable zone 695 * @type ByteString 696 * @return the SHA-1 hash over the concatenation of document number, date of birth and date of expiration 697 */ 698 EAC20.prototype.hashMRZ = function(mrz) { 699 var hash_input = new ByteString(EAC20.decodeDocumentNumber(mrz), ASCII); 700 // Convert to byte string 701 var strbin = new ByteString(mrz, ASCII); 702 703 if (strbin.length == 88) { // 2 line MRZ 704 // Extract Date of Birth and Date of Expiration 705 hash_input = hash_input.concat(strbin.bytes(57, 7)); 706 hash_input = hash_input.concat(strbin.bytes(65, 7)); 707 } else if (strbin.length == 90) { // 3 line MRZ 708 // Extract Date of Birth and Date of Expiration 709 hash_input = hash_input.concat(strbin.bytes(30, 7)); 710 hash_input = hash_input.concat(strbin.bytes(38, 7)); 711 } else { 712 throw new GPError("EAC20", GPError.INVALID_DATA, strbin.length, "MRZ must be either 88 or 90 character long"); 713 } 714 715 this.log("Hash Input : " + hash_input.toString(ASCII)); 716 var mrz_hash = this.crypto.digest(Crypto.SHA_1, hash_input); 717 this.log("MRZ Hash : " + mrz_hash); 718 return mrz_hash; 719 } 720 721 722 723 /** 724 * Calculate the Basic Access Control (BAC) key from the MRZ 725 * 726 * @param {String} mrz 2 line or 3 line machine readable zone 727 * @param {Number} keyno Number of key to calculate (1 for Kenc and 2 for Kmac) 728 * @type Key 729 * @returns the key object 730 */ 731 EAC20.prototype.calculateBACKey = function(mrz, keyno) { 732 var mrz_hash = this.hashMRZ(mrz); 733 734 // Extract first 16 byte and append 00000001 or 00000002 735 var bb = new ByteBuffer(mrz_hash.bytes(0, 16)); 736 bb.append(new ByteString("000000", HEX)); 737 bb.append(keyno); 738 739 // Hash again to calculate key value 740 var keyval = this.crypto.digest(Crypto.SHA_1, bb.toByteString()); 741 keyval = keyval.bytes(0, 16); 742 this.log("Value of Key : " + keyval); 743 var key = new Key(); 744 key.setComponent(Key.DES, keyval); 745 746 return key; 747 } 748 749 750 751 /** 752 * Perform BAC using the provided Kenc and Kmac values. 753 * 754 * @param {Key} kenc the key Kenc 755 * @param {Key} kmac the key Kmac 756 */ 757 EAC20.prototype.performBACWithMRZ = function(mrz) { 758 this.setIDPICC(new ByteString(EAC20.decodeDocumentNumber(mrz), ASCII)); 759 760 var kenc = this.calculateBACKey(mrz, 1); 761 var kmac = this.calculateBACKey(mrz, 2); 762 763 this.performBAC(kenc, kmac); 764 } 765 766 767 768 /** 769 * Perform BAC using the provided Kenc and Kmac values. 770 * 771 * @param {Key} kenc the key Kenc 772 * @param {Key} kmac the key Kmac 773 */ 774 EAC20.prototype.performBAC = function(kenc, kmac) { 775 776 // GET CHALLENGE 777 var rndicc = this.card.sendApdu(0x00, 0x84, 0x00, 0x00, 0x08, [0x9000]); 778 779 var rndifd = this.crypto.generateRandom(8); 780 var kifd = this.crypto.generateRandom(16); 781 782 var plain = rndifd.concat(rndicc).concat(kifd); 783 784 var cryptogram = this.crypto.encrypt(kenc, Crypto.DES_CBC, plain, new ByteString("0000000000000000", HEX)); 785 786 var mac = this.crypto.sign(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2)); 787 788 // EXTERNAL AUTHENTICATE 789 var autresp = this.card.sendApdu(0x00, 0x82, 0x00, 0x00, cryptogram.concat(mac), 0); 790 791 if (this.card.SW != 0x9000) { 792 this.log("Mutual authenticate failed with " + this.card.SW.toString(16) + " \"" + this.card.SWMSG + "\". MRZ correct ?"); 793 throw new GPError("EAC20", GPError.CRYPTO_FAILED, 0, "Card did not accept MAC in BAC establishment"); 794 } 795 796 cryptogram = autresp.bytes(0, 32); 797 mac = autresp.bytes(32, 8); 798 799 if (!this.crypto.verify(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2), mac)) { 800 throw new GPError("EAC20", GPError.CRYPTO_FAILED, 0, "Card MAC did not verify correctly"); 801 } 802 803 plain = this.crypto.decrypt(kenc, Crypto.DES_CBC, cryptogram, new ByteString("0000000000000000", HEX)); 804 805 if (!plain.bytes(0, 8).equals(rndicc)) { 806 throw new GPError("EAC20", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.ICC"); 807 } 808 809 if (!plain.bytes(8, 8).equals(rndifd)) { 810 throw new GPError("EAC20", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.IFD"); 811 } 812 813 var kicc = plain.bytes(16, 16); 814 keyinp = kicc.xor(kifd); 815 816 var hashin = keyinp.concat(new ByteString("00000001", HEX)); 817 var kencval = this.crypto.digest(Crypto.SHA_1, hashin); 818 kencval = kencval.bytes(0, 16); 819 var kenc = new Key(); 820 kenc.setComponent(Key.DES, kencval); 821 822 var hashin = keyinp.concat(new ByteString("00000002", HEX)); 823 var kmacval = this.crypto.digest(Crypto.SHA_1, hashin); 824 kmacval = kmacval.bytes(0, 16); 825 var kmac = new Key(); 826 kmac.setComponent(Key.DES, kmacval); 827 828 var ssc = rndicc.bytes(4, 4).concat(rndifd.bytes(4, 4)); 829 830 this.sm = new IsoSecureChannel(this.crypto); 831 this.sm.setEncKey(kenc); 832 this.sm.setMacKey(kmac); 833 this.sm.setSendSequenceCounter(ssc); 834 835 this.card.setCredential(this.sm); 836 } 837 838 839 840 /** 841 * Perform PACE using the indicated parameter set, the identified password, the password value and 842 * an optional cardholder authentication template. 843 * 844 * <p>This method supports PACE version 1 and 2. For version 2, parameterId with a value between 0 and 31 denotes 845 * a standardized domain parameter as defined in TR-03110 2.04 or later.</p> 846 * 847 * @param {Number} parameterId the identifier for the PACEInfo and PACEDomainParameterInfo from EF.CardInfo. Use 0 for 848 * the default. 849 * @param {Number} pwdid one of EAC20.ID_MRZ, EAC20.ID_CAN, EAC20.ID_PIN, EAC20.ID_PUK 850 * @param {ByteString} pwd the PACE password or PACE key 851 * @param {ASN1} chat the CHAT data object with tag 7F4C or null 852 * @param {ASN1} certExt the certificate extensions data object with tag 65 or null 853 */ 854 EAC20.prototype.performPACE = function(parameterId, pwdid, pwd, chat, certExt) { 855 856 var paceinfo = this.PACEInfos[parameterId]; 857 if (typeof(paceinfo) == "undefined") { 858 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown parameterId " + parameterId + " for PACEInfo"); 859 } 860 861 var domainParameter; 862 863 // Used for Chip Authentication 864 this.includeDPinAuthToken = !(paceinfo.version >= 2); 865 866 if ((paceinfo.version == 1) || ((paceinfo.version == 2) && (parameterId > 31))) { 867 var pacedp = this.PACEDPs[parameterId]; 868 if (typeof(pacedp) == "undefined") { 869 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown parameterId " + parameterId + " for PACEDomainParameterInfo"); 870 } 871 domainParameter = pacedp.domainParameter; 872 } else { 873 domainParameter = PACEDomainParameterInfo.getStandardizedDomainParameter(parameterId); 874 } 875 876 if (!(pwd instanceof ByteString) && !(pwd instanceof Key)) { 877 throw new GPError("EAC20", GPError.INVALID_TYPE, 0, "Argument pwd must be of type ByteString or Key"); 878 } 879 880 if ((chat != null) && !(chat instanceof ASN1)) { 881 throw new GPError("EAC20", GPError.INVALID_TYPE, 0, "Argument chat must be of type ASN1"); 882 } 883 884 885 var pace = new PACE(this.crypto, paceinfo.protocol, domainParameter, paceinfo.version); 886 if (pwd instanceof(ByteString)) { 887 pace.setPassword(pwd); 888 } else { 889 pace.setPACEKey(pwd); 890 } 891 892 // Manage SE 893 var crt = new ByteBuffer(); 894 crt.append((new ASN1(0x80, paceinfo.protocol)).getBytes()); 895 crt.append(new ByteString("8301", HEX)); 896 crt.append(pwdid); 897 if (chat != null) { 898 crt.append(chat.getBytes()); 899 } 900 if (certExt != null) { 901 crt.append(certExt.getBytes()); 902 } 903 904 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0xC1, 0xA4, crt.toByteString(), [0x9000, 0x63C2, 0x63C1 ]); 905 906 // General Authenticate 907 var dado = new ASN1(0x7C); 908 909 dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x10, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]); 910 911 var dado = new ASN1(dadobin); 912 assert(dado.tag == 0x7C); 913 assert(dado.elements == 1); 914 var encryptedNonceDO = dado.get(0); 915 assert(encryptedNonceDO.tag == 0x80); 916 var encryptedNonce = encryptedNonceDO.value; 917 918 this.log("Encrypted nonce: " + encryptedNonce); 919 920 pace.decryptNonce(encryptedNonce); 921 922 var mappingData = pace.getMappingData(); 923 924 var dado = new ASN1(0x7C, new ASN1(0x81, mappingData)); 925 926 dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x10, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]); 927 928 var dado = new ASN1(dadobin); 929 assert(dado.tag == 0x7C); 930 assert(dado.elements == 1); 931 var mappingDataDO = dado.get(0); 932 assert(mappingDataDO.tag == 0x82); 933 934 pace.performMapping(mappingDataDO.value); 935 936 var ephemeralPublicKeyIfd = pace.getEphemeralPublicKey(); 937 938 var dado = new ASN1(0x7C, new ASN1(0x83, ephemeralPublicKeyIfd)); 939 940 dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x10, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]); 941 942 var dado = new ASN1(dadobin); 943 assert(dado.tag == 0x7C); 944 assert(dado.elements == 1); 945 var ephemeralPublicKeyICC = dado.get(0); 946 assert(ephemeralPublicKeyICC.tag == 0x84); 947 948 this.idPICC = ephemeralPublicKeyICC.value.bytes(1, (ephemeralPublicKeyICC.value.length - 1) >> 1); 949 this.log("ID_PICC : " + this.idPICC); 950 951 pace.performKeyAgreement(ephemeralPublicKeyICC.value); 952 953 954 var authToken = pace.calculateAuthenticationToken(); 955 956 var dado = new ASN1(0x7C, new ASN1(0x85, authToken)); 957 958 dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000, 0x63C2, 0x63C1, 0x63C0, 0x6283 ]); 959 960 if ((this.card.SW & 0xFFF0) == 0x63C0) { 961 throw new GPError("EAC20", GPError.DEVICE_ERROR, this.card.SW, "Authentication failed: " + (this.card.SW & 0xF) + " tries left"); 962 } 963 964 if (this.card.SW == 0x6300) { 965 throw new GPError("EAC20", GPError.DEVICE_ERROR, this.card.SW, "Authentication failed"); 966 } 967 968 var dado = new ASN1(dadobin); 969 this.log(dado); 970 assert(dado.tag == 0x7C); 971 assert(dado.elements >= 1); 972 assert(dado.elements <= 3); 973 var authTokenDO = dado.get(0); 974 assert(authTokenDO.tag == 0x86); 975 976 if (dado.elements > 1) { 977 var cardo = dado.get(1); 978 assert(cardo.tag == 0x87); 979 this.lastCAR = new PublicKeyReference(cardo.value); 980 } 981 982 if (dado.elements > 2) { 983 var cardo = dado.get(2); 984 assert(cardo.tag == 0x88); 985 this.previousCAR = new PublicKeyReference(cardo.value); 986 } 987 988 var sm = null; 989 990 if (pace.verifyAuthenticationToken(authTokenDO.value)) { 991 this.log("Authentication token valid"); 992 993 var symalgo = pace.getSymmetricAlgorithm(); 994 995 if (symalgo == Key.AES) { 996 sm = new IsoSecureChannel(this.crypto, IsoSecureChannel.SSC_SYNC_ENC_POLICY); 997 sm.setEncKey(pace.kenc); 998 sm.setMacKey(pace.kmac); 999 sm.setMACSendSequenceCounter(new ByteString("00000000000000000000000000000000", HEX)); 1000 } else { 1001 sm = new IsoSecureChannel(this.crypto); 1002 sm.setEncKey(pace.kenc); 1003 sm.setMacKey(pace.kmac); 1004 sm.setMACSendSequenceCounter(new ByteString("0000000000000000", HEX)); 1005 } 1006 this.card.setCredential(sm); 1007 } 1008 this.sm = sm; 1009 } 1010 1011 1012 1013 /** 1014 * Return the trust anchor's CAR as indicated by the card in the PACE protocol 1015 * 1016 * @param {boolean} previous, true to return the previous CAR, if any 1017 * @return the CertificationAuthorityReference (CAR) 1018 * @type PublicKeyReference 1019 */ 1020 EAC20.prototype.getTrustAnchorCAR = function(previous) { 1021 if (previous) { 1022 return this.previousCAR; 1023 } else { 1024 return this.lastCAR; 1025 } 1026 } 1027 1028 1029 1030 /** 1031 * Submit a list of certificates to the card for verification 1032 * 1033 * @param {CVC[]} cvcchain the list of certificates, starting with link certificates, DVCA certificate and terminal certificate. 1034 */ 1035 EAC20.prototype.verifyCertificateChain = function(cvcchain) { 1036 1037 var i = cvcchain.length; 1038 do { 1039 i--; 1040 var cvc = cvcchain[i]; 1041 1042 var car = cvc.getCAR().getBytes(); 1043 1044 var pukrefdo = new ASN1(0x83, car); 1045 var pukref = pukrefdo.getBytes(); 1046 1047 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x81, 0xB6, pukref); 1048 } while ((this.card.SW != 0x9000) && (i > 0)); 1049 1050 for (; i < cvcchain.length; i++) { 1051 var cvc = cvcchain[i]; 1052 1053 var car = cvc.getCAR().getBytes(); 1054 1055 var pukrefdo = new ASN1(0x83, car); 1056 var pukref = pukrefdo.getBytes(); 1057 1058 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x81, 0xB6, pukref, [0x9000]); 1059 1060 // Extract value of 7F21 1061 var tl = new TLVList(cvc.getBytes(), TLV.EMV); 1062 var t = tl.index(0); 1063 var v = t.getValue(); 1064 1065 this.log("Certificate: "); 1066 this.log(new ASN1(v)); 1067 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x2A, 0x00, 0xBE, v, [0x9000]); 1068 } 1069 1070 this.terminalCert = cvcchain[cvcchain.length - 1]; 1071 } 1072 1073 1074 1075 /** 1076 * Set the ID_PICC used for terminal authentication in EAC 1.11 1077 * 1078 * @param {ByteString} id 1079 * @param {Key} kmac the key Kmac 1080 */ 1081 EAC20.prototype.setIDPICC = function(idPICC) { 1082 this.idPICC = idPICC; 1083 } 1084 1085 1086 1087 /** 1088 * Perform terminal authentication using a given terminal key 1089 * 1090 * @param {Key} termkey the terminal private key 1091 * @param {ByteString} auxdata auxiliary data (tag '67') to be included in terminal authentication 1092 * @param {Crypto} crypto optional alternative crypto provider (e.g. for key in SmartCard-HSM) 1093 */ 1094 EAC20.prototype.performTerminalAuthentication = function(termkey, auxdata, crypto) { 1095 var signatureInput = this.performTerminalAuthenticationSetup(auxdata); 1096 1097 if (crypto == undefined) { 1098 var crypto = this.crypto; 1099 } 1100 var signature = crypto.sign(termkey, CVC.getSignatureMech(this.terminalCert.getPublicKeyOID()), signatureInput); 1101 1102 var keysize = termkey.getSize(); 1103 if (keysize < 0) { 1104 keysize = termkey.getComponent(Key.ECC_P).length; 1105 } else { 1106 keysize >>= 3; 1107 } 1108 1109 signature = CVC.unwrapSignature(signature, keysize); 1110 this.log("Signature (Encoded):"); 1111 this.log(signature); 1112 1113 this.performTerminalAuthenticationFinal(signature); 1114 } 1115 1116 1117 1118 /** 1119 * Prepare terminal authentication by setting the required security environment 1120 * 1121 * @param {ByteString} auxdata auxiliary data (tag '67') to be included in terminal authentication 1122 */ 1123 EAC20.prototype.performTerminalAuthenticationSetup = function(auxdata) { 1124 1125 var idIFD = this.ca.getCompressedPublicKey(); 1126 1127 var bb = new ByteBuffer(); 1128 1129 if (typeof(this.cakeyId) != "undefined") { 1130 bb.append(new ASN1(0x80, this.terminalCert.getPublicKeyOID()).getBytes()); 1131 } 1132 1133 bb.append(new ASN1(0x83, this.terminalCert.getCHR().getBytes()).getBytes()); 1134 1135 if (typeof(this.cakeyId) != "undefined") { 1136 if (auxdata) { 1137 bb.append(auxdata); 1138 } 1139 bb.append(new ASN1(0x91, idIFD).getBytes()); 1140 } 1141 1142 var msedata = bb.toByteString(); 1143 this.log("Manage SE data:"); 1144 this.log(msedata); 1145 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x81, 0xA4, msedata, [0x9000]); 1146 1147 var challenge = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x84, 0x00, 0x00, 8, [0x9000]); 1148 1149 var bb = new ByteBuffer(); 1150 bb.append(this.idPICC); 1151 bb.append(challenge); 1152 bb.append(idIFD); 1153 if (auxdata) { 1154 bb.append(auxdata); 1155 } 1156 var signatureInput = bb.toByteString(); 1157 this.log("Signature Input:"); 1158 this.log(signatureInput); 1159 return signatureInput; 1160 } 1161 1162 1163 1164 /** 1165 * Complete terminal authentication by submitting the signature to the card 1166 * 1167 * @param {ByteString} signature the signature as concatenation of r and s 1168 */ 1169 EAC20.prototype.performTerminalAuthenticationFinal = function(signature) { 1170 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x82, 0x00, 0x00, signature, [0x9000]); 1171 } 1172 1173 1174 1175 /** 1176 * Perform chip authentication in version 1 and establish a secure channel 1177 * 1178 * @return true, if chip authentication was successfull 1179 * @type boolean 1180 */ 1181 EAC20.prototype.performChipAuthenticationV1 = function(keyid) { 1182 this.log("performChipAuthenticationV1() called"); 1183 1184 if (typeof(keyid) == "undefined") { 1185 keyid = 0; 1186 } 1187 1188 var cainfo = this.CAInfos[keyid]; 1189 if (typeof(cainfo) == "undefined") { 1190 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyid + " for ChipAuthenticationInfo"); 1191 } 1192 1193 var capuk = this.CAPublicKeys[keyid]; 1194 if (typeof(capuk) == "undefined") { 1195 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyid + " for ChipAuthenticationPublicKeyInfo"); 1196 } 1197 1198 var domainParameter = capuk.domainParameter; 1199 1200 this.ca = new ChipAuthentication(this.crypto, cainfo.protocol, domainParameter); 1201 1202 this.ca.generateEphemeralCAKeyPair(); 1203 1204 if (cainfo.protocol.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) { 1205 var bb = new ByteBuffer(); 1206 bb.append(new ASN1(0x91, this.ca.getEphemeralPublicKey()).getBytes()); 1207 1208 if (typeof(cainfo.keyId) != "undefined") { 1209 bb.append(new ByteString("8401", HEX)); 1210 bb.append(cainfo.keyId); 1211 } 1212 1213 var msedata = bb.toByteString(); 1214 this.log("Manage SE data:"); 1215 this.log(msedata); 1216 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA6, msedata, [0x9000]); 1217 1218 this.ca.performKeyAgreement(capuk.publicKey); 1219 1220 this.log("Create DES based secure channel"); 1221 var sm = new IsoSecureChannel(this.crypto); 1222 sm.setEncKey(this.ca.kenc); 1223 sm.setMacKey(this.ca.kmac); 1224 sm.setSendSequenceCounter(new ByteString("0000000000000000", HEX)); 1225 } else { 1226 var bb = new ByteBuffer(); 1227 bb.append(new ASN1(0x80, cainfo.protocol).getBytes()); 1228 1229 if (typeof(cainfo.keyId) != "undefined") { 1230 bb.append(new ByteString("8401", HEX)); 1231 bb.append(cainfo.keyId); 1232 } 1233 1234 var msedata = bb.toByteString(); 1235 this.log("Manage SE data:"); 1236 this.log(msedata); 1237 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]); 1238 1239 var ephemeralPublicKeyIfd = this.ca.getEphemeralPublicKey(); 1240 1241 var dado = new ASN1(0x7C, new ASN1(0x80, ephemeralPublicKeyIfd)); 1242 1243 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]); 1244 1245 this.ca.performKeyAgreement(capuk.publicKey); 1246 1247 this.log("Create AES based secure channel"); 1248 var sm = new IsoSecureChannel(this.crypto, IsoSecureChannel.SSC_SYNC_ENC_POLICY); 1249 sm.setEncKey(this.ca.kenc); 1250 sm.setMacKey(this.ca.kmac); 1251 sm.setMACSendSequenceCounter(new ByteString("00000000000000000000000000000000", HEX)); 1252 } 1253 1254 this.card.setCredential(sm); 1255 this.sm = sm; 1256 } 1257 1258 1259 1260 /** 1261 * Prepare chip authentication by generating the ephemeral key pair 1262 * 1263 * @param {Number} keyId the key identifier to be used for chip authentication 1264 */ 1265 EAC20.prototype.prepareChipAuthentication = function(keyId) { 1266 this.log("prepareChipAuthentication() called"); 1267 1268 this.cakeyId = keyId; 1269 1270 var cainfo = this.CAInfos[keyId]; 1271 if (typeof(cainfo) == "undefined") { 1272 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyId + " for ChipAuthenticationInfo"); 1273 } 1274 1275 var cadp = this.CADPs[keyId]; 1276 if (typeof(cadp) == "undefined") { 1277 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyId + " for ChipAuthenticationDomainParameterInfo"); 1278 } 1279 this.cadp = cadp; 1280 1281 this.ca = new ChipAuthentication(this.crypto, cainfo.protocol, cadp.domainParameter); 1282 1283 this.ca.includeDPinAuthToken = this.includeDPinAuthToken; 1284 this.ca.generateEphemeralCAKeyPair(); 1285 } 1286 1287 1288 1289 /** 1290 * Perform chip authentication in version 2 and establish a secure channel 1291 * 1292 * @return true, if chip authentication was successfull 1293 * @type boolean 1294 */ 1295 EAC20.prototype.performChipAuthenticationV2 = function() { 1296 this.log("performChipAuthenticationV2() called"); 1297 1298 var cainfo = this.CAInfos[this.cakeyId]; 1299 if (typeof(cainfo) == "undefined") { 1300 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + this.cakeyId + " for ChipAuthenticationInfo"); 1301 } 1302 1303 if (this.ca.algo != cainfo.protocol) { 1304 this.log("Special handling for ChipAuthenticationInfo in EF.CardSecurity overwriting ChipAuthenticationInfo in EF.CardAccess"); 1305 this.log("Protocol in EF.CardAccess: " + this.ca.algo); 1306 this.log("Protocol is EF.CardSecurity: " + cainfo.protocol); 1307 this.ca.algo = cainfo.protocol; 1308 } 1309 1310 var capuk = this.CAPublicKeys[this.cakeyId]; 1311 if (typeof(capuk) == "undefined") { 1312 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + this.cakeyId + " for ChipAuthenticationPublicKeyInfo"); 1313 } 1314 1315 var bb = new ByteBuffer(); 1316 bb.append(new ASN1(0x80, cainfo.protocol).getBytes()); 1317 1318 if (typeof(cainfo.keyId) != "undefined") { 1319 bb.append(new ByteString("8401", HEX)); 1320 bb.append(cainfo.keyId); 1321 } 1322 1323 var msedata = bb.toByteString(); 1324 this.log("Manage SE data:"); 1325 this.log(msedata); 1326 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]); 1327 1328 var ephemeralPublicKeyIfd = this.ca.getEphemeralPublicKey(); 1329 1330 var dado = new ASN1(0x7C, new ASN1(0x80, ephemeralPublicKeyIfd)); 1331 1332 var dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]); 1333 1334 this.log(dadobin); 1335 1336 var dado = new ASN1(dadobin); 1337 assert(dado.tag == 0x7C); 1338 assert(dado.elements == 2); 1339 var nonceDO = dado.get(0); 1340 assert(nonceDO.tag == 0x81); 1341 var nonce = nonceDO.value; 1342 1343 var authTokenDO = dado.get(1); 1344 assert(authTokenDO.tag == 0x82); 1345 var authToken = authTokenDO.value; 1346 1347 this.ca.performKeyAgreement(capuk.publicKey, nonce); 1348 1349 var result = this.ca.verifyAuthenticationToken(authToken); 1350 1351 if (result) { 1352 this.log("Authentication token valid"); 1353 1354 if (this.ca.algo.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) { 1355 this.log("Create DES based secure channel"); 1356 var sm = new IsoSecureChannel(this.crypto); 1357 sm.setEncKey(this.ca.kenc); 1358 sm.setMacKey(this.ca.kmac); 1359 sm.setMACSendSequenceCounter(new ByteString("0000000000000000", HEX)); 1360 } else { 1361 this.log("Create AES based secure channel"); 1362 var sm = new IsoSecureChannel(this.crypto, IsoSecureChannel.SSC_SYNC_ENC_POLICY); 1363 sm.setEncKey(this.ca.kenc); 1364 sm.setMacKey(this.ca.kmac); 1365 sm.setMACSendSequenceCounter(new ByteString("00000000000000000000000000000000", HEX)); 1366 } 1367 this.card.setCredential(sm); 1368 this.sm = sm; 1369 } else { 1370 this.log("Authentication token invalid"); 1371 } 1372 1373 return result; 1374 } 1375 1376 1377 1378 /** 1379 * Verify authenticated auxiliary data 1380 * 1381 * @param {ByteString} oid the object identifier for the auxiliary data provided during terminal authentication 1382 * @return true, if auxiliary data was verified 1383 * @type boolean 1384 */ 1385 EAC20.prototype.verifyAuxiliaryData = function(oid) { 1386 var o = new ASN1(ASN1.OBJECT_IDENTIFIER, oid); 1387 this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x20, 0x80, 0x00, o.getBytes(), [0x9000,0x6300]); 1388 return this.card.SW == 0x9000; 1389 } 1390 1391 1392 1393 /** 1394 * Perform chip authentication and establish a secure channel 1395 * 1396 * @param {Number} keyid the key identifier (only required for ChipAuthentication in version 1) 1397 * @return true, if chip authentication was successfull 1398 * @type boolean 1399 */ 1400 EAC20.prototype.performChipAuthentication = function(keyid) { 1401 if (typeof(this.cakeyId) != "undefined") { 1402 return this.performChipAuthenticationV2(); 1403 } else { 1404 return this.performChipAuthenticationV1(keyid); 1405 } 1406 } 1407 1408 1409 1410 /** 1411 * Perform restricted identification 1412 * 1413 * @param {Number} keyId restricted identification key identifier 1414 * @param {ByteString} sectorPublicKey the sector public key data 1415 * @param {Number} sectorPublicKeyIndex optional argument that allows to select a specific sector public key in the terminal certificate 1416 * @return the sector specific identifier 1417 * @type ByteString 1418 */ 1419 EAC20.prototype.performRestrictedIdentification = function(keyId, sectorPublicKey, sectorPublicKeyIndex) { 1420 var bb = new ByteBuffer(); 1421 bb.append(new ASN1(0x80, new ByteString("id-RI-ECDH-SHA-256", OID)).getBytes()); 1422 1423 bb.append(new ByteString("8401", HEX)); 1424 bb.append(keyId); 1425 1426 var msedata = bb.toByteString(); 1427 this.log("Manage SE data:"); 1428 this.log(msedata); 1429 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]); 1430 1431 var tag = 0xA0; 1432 if (sectorPublicKeyIndex) { 1433 tag = 0xA0 + (sectorPublicKeyIndex << 1); 1434 } 1435 1436 // ToDo change to sectorPublicKey.value 1437 var dado = new ASN1(0x7C, new ASN1(tag, sectorPublicKey.bytes(5))); 1438 1439 this.log("GA Input: " + dado.getBytes()); 1440 1441 var dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]); 1442 1443 this.log(dadobin); 1444 1445 var dado = new ASN1(dadobin); 1446 assert(dado.tag == 0x7C); 1447 assert(dado.elements == 1); 1448 var nonceDO = dado.get(0); 1449 assert((nonceDO.tag == 0x81) || (nonceDO.tag == 0x83)); 1450 var sectorId = nonceDO.value; 1451 1452 this.log("Sector specific identifier: " + sectorId); 1453 return sectorId; 1454 } 1455