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 chip authentication protocol for both card and terminal 25 * as defined for EAC 2.0 26 */ 27 28 29 30 /** 31 * Create a ChipAuthenticationInfo object 32 * 33 * @class <p>This class encodes and decodes ChipAuthenticationInfo objects.</p> 34 * <p>The class implements the following ASN.1 syntax:</p> 35 * <pre> 36 * ChipAuthenticationInfo ::= SEQUENCE { 37 * protocol OBJECT IDENTIFIER( 38 * id-CA-DH-3DES-CBC-CBC | 39 * id-CA-DH-AES-CBC-CMAC-128 | 40 * id-CA-DH-AES-CBC-CMAC-192 | 41 * id-CA-DH-AES-CBC-CMAC-256 | 42 * id-CA-ECDH-3DES-CBC-CBC | 43 * id-CA-ECDH-AES-CBC-CMAC-128 | 44 * id-CA-ECDH-AES-CBC-CMAC-192 | 45 * id-CA-ECDH-AES-CBC-CMAC-256), 46 * version INTEGER, -- MUST be 1 or 2 47 * keyId INTEGER OPTIONAL 48 * } 49 * </pre> 50 * @constructor 51 * @param {ASN1} the optional tlv structure to initialize the object 52 */ 53 function ChipAuthenticationInfo(tlv) { 54 if (tlv && (tlv instanceof ASN1)) { 55 assert(tlv.isconstructed); 56 assert(tlv.elements >= 2); 57 58 var i = 0; 59 var t = tlv.get(i++); 60 assert(t.tag == ASN1.OBJECT_IDENTIFIER); 61 this.protocol = t.value; 62 63 var t = tlv.get(i++); 64 assert(t.tag == ASN1.INTEGER); 65 this.version = t.value.toSigned(); 66 67 if (i < tlv.elements) { 68 var t = tlv.get(i++); 69 assert(t.tag == ASN1.INTEGER); 70 this.keyId = t.value.toSigned(); 71 } 72 73 } 74 } 75 76 exports.ChipAuthenticationInfo = ChipAuthenticationInfo; 77 78 79 /** 80 * Convert object to TLV structure 81 * 82 * @return the TLV structure 83 * @type ASN1 84 */ 85 ChipAuthenticationInfo.prototype.toTLV = function() { 86 var t = new ASN1(ASN1.SEQUENCE); 87 88 t.add(new ASN1(ASN1.OBJECT_IDENTIFIER, this.protocol)); 89 90 var bb = new ByteBuffer(); 91 bb.append(this.version); 92 t.add(new ASN1(ASN1.INTEGER, bb.toByteString())); 93 94 if (typeof(this.keyId) != "undefined") { 95 t.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(this.keyId))); 96 } 97 return t; 98 } 99 100 101 102 ChipAuthenticationInfo.prototype.toString = function() { 103 return "ChipAuthenticationInfo(protocol=" + this.protocol + ", version=" + this.version + ", keyId=" + this.keyId + ")"; 104 } 105 106 107 108 /** 109 * Create a ChipAuthenticationDomainParameterInfo object 110 * 111 * @class <p>This class encodes and decodes ChipAuthenticationDomainParameterInfo objects.</p> 112 * <p>The class implements the following ASN.1 syntax:</p> 113 * <pre> 114 * ChipAuthenticationDomainParameterInfo ::= SEQUENCE { 115 * protocol OBJECT IDENTIFIER(id-CA-DH | id-CA-ECDH), 116 * domainParameter AlgorithmIdentifier, 117 * keyId INTEGER OPTIONAL 118 * } 119 * </pre> 120 * @constructor 121 * @param {ASN1} the optional tlv structure to initialize the object 122 */ 123 function ChipAuthenticationDomainParameterInfo(tlv) { 124 if (tlv && (tlv instanceof ASN1)) { 125 assert(tlv.isconstructed); 126 assert(tlv.elements >= 2); 127 128 var i = 0; 129 var t = tlv.get(i++); 130 assert(t.tag == ASN1.OBJECT_IDENTIFIER); 131 this.protocol = t.value; 132 133 var t = tlv.get(i++); 134 assert(t.tag == ASN1.SEQUENCE); 135 136 if (t.elements > 0) { 137 var oid = t.get(0); 138 assert(oid.tag == ASN1.OBJECT_IDENTIFIER); 139 if (oid.value.equals(new ByteString("standardizedDomainParameter", OID))) { 140 this.standardizedDomainParameter = t.get(1).value.toUnsigned(); 141 var curveoid = ChipAuthentication.standardizedDomainParameter[this.standardizedDomainParameter]; 142 if (!curveoid) { 143 throw new GPError("ChipAuthenticationPublicKeyInfo", GPError.INVALID_DATA, 0, "Standardized domain parameter " + this.standardizedDomainParameter + " is unknown"); 144 } 145 this.domainParameter = new Key(); 146 this.domainParameter.setComponent(Key.ECC_CURVE_OID, new ByteString(curveoid, OID)); 147 } else { 148 this.domainParameter = ECCUtils.decodeECParameters(t.get(1)); 149 } 150 } else { 151 this.domainParameter = new Key(); 152 this.domainParameter.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID)); 153 } 154 155 if (i < tlv.elements) { 156 var t = tlv.get(i++); 157 assert(t.tag == ASN1.INTEGER); 158 this.keyId = t.value.toSigned(); 159 } 160 } 161 } 162 163 if (typeof(exports) != "undefined") 164 exports.ChipAuthenticationDomainParameterInfo = ChipAuthenticationDomainParameterInfo; 165 166 167 /** 168 * Convert object to TLV structure 169 * 170 * @return the TLV structure 171 * @type ASN1 172 */ 173 ChipAuthenticationDomainParameterInfo.prototype.toTLV = function() { 174 var t = new ASN1(ASN1.SEQUENCE); 175 176 t.add(new ASN1(ASN1.OBJECT_IDENTIFIER, this.protocol)); 177 178 var c = new ASN1(ASN1.SEQUENCE); 179 if (this.standardizedDomainParameter) { 180 c.add(new ASN1(ASN1.OBJECT_IDENTIFIER, new ByteString("standardizedDomainParameter", OID))); 181 c.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(this.standardizedDomainParameter))); 182 } else { 183 184 } 185 t.add(c); 186 187 if (typeof(this.keyId) != "undefined") { 188 t.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(this.keyId))); 189 } 190 return t; 191 } 192 193 194 195 ChipAuthenticationDomainParameterInfo.prototype.toString = function() { 196 return "ChipAuthenticationDomainParameterInfo(protocol=" + this.protocol + ", keyId=" + this.keyId + ")"; 197 } 198 199 200 201 /** 202 * Create a ChipAuthenticationPublicKeyInfo object 203 * 204 * @class <p>This class encodes and decodes ChipAuthenticationPublicKeyInfo objects.</p> 205 * <p>The class implements the following ASN.1 syntax:</p> 206 * <pre> 207 * ChipAuthenticationPublicKeyInfo ::= SEQUENCE { 208 * protocol OBJECT IDENTIFIER(id-PK-DH | id-PK-ECDH), 209 * chipAuthenticationPublicKey SubjectPublicKeyInfo, 210 * keyId INTEGER OPTIONAL 211 * } 212 * 213 * SubjectPublicKeyInfo ::= SEQUENCE { 214 * algorithm AlgorithmIdentifier, 215 * subjectPublicKey BIT STRING 216 * } 217 * 218 * AlgorithmIdentifier ::= SEQUENCE { 219 * algorithm OBJECT IDENTIFIER, 220 * parameters ANY DEFINED BY algorithm OPTIONAL 221 * } 222 * </pre> 223 * @constructor 224 * @param {ASN1} the optional tlv structure to initialize the object 225 */ 226 function ChipAuthenticationPublicKeyInfo(tlv) { 227 if (tlv && (tlv instanceof ASN1)) { 228 assert(tlv.isconstructed); 229 assert(tlv.elements >= 2); 230 231 var i = 0; 232 var t = tlv.get(i++); 233 assert(t.tag == ASN1.OBJECT_IDENTIFIER); // protocol 234 this.protocol = t.value; 235 236 var t = tlv.get(i++); 237 assert(t.tag == ASN1.SEQUENCE); // Subject public key info 238 assert(t.elements == 2); 239 240 var algo = t.get(0); 241 assert(algo.tag == ASN1.SEQUENCE); // Algorithm Identifier 242 assert(algo.elements == 2); 243 244 var oid = algo.get(0); // algorithm 245 assert(oid.tag == ASN1.OBJECT_IDENTIFIER); 246 this.algorithm = oid.value; 247 248 if (oid.value.equals(new ByteString("standardizedDomainParameter", OID))) { 249 this.standardizedDomainParameter = algo.get(1).value.toUnsigned(); 250 var curveoid = ChipAuthentication.standardizedDomainParameter[this.standardizedDomainParameter]; 251 if (!curveoid) { 252 throw new GPError("ChipAuthenticationPublicKeyInfo", GPError.INVALID_DATA, 0, "Standardized domain parameter " + this.standardizedDomainParameter + " is unknown"); 253 } 254 this.domainParameter = new Key(); 255 this.domainParameter.setComponent(Key.ECC_CURVE_OID, new ByteString(curveoid, OID)); 256 } else { 257 this.domainParameter = ECCUtils.decodeECParameters(algo.get(1)); 258 } 259 260 var puk = t.get(1); 261 assert(puk.tag == ASN1.BIT_STRING); 262 this.publicKey = puk.value.bytes(1); 263 264 if (i < tlv.elements) { 265 var t = tlv.get(i++); 266 assert(t.tag == ASN1.INTEGER); 267 this.keyId = t.value.toSigned(); 268 } 269 } 270 } 271 272 if (typeof(exports) != "undefined") 273 exports.ChipAuthenticationPublicKeyInfo = ChipAuthenticationPublicKeyInfo; 274 275 276 277 /** 278 * Removes leading zeros and prepends a single '00' to ByteStrings which have the most significant bit set. 279 * 280 * This prevent interpretation of the integer representation if converted into 281 * a signed ASN1 INTEGER. 282 * 283 * @param {ByteString} value the value to convert 284 * @return the converted value 285 * @type ByteString 286 */ 287 ChipAuthenticationPublicKeyInfo.convertUnsignedInteger = function(value) { 288 assert(value.length > 0); 289 290 var i = 0; 291 for (var i = 0; (i < value.length - 1) && (value.byteAt(i) == 0); i++); 292 293 if (value.byteAt(i) >= 0x80) { 294 value = (new ByteString("00", HEX)).concat(value.bytes(i)); 295 } else { 296 value = value.bytes(i); 297 } 298 299 return value; 300 } 301 302 303 304 /** 305 * Creates the EC Public Key as subjectPublicKeyInfo TLV structure object. 306 * 307 * <p>The structure is defined as:</p> 308 * <pre> 309 * SubjectPublicKeyInfo ::= SEQUENCE { 310 * algorithm AlgorithmIdentifier, 311 * subjectPublicKey BIT STRING } 312 * 313 * AlgorithmIdentifier ::= SEQUENCE { 314 * algorithm OBJECT IDENTIFIER, 315 * parameters ANY DEFINED BY algorithm OPTIONAL } 316 * 317 * id-ecPublicKey OBJECT IDENTIFIER ::= { 318 * iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } 319 * 320 * ECParameters ::= CHOICE { 321 * namedCurve OBJECT IDENTIFIER, 322 * implicitCurve NULL, 323 * specifiedCurve SpecifiedECDomain } 324 * </pre> 325 * @return the subjectPublicKey TLV structure 326 * @type ASN1 327 */ 328 ChipAuthenticationPublicKeyInfo.createECSubjectPublicKeyInfo = function(publicKey, encodeECDomainParameter) { 329 var t = new ASN1("subjectPublicKeyInfo", ASN1.SEQUENCE); 330 331 var algorithm = new ASN1("algorithm", ASN1.SEQUENCE, 332 new ASN1("algorithm", ASN1.OBJECT_IDENTIFIER, new ByteString("1.2.840.10045.2.1", OID)) 333 ); 334 335 if (encodeECDomainParameter) { 336 if (publicKey.getComponent(Key.ECC_P)) { // Make sure curve components are available if only curve oid is defined 337 publicKey.setComponent(Key.ECC_CURVE_OID, publicKey.getComponent(Key.ECC_CURVE_OID)); 338 } 339 groupCAPuk.setComponent(Key.ECC_CURVE_OID, groupCAPuk.getComponent(Key.ECC_CURVE_OID)); 340 var ecParameter = 341 new ASN1("ecParameters", ASN1.SEQUENCE, 342 new ASN1("version", ASN1.INTEGER, new ByteString("01", HEX)), 343 new ASN1("fieldID", ASN1.SEQUENCE, 344 new ASN1("fieldType", ASN1.OBJECT_IDENTIFIER, new ByteString("prime-field", OID)), 345 new ASN1("prime", ASN1.INTEGER, 346 ChipAuthenticationPublicKeyInfo.convertUnsignedInteger(publicKey.getComponent(Key.ECC_P))) 347 ), 348 new ASN1("curve", ASN1.SEQUENCE, 349 new ASN1("a", ASN1.OCTET_STRING, 350 ChipAuthenticationPublicKeyInfo.convertUnsignedInteger(publicKey.getComponent(Key.ECC_A))), 351 new ASN1("b", ASN1.OCTET_STRING, 352 ChipAuthenticationPublicKeyInfo.convertUnsignedInteger(publicKey.getComponent(Key.ECC_B))) 353 ), 354 new ASN1("base", ASN1.OCTET_STRING, 355 (new ByteString("04", HEX)).concat(publicKey.getComponent(Key.ECC_GX)).concat(publicKey.getComponent(Key.ECC_GY))), 356 new ASN1("order", ASN1.INTEGER, 357 ChipAuthenticationPublicKeyInfo.convertUnsignedInteger(publicKey.getComponent(Key.ECC_N))) 358 ); 359 360 var cofactor = publicKey.getComponent(Key.ECC_H); 361 var i = 0; 362 for (; (i < cofactor.length) && (cofactor.byteAt(i) == 0); i++); 363 if (i < cofactor.length) { 364 ecParameter.add(new ASN1("cofactor", ASN1.INTEGER, cofactor.bytes(i))); 365 } 366 algorithm.add(ecParameter); 367 } else { 368 algorithm.add(new ASN1("parameters", ASN1.OBJECT_IDENTIFIER, publicKey.getComponent(Key.ECC_CURVE_OID))); 369 } 370 371 t.add(algorithm); 372 373 // Prefix a 00 to form correct bitstring 374 // Prefix a 04 to indicate uncompressed format 375 var keybin = new ByteString("0004", HEX); 376 keybin = keybin.concat(publicKey.getComponent(Key.ECC_QX)); 377 keybin = keybin.concat(publicKey.getComponent(Key.ECC_QY)); 378 t.add(new ASN1("subjectPublicKey", ASN1.BIT_STRING, keybin)); 379 380 return t; 381 } 382 383 384 385 /** 386 * Convert object to TLV structure 387 * 388 * @return the TLV structure 389 * @type ASN1 390 */ 391 ChipAuthenticationPublicKeyInfo.prototype.toTLV = function() { 392 var t = new ASN1("chipAuthenticationPublicKeyInfo", ASN1.SEQUENCE); 393 394 t.add(new ASN1("protocol", ASN1.OBJECT_IDENTIFIER, this.protocol)); 395 396 if (this.algorithm.equals(new ByteString("id-ecPublicKey", OID))) { 397 var spki = ChipAuthenticationPublicKeyInfo.createECSubjectPublicKeyInfo(this.publicKey, true); 398 } else { 399 var algoid = new ASN1("algorithm", ASN1.SEQUENCE, new ASN1("algorithm", ASN1.OBJECT_IDENTIFIER, this.algorithm)); 400 if (this.algorithm.equals(new ByteString("standardizedDomainParameter", OID))) { 401 algoid.add(new ASN1("standardizedDomainParameter", ASN1.INTEGER, ByteString.valueOf(this.standardizedDomainParameter))); 402 } 403 404 var spki = new ASN1("subjectPublicKey", ASN1.SEQUENCE); 405 spki.add(algoid); 406 var puk = (new ByteString("0004", HEX)).concat(this.publicKey.getComponent(Key.ECC_QX)).concat(this.publicKey.getComponent(Key.ECC_QY)); 407 spki.add(new ASN1("publicKey", ASN1.BIT_STRING, puk)); 408 409 } 410 411 t.add(spki); 412 413 if (typeof(this.keyId) != "undefined") { 414 t.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(this.keyId))); 415 } 416 return t; 417 } 418 419 420 421 ChipAuthenticationPublicKeyInfo.prototype.toString = function() { 422 return "ChipAuthenticationPublicKeyInfo(protocol=" + this.protocol + ", algorithm=" + this.algorithm + ", publicKey=" + this.publicKey + ", keyId=" + this.keyId + ")"; 423 } 424 425 426 427 /** 428 * Create a ChipAuthentication protocol object 429 * 430 * @class This class implements the ChipAuthentication protocol 431 * 432 * @constructor 433 * 434 * @param {Crypto} crypto the crypto provider 435 * @param {ByteString} algo the algorithm OID 436 * @param {Key} domparam the key object holding ECC domain parameter 437 */ 438 function ChipAuthentication(crypto, algo, domparam) { 439 this.crypto = crypto; 440 this.algo = algo; 441 this.domparam = domparam; 442 this.includeDPinAuthToken = false; 443 this.noPadding = false; 444 445 // print(ECCUtils.ECParametersToString(domparam)); 446 } 447 448 if (typeof(exports) != "undefined") 449 exports.ChipAuthentication = ChipAuthentication; 450 451 452 ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC = (new ByteString("id-CA-ECDH-3DES-CBC-CBC", OID)); 453 ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_128 = (new ByteString("id-CA-ECDH-AES-CBC-CMAC-128", OID)); 454 ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_192 = (new ByteString("id-CA-ECDH-AES-CBC-CMAC-192", OID)); 455 ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_256 = (new ByteString("id-CA-ECDH-AES-CBC-CMAC-256", OID)); 456 457 ChipAuthentication.standardizedDomainParameter = []; 458 ChipAuthentication.standardizedDomainParameter[8] = "secp192r1"; 459 ChipAuthentication.standardizedDomainParameter[9] = "brainpoolP192r1"; 460 ChipAuthentication.standardizedDomainParameter[10] = "secp224r1"; 461 ChipAuthentication.standardizedDomainParameter[11] = "brainpoolP224r1"; 462 ChipAuthentication.standardizedDomainParameter[12] = "secp256r1"; 463 ChipAuthentication.standardizedDomainParameter[13] = "brainpoolP256r1"; 464 ChipAuthentication.standardizedDomainParameter[14] = "brainpoolP320r1"; 465 ChipAuthentication.standardizedDomainParameter[15] = "secp384r1"; 466 ChipAuthentication.standardizedDomainParameter[16] = "brainpoolP384r1"; 467 ChipAuthentication.standardizedDomainParameter[17] = "brainpoolP512r1"; 468 ChipAuthentication.standardizedDomainParameter[18] = "secp521r1"; 469 470 471 /** 472 * Derive key from input parameter, counter and optional nonce 473 * 474 * @param {ByteString} input the first part of the hash input 475 * @param {Number} counter the counter value 476 * @param {nonce} the optional nonce inserted between the input and the counter 477 * @return the key object 478 * @type Key 479 */ 480 ChipAuthentication.prototype.deriveKey = function(input, counter, nonce) { 481 if (typeof(nonce) != "undefined") { 482 input = input.concat(nonce); 483 } 484 485 var bb = new ByteBuffer("000000", HEX); 486 bb.append(counter); 487 488 input = input.concat(bb.toByteString()); 489 490 var key = new Key(); 491 492 if (this.algo.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) { 493 var digest = this.crypto.digest(Crypto.SHA_1, input); 494 key.setComponent(Key.DES, digest.left(16)); 495 } else if (this.algo.equals(ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_128)) { 496 var digest = this.crypto.digest(Crypto.SHA_1, input); 497 key.setComponent(Key.AES, digest.left(16)); 498 } else if (this.algo.equals(ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_192)) { 499 var digest = this.crypto.digest(Crypto.SHA_256, input); 500 key.setComponent(Key.AES, digest.left(24)); 501 } else if (this.algo.equals(ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_256)) { 502 var digest = this.crypto.digest(Crypto.SHA_256, input); 503 key.setComponent(Key.AES, digest); 504 } else { 505 throw new GPError("ChipAuthentication", GPError.INVALID_MECH, 0, "Algorithm not supported"); 506 } 507 508 return key; 509 } 510 511 512 513 /** 514 * Generate ephemeral key pair 515 */ 516 ChipAuthentication.prototype.generateEphemeralCAKeyPair = function() { 517 this.prkCA = new Key(this.domparam); 518 this.prkCA.setType(Key.PRIVATE); 519 520 this.pukCA = new Key(this.domparam); 521 this.pukCA.setType(Key.PUBLIC); 522 523 this.crypto.generateKeyPair(Crypto.EC, this.pukCA, this.prkCA); 524 } 525 526 527 528 /** 529 * Set chip authentication keys 530 * 531 * @param {Key} prk the private key 532 * @param {Key} puk the public key 533 */ 534 ChipAuthentication.prototype.setKeyPair = function(prk, puk) { 535 this.prkCA = prk; 536 this.pukCA = puk; 537 } 538 539 540 541 /** 542 * Returns the x coordinate of the public key 543 * 544 * @return the encoded public key 545 * @type ByteString 546 */ 547 ChipAuthentication.prototype.getCompressedPublicKey = function() { 548 return (this.pukCA.getComponent(Key.ECC_QX)); 549 } 550 551 552 553 /** 554 * Returns the ephemeral public key 555 * 556 * @return the encoded public key 557 * @type ByteString 558 */ 559 ChipAuthentication.prototype.getEphemeralPublicKey = function() { 560 var ecpk = new ByteString("04", HEX); 561 ecpk = ecpk.concat(this.pukCA.getComponent(Key.ECC_QX)); 562 ecpk = ecpk.concat(this.pukCA.getComponent(Key.ECC_QY)); 563 return ecpk; 564 } 565 566 567 568 /** 569 * Decodes the ephemeral public key 570 * 571 * @return the decoded public key 572 * @type ByteString 573 */ 574 ChipAuthentication.prototype.decodeEphemeralPublicKey = function(encodedKey) { 575 if (encodedKey.byteAt(0) != 0x04) { 576 throw new GPError("ChipAuthentication", GPError.INVALID_DATA, 0, "Terminal ephemeral public key does not start with '04'"); 577 } 578 579 var key = new Key(this.domparam); 580 var l = key.getSize() >> 3; 581 582 if (encodedKey.length != 1 + (l << 1)) { 583 throw new GPError("ChipAuthentication", GPError.INVALID_DATA, 0, "Length of terminal ephemeral public key does not match curve"); 584 } 585 586 key.setType(Key.PUBLIC); 587 key.setComponent(Key.ECC_QX, encodedKey.bytes(1, l)); 588 key.setComponent(Key.ECC_QY, encodedKey.bytes(1 + l, l)); 589 return key; 590 } 591 592 593 594 /** 595 * Performs the mapping operation with mapping data from the other side 596 * 597 * @param {ByteString} publicKey the public key in encoded format 598 */ 599 ChipAuthentication.prototype.performKeyAgreement = function(publicKey, nonce) { 600 if (publicKey.byteAt(0) != 0x04) 601 throw new GPError("ChipAuthentication", GPError.INVALID_DATA, 0, "Public key must start with '04'"); 602 603 if ((nonce != undefined) && !(nonce instanceof ByteString)) 604 throw new GPError("ChipAuthentication", GPError.INVALID_TYPE, 0, "nonce must be of type ByteString"); 605 606 var l = (publicKey.length - 1) >> 1; 607 if (l != this.prkCA.getComponent(Key.ECC_P).length) { 608 throw new GPError("ChipAuthentication", GPError.INVALID_DATA, 0, "Public key size does not match private key size"); 609 } 610 611 this.otherPuK = new Key(this.domparam); 612 this.otherPuK.setComponent(Key.ECC_QX, publicKey.bytes( 1, l)); 613 this.otherPuK.setComponent(Key.ECC_QY, publicKey.bytes(l + 1, l)); 614 615 var k = this.crypto.decrypt(this.prkCA, Crypto.ECDH, publicKey.bytes(1)); 616 // GPSystem.trace("Shared Secret K:"); 617 // GPSystem.trace(k); 618 this.kenc = this.deriveKey(k, 1, nonce); 619 this.kmac = this.deriveKey(k, 2, nonce); 620 } 621 622 623 624 /** 625 * Calculate and verify the authentication token over the public key received from 626 * the other side 627 * 628 * @param {ByteString} the MAC over the authentication data 629 * @return true if the MAC is valid 630 * @type Boolean 631 */ 632 ChipAuthentication.prototype.verifyAuthenticationToken = function(authToken) { 633 var t = ChipAuthentication.encodePublicKey(this.algo.toString(OID), this.pukCA, this.includeDPinAuthToken); 634 // GPSystem.trace("Authentication Token for verification:"); 635 // GPSystem.trace(t); 636 637 if (this.algo.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) { 638 if (this.noPadding) { 639 var inp = t.getBytes(); 640 } else { 641 var inp = t.getBytes().pad(Crypto.ISO9797_METHOD_2); 642 } 643 var at = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, inp); 644 return at.equals(authToken); 645 } else { 646 var at = this.crypto.sign(this.kmac, Crypto.AES_CMAC, t.getBytes()); 647 return at.left(8).equals(authToken); 648 } 649 } 650 651 652 653 /** 654 * Calculate the authentication token over the public key received from 655 * the other side 656 * 657 * @param {ByteString} the MAC over the authentication data 658 * @return true if the MAC is valid 659 * @type Boolean 660 */ 661 ChipAuthentication.prototype.calculateAuthenticationToken = function() { 662 var t = ChipAuthentication.encodePublicKey(this.algo.toString(OID), this.otherPuK, this.includeDPinAuthToken); 663 // GPSystem.trace("Authentication Token for signing:"); 664 // GPSystem.trace(t); 665 666 if (this.algo.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) { 667 if (this.noPadding) { 668 var inp = t.getBytes(); 669 } else { 670 var inp = t.getBytes().pad(Crypto.ISO9797_METHOD_2); 671 } 672 var at = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, inp); 673 } else { 674 var at = this.crypto.sign(this.kmac, Crypto.AES_CMAC, t.getBytes()).left(8); 675 } 676 return at; 677 } 678 679 680 681 /** 682 * Strips leading zeros of a ByteString 683 * 684 * @param {ByteString} value the ByteString value 685 * @return the stripped ByteString object, may be an empty ByteString 686 * @type ByteString 687 */ 688 ChipAuthentication.stripLeadingZeros = function(value) { 689 var i = 0; 690 for (; (i < value.length) && (value.byteAt(i) == 0); i++); 691 692 return value.right(value.length - i); 693 } 694 695 696 697 /** 698 * Encode an ECC public key in the format defined by the EAC 2.0 specification 699 * 700 * @param {String} oid the object identifier to encode 701 * @param {Key} key the EC public key 702 * @param {Boolean} withDP true to encode domain parameter as well 703 */ 704 ChipAuthentication.encodePublicKey = function(oid, key, withDP) { 705 706 var t = new ASN1("ecPublicKey", 0x7F49); 707 t.add(new ASN1("objectIdentifier", ASN1.OBJECT_IDENTIFIER, new ByteString(oid, OID))); 708 if (withDP) { 709 t.add(new ASN1("primeModulus", 0x81, key.getComponent(Key.ECC_P))); 710 t.add(new ASN1("firstCoefficient", 0x82, key.getComponent(Key.ECC_A))); 711 t.add(new ASN1("secondCoefficient", 0x83, key.getComponent(Key.ECC_B))); 712 713 var point = new ByteString("04", HEX); 714 point = point.concat(key.getComponent(Key.ECC_GX)); 715 point = point.concat(key.getComponent(Key.ECC_GY)); 716 t.add(new ASN1("basePoint", 0x84, point)); 717 718 t.add(new ASN1("orderOfTheBasePoint", 0x85, key.getComponent(Key.ECC_N))); 719 } 720 var point = new ByteString("04", HEX); 721 point = point.concat(key.getComponent(Key.ECC_QX)); 722 point = point.concat(key.getComponent(Key.ECC_QY)); 723 t.add(new ASN1("publicPoint", 0x86, point)); 724 725 if (withDP) { 726 var cofactor = key.getComponent(Key.ECC_H); 727 cofactor = ChipAuthentication.stripLeadingZeros(cofactor); 728 729 t.add(new ASN1("cofactor", 0x87, cofactor)); 730 } 731 732 return t; 733 } 734