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 // If there is a second TA in the established CA channel, then skip sending the 1140 // ephemeral terminal key again. 1141 if (!this.tacomplete) { 1142 bb.append(new ASN1(0x91, idIFD).getBytes()); 1143 } 1144 } 1145 1146 var msedata = bb.toByteString(); 1147 this.log("Manage SE data:"); 1148 this.log(msedata); 1149 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x81, 0xA4, msedata, [0x9000]); 1150 1151 var challenge = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x84, 0x00, 0x00, 8, [0x9000]); 1152 1153 var bb = new ByteBuffer(); 1154 bb.append(this.idPICC); 1155 bb.append(challenge); 1156 bb.append(idIFD); 1157 if (auxdata) { 1158 bb.append(auxdata); 1159 } 1160 var signatureInput = bb.toByteString(); 1161 this.log("Signature Input:"); 1162 this.log(signatureInput); 1163 return signatureInput; 1164 } 1165 1166 1167 1168 /** 1169 * Complete terminal authentication by submitting the signature to the card 1170 * 1171 * @param {ByteString} signature the signature as concatenation of r and s 1172 */ 1173 EAC20.prototype.performTerminalAuthenticationFinal = function(signature) { 1174 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x82, 0x00, 0x00, signature, [0x9000]); 1175 this.tacomplete = true; 1176 } 1177 1178 1179 1180 /** 1181 * Perform chip authentication in version 1 and establish a secure channel 1182 * 1183 * @return true, if chip authentication was successfull 1184 * @type boolean 1185 */ 1186 EAC20.prototype.performChipAuthenticationV1 = function(keyid) { 1187 this.log("performChipAuthenticationV1() called"); 1188 1189 if (typeof(keyid) == "undefined") { 1190 keyid = 0; 1191 } 1192 1193 var cainfo = this.CAInfos[keyid]; 1194 if (typeof(cainfo) == "undefined") { 1195 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyid + " for ChipAuthenticationInfo"); 1196 } 1197 1198 var capuk = this.CAPublicKeys[keyid]; 1199 if (typeof(capuk) == "undefined") { 1200 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyid + " for ChipAuthenticationPublicKeyInfo"); 1201 } 1202 1203 var domainParameter = capuk.domainParameter; 1204 1205 this.ca = new ChipAuthentication(this.crypto, cainfo.protocol, domainParameter); 1206 1207 this.ca.generateEphemeralCAKeyPair(); 1208 1209 if (cainfo.protocol.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) { 1210 var bb = new ByteBuffer(); 1211 bb.append(new ASN1(0x91, this.ca.getEphemeralPublicKey()).getBytes()); 1212 1213 if (typeof(cainfo.keyId) != "undefined") { 1214 bb.append(new ByteString("8401", HEX)); 1215 bb.append(cainfo.keyId); 1216 } 1217 1218 var msedata = bb.toByteString(); 1219 this.log("Manage SE data:"); 1220 this.log(msedata); 1221 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA6, msedata, [0x9000]); 1222 1223 this.ca.performKeyAgreement(capuk.publicKey); 1224 1225 this.log("Create DES based secure channel"); 1226 var sm = new IsoSecureChannel(this.crypto); 1227 sm.setEncKey(this.ca.kenc); 1228 sm.setMacKey(this.ca.kmac); 1229 sm.setSendSequenceCounter(new ByteString("0000000000000000", HEX)); 1230 } else { 1231 var bb = new ByteBuffer(); 1232 bb.append(new ASN1(0x80, cainfo.protocol).getBytes()); 1233 1234 if (typeof(cainfo.keyId) != "undefined") { 1235 bb.append(new ByteString("8401", HEX)); 1236 bb.append(cainfo.keyId); 1237 } 1238 1239 var msedata = bb.toByteString(); 1240 this.log("Manage SE data:"); 1241 this.log(msedata); 1242 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]); 1243 1244 var ephemeralPublicKeyIfd = this.ca.getEphemeralPublicKey(); 1245 1246 var dado = new ASN1(0x7C, new ASN1(0x80, ephemeralPublicKeyIfd)); 1247 1248 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]); 1249 1250 this.ca.performKeyAgreement(capuk.publicKey); 1251 1252 this.log("Create AES based secure channel"); 1253 var sm = new IsoSecureChannel(this.crypto, IsoSecureChannel.SSC_SYNC_ENC_POLICY); 1254 sm.setEncKey(this.ca.kenc); 1255 sm.setMacKey(this.ca.kmac); 1256 sm.setMACSendSequenceCounter(new ByteString("00000000000000000000000000000000", HEX)); 1257 } 1258 1259 this.card.setCredential(sm); 1260 this.sm = sm; 1261 } 1262 1263 1264 1265 /** 1266 * Prepare chip authentication by generating the ephemeral key pair 1267 * 1268 * @param {Number} keyId the key identifier to be used for chip authentication 1269 */ 1270 EAC20.prototype.prepareChipAuthentication = function(keyId) { 1271 this.log("prepareChipAuthentication() called"); 1272 1273 this.cakeyId = keyId; 1274 1275 var cainfo = this.CAInfos[keyId]; 1276 if (typeof(cainfo) == "undefined") { 1277 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyId + " for ChipAuthenticationInfo"); 1278 } 1279 1280 var cadp = this.CADPs[keyId]; 1281 if (typeof(cadp) == "undefined") { 1282 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyId + " for ChipAuthenticationDomainParameterInfo"); 1283 } 1284 this.cadp = cadp; 1285 1286 this.ca = new ChipAuthentication(this.crypto, cainfo.protocol, cadp.domainParameter); 1287 1288 this.ca.includeDPinAuthToken = this.includeDPinAuthToken; 1289 this.ca.generateEphemeralCAKeyPair(); 1290 } 1291 1292 1293 1294 /** 1295 * Perform chip authentication in version 2 and establish a secure channel 1296 * 1297 * @return true, if chip authentication was successfull 1298 * @type boolean 1299 */ 1300 EAC20.prototype.performChipAuthenticationV2 = function() { 1301 this.log("performChipAuthenticationV2() called"); 1302 1303 var cainfo = this.CAInfos[this.cakeyId]; 1304 if (typeof(cainfo) == "undefined") { 1305 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + this.cakeyId + " for ChipAuthenticationInfo"); 1306 } 1307 1308 if (this.ca.algo != cainfo.protocol) { 1309 this.log("Special handling for ChipAuthenticationInfo in EF.CardSecurity overwriting ChipAuthenticationInfo in EF.CardAccess"); 1310 this.log("Protocol in EF.CardAccess: " + this.ca.algo); 1311 this.log("Protocol is EF.CardSecurity: " + cainfo.protocol); 1312 this.ca.algo = cainfo.protocol; 1313 } 1314 1315 var capuk = this.CAPublicKeys[this.cakeyId]; 1316 if (typeof(capuk) == "undefined") { 1317 throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + this.cakeyId + " for ChipAuthenticationPublicKeyInfo"); 1318 } 1319 1320 var bb = new ByteBuffer(); 1321 bb.append(new ASN1(0x80, cainfo.protocol).getBytes()); 1322 1323 if (typeof(cainfo.keyId) != "undefined") { 1324 bb.append(new ByteString("8401", HEX)); 1325 bb.append(cainfo.keyId); 1326 } 1327 1328 var msedata = bb.toByteString(); 1329 this.log("Manage SE data:"); 1330 this.log(msedata); 1331 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]); 1332 1333 var ephemeralPublicKeyIfd = this.ca.getEphemeralPublicKey(); 1334 1335 var dado = new ASN1(0x7C, new ASN1(0x80, ephemeralPublicKeyIfd)); 1336 1337 var dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]); 1338 1339 this.log(dadobin); 1340 1341 var dado = new ASN1(dadobin); 1342 assert(dado.tag == 0x7C); 1343 assert(dado.elements == 2); 1344 var nonceDO = dado.get(0); 1345 assert(nonceDO.tag == 0x81); 1346 var nonce = nonceDO.value; 1347 1348 var authTokenDO = dado.get(1); 1349 assert(authTokenDO.tag == 0x82); 1350 var authToken = authTokenDO.value; 1351 1352 this.ca.performKeyAgreement(capuk.publicKey, nonce); 1353 1354 var result = this.ca.verifyAuthenticationToken(authToken); 1355 1356 if (result) { 1357 this.log("Authentication token valid"); 1358 1359 if (this.ca.algo.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) { 1360 this.log("Create DES based secure channel"); 1361 var sm = new IsoSecureChannel(this.crypto); 1362 sm.setEncKey(this.ca.kenc); 1363 sm.setMacKey(this.ca.kmac); 1364 sm.setMACSendSequenceCounter(new ByteString("0000000000000000", HEX)); 1365 } else { 1366 this.log("Create AES based secure channel"); 1367 var sm = new IsoSecureChannel(this.crypto, IsoSecureChannel.SSC_SYNC_ENC_POLICY); 1368 sm.setEncKey(this.ca.kenc); 1369 sm.setMacKey(this.ca.kmac); 1370 sm.setMACSendSequenceCounter(new ByteString("00000000000000000000000000000000", HEX)); 1371 } 1372 this.card.setCredential(sm); 1373 this.sm = sm; 1374 } else { 1375 this.log("Authentication token invalid"); 1376 } 1377 1378 return result; 1379 } 1380 1381 1382 1383 /** 1384 * Verify authenticated auxiliary data 1385 * 1386 * @param {ByteString} oid the object identifier for the auxiliary data provided during terminal authentication 1387 * @return true, if auxiliary data was verified 1388 * @type boolean 1389 */ 1390 EAC20.prototype.verifyAuxiliaryData = function(oid) { 1391 var o = new ASN1(ASN1.OBJECT_IDENTIFIER, oid); 1392 this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x20, 0x80, 0x00, o.getBytes(), [0x9000,0x6300]); 1393 return this.card.SW == 0x9000; 1394 } 1395 1396 1397 1398 /** 1399 * Perform chip authentication and establish a secure channel 1400 * 1401 * @param {Number} keyid the key identifier (only required for ChipAuthentication in version 1) 1402 * @return true, if chip authentication was successfull 1403 * @type boolean 1404 */ 1405 EAC20.prototype.performChipAuthentication = function(keyid) { 1406 if (typeof(this.cakeyId) != "undefined") { 1407 return this.performChipAuthenticationV2(); 1408 } else { 1409 return this.performChipAuthenticationV1(keyid); 1410 } 1411 } 1412 1413 1414 1415 /** 1416 * Perform restricted identification 1417 * 1418 * @param {Number} keyId restricted identification key identifier 1419 * @param {ByteString} sectorPublicKey the sector public key data 1420 * @param {Number} sectorPublicKeyIndex optional argument that allows to select a specific sector public key in the terminal certificate 1421 * @return the sector specific identifier 1422 * @type ByteString 1423 */ 1424 EAC20.prototype.performRestrictedIdentification = function(keyId, sectorPublicKey, sectorPublicKeyIndex) { 1425 var bb = new ByteBuffer(); 1426 bb.append(new ASN1(0x80, new ByteString("id-RI-ECDH-SHA-256", OID)).getBytes()); 1427 1428 bb.append(new ByteString("8401", HEX)); 1429 bb.append(keyId); 1430 1431 var msedata = bb.toByteString(); 1432 this.log("Manage SE data:"); 1433 this.log(msedata); 1434 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]); 1435 1436 var tag = 0xA0; 1437 if (sectorPublicKeyIndex) { 1438 tag = 0xA0 + (sectorPublicKeyIndex << 1); 1439 } 1440 1441 // ToDo change to sectorPublicKey.value 1442 var dado = new ASN1(0x7C, new ASN1(tag, sectorPublicKey.bytes(5))); 1443 1444 this.log("GA Input: " + dado.getBytes()); 1445 1446 var dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]); 1447 1448 this.log(dadobin); 1449 1450 var dado = new ASN1(dadobin); 1451 assert(dado.tag == 0x7C); 1452 assert(dado.elements == 1); 1453 var nonceDO = dado.get(0); 1454 assert((nonceDO.tag == 0x81) || (nonceDO.tag == 0x83)); 1455 var sectorId = nonceDO.value; 1456 1457 this.log("Sector specific identifier: " + sectorId); 1458 return sectorId; 1459 } 1460