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