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