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 PACE/SAC protocol for both card and terminal 25 */ 26 27 28 29 /** 30 * Create a PACEInfo object 31 * 32 * @class <p>This class encodes and decodes PACEInfo objects.</p> 33 * <p>The class implements the following ASN.1 syntax:</p> 34 * <pre> 35 * PACEInfo ::= SEQUENCE { 36 * protocol OBJECT IDENTIFIER, 37 * version INTEGER, -- MUST be 1 or 2 38 * parameterId INTEGER OPTIONAL 39 * } 40 * </pre> 41 * @constructor 42 * @param {ASN1} the optional tlv structure to initialize the object 43 */ 44 function PACEInfo(tlv) { 45 if (tlv && (tlv instanceof ASN1)) { 46 assert(tlv.isconstructed); 47 assert(tlv.elements >= 2); 48 49 var i = 0; 50 var t = tlv.get(i++); 51 assert(t.tag == ASN1.OBJECT_IDENTIFIER); 52 this.protocol = t.value; 53 54 var t = tlv.get(i++); 55 assert(t.tag == ASN1.INTEGER); 56 this.version = t.value.toSigned(); 57 58 if (i < tlv.elements) { 59 var t = tlv.get(i++); 60 assert(t.tag == ASN1.INTEGER); 61 this.parameterId = t.value.toSigned(); 62 } 63 } 64 } 65 66 exports.PACEInfo = PACEInfo; 67 68 69 70 /** 71 * Convert object to TLV structure 72 * 73 * @return the TLV structure 74 * @type ASN1 75 */ 76 PACEInfo.prototype.toTLV = function() { 77 var t = new ASN1(ASN1.SEQUENCE); 78 79 t.add(new ASN1(ASN1.OBJECT_IDENTIFIER, this.protocol)); 80 81 var bb = new ByteBuffer(); 82 bb.append(this.version); 83 t.add(new ASN1(ASN1.INTEGER, bb.toByteString())); 84 85 if (typeof(this.parameterId) != "undefined") { 86 var bb = new ByteBuffer(); 87 bb.append(this.parameterId); 88 t.add(new ASN1(ASN1.INTEGER, bb.toByteString())); 89 } 90 return t; 91 } 92 93 94 95 PACEInfo.prototype.toString = function() { 96 return "PACEInfo(protocol=" + this.protocol + ", version=" + this.version + ", parameterId=" + this.parameterId + ")"; 97 } 98 99 100 101 /** 102 * Create a PACEDomainParameterInfo object 103 * 104 * @class <p>This class encodes and decodes PACEDomainParameterInfo objects.</p> 105 * <p>The class implements the following ASN.1 syntax:</p> 106 * <pre> 107 * PACEDomainParameterInfo ::= SEQUENCE { 108 * protocol OBJECT IDENTIFIER(id-PACE-DH | id-PACE-ECDH), 109 * domainParameter AlgorithmIdentifier, 110 * parameterId INTEGER OPTIONAL 111 * } 112 * </pre> 113 * @constructor 114 * @param {ASN1} the optional tlv structure to initialize the object 115 */ 116 function PACEDomainParameterInfo(tlv) { 117 if (tlv && (tlv instanceof ASN1)) { 118 assert(tlv.isconstructed); 119 assert(tlv.elements >= 2); 120 121 var i = 0; 122 var t = tlv.get(i++); 123 assert(t.tag == ASN1.OBJECT_IDENTIFIER); 124 this.protocol = t.value; 125 126 var t = tlv.get(i++); 127 assert(t.tag == ASN1.SEQUENCE); 128 129 if (t.elements > 0) { 130 this.domainParameter = ECCUtils.decodeECParameters(t.get(1)); 131 } else { 132 this.domainParameter = new Key(); 133 this.domainParameter.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID)); 134 } 135 136 if (i < tlv.elements) { 137 var t = tlv.get(i++); 138 assert(t.tag == ASN1.INTEGER); 139 this.parameterId = t.value.toSigned(); 140 } 141 142 } 143 } 144 145 exports.PACEDomainParameterInfo = PACEDomainParameterInfo; 146 147 148 149 /** 150 * Convert object to TLV structure 151 * 152 * @return the TLV structure 153 * @type ASN1 154 */ 155 PACEDomainParameterInfo.prototype.toTLV = function() { 156 var t = new ASN1(ASN1.SEQUENCE); 157 158 t.add(new ASN1(ASN1.OBJECT_IDENTIFIER, this.protocol)); 159 160 var c = new ASN1(ASN1.SEQUENCE); 161 if (this.standardizedDomainParameter) { 162 c.add(new ASN1(ASN1.OBJECT_IDENTIFIER, new ByteString("standardizedDomainParameter", OID))); 163 c.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(this.standardizedDomainParameter))); 164 } else { 165 166 } 167 t.add(c); 168 169 if (typeof(this.parameterId) != "undefined") { 170 var bb = new ByteBuffer(); 171 bb.append(this.parameterId); 172 t.add(new ASN1(ASN1.INTEGER, bb.toByteString())); 173 } 174 return t; 175 } 176 177 178 179 PACEDomainParameterInfo.getStandardizedDomainParameter = function(id) { 180 var key = new Key(); 181 key.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID)); 182 return key; 183 } 184 185 186 187 PACEDomainParameterInfo.prototype.toString = function() { 188 return "PACEDomainParameterInfo(protocol=" + this.protocol + ", parameterId=" + this.parameterId + ")"; 189 } 190 191 192 193 /** 194 * Create a PACE protocol object 195 * 196 * @class This class implements the PACE protocol 197 * 198 * @constructor 199 * 200 * @param {Crypto} crypto the crypto provider 201 * @param {ByteString} algo the algorithm OID 202 * @param {Key} domainparam the key object holding ECC domain parameter 203 * @param {Number} version protocol version (1 or 2) 204 */ 205 function PACE(crypto, algo, domparam, version) { 206 this.crypto = crypto; 207 this.algo = algo.toString(OID); 208 this.domparam = domparam; 209 210 if (typeof(version) != "undefined") { 211 this.version = version; 212 } else { 213 this.version = 1; 214 } 215 216 if (this.algo == PACE.id_PACE_ECDH_GM_3DES_CBC_CBC) { 217 this.symalgo = Key.DES; 218 } else { 219 this.symalgo = Key.AES; 220 } 221 222 this.sym = Crypto.AES; 223 } 224 225 exports.PACE = PACE; 226 227 228 229 /** 230 * Return algorithm type 231 * 232 * @type Number 233 * @returns Either Key.DES or Key.AES 234 */ 235 PACE.prototype.getSymmetricAlgorithm = function() { 236 return this.symalgo; 237 } 238 239 240 241 /** 242 * Derive key from input parameter, counter and optional nonce 243 * 244 * @param {ByteString} input the first part of the hash input 245 * @param {Number} counter the counter value 246 * @param {nonce} the optional nonce inserted between the input and the counter 247 * @return the key object 248 * @type Key 249 */ 250 PACE.prototype.deriveKey = function(input, counter, nonce) { 251 if (typeof(nonce) != "undefined") { 252 input = input.concat(nonce); 253 } 254 255 var bb = new ByteBuffer("000000", HEX); 256 bb.append(counter); 257 258 input = input.concat(bb.toByteString()); 259 260 var key = new Key(); 261 262 if (this.algo == PACE.id_PACE_ECDH_GM_3DES_CBC_CBC) { 263 var digest = this.crypto.digest(Crypto.SHA_1, input); 264 key.setComponent(Key.DES, digest.left(16)); 265 } else if (this.algo == PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_128) { 266 var digest = this.crypto.digest(Crypto.SHA_1, input); 267 key.setComponent(Key.AES, digest.left(16)); 268 } else if (this.algo == PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_192) { 269 var digest = this.crypto.digest(Crypto.SHA_256, input); 270 key.setComponent(Key.AES, digest.left(24)); 271 } else if (this.algo == PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_256) { 272 var digest = this.crypto.digest(Crypto.SHA_256, input); 273 key.setComponent(Key.AES, digest); 274 } else { 275 throw new GPError("pace", GPError.INVALID_MECH, 0x6A80, "Algorithm not supported"); 276 } 277 return key; 278 } 279 280 281 282 /** 283 * Set the password and derive the PACE key. 284 * @param {ByteString} pwd the PACE password (Hash Value for MRZ and ASCII string for others) 285 * @return the PACE key. 286 */ 287 PACE.prototype.setPassword = function(pwd) { 288 this.pacekey = this.deriveKey(pwd, 3); 289 } 290 291 292 293 /** 294 * Set the PACE key. 295 * @param {ByteString} key the PACE key 296 * @return the PACE key. 297 */ 298 PACE.prototype.setPACEKey = function(key) { 299 this.pacekey = key; 300 } 301 302 303 304 /** 305 * Generate nonce and encrypt using PACE key. 306 * @return the encrypted nonce 307 * @type ByteString 308 */ 309 PACE.prototype.getEncryptedNonce = function() { 310 this.nonce = this.crypto.generateRandom(16); 311 if (this.symalgo == Key.DES) { 312 var encnonce = this.crypto.encrypt(this.pacekey, Crypto.DES_CBC, this.nonce); 313 } else { 314 var encnonce = this.crypto.encrypt(this.pacekey, Crypto.AES_ECB, this.nonce); 315 } 316 return encnonce; 317 } 318 319 320 321 /** 322 * Decrypt and store nonce using PACE key. 323 * 324 * @param {ByteString} nonce the encrypted nonce 325 */ 326 PACE.prototype.decryptNonce = function(encnonce) { 327 if (this.symalgo == Key.DES) { 328 this.nonce = this.crypto.decrypt(this.pacekey, Crypto.DES_CBC, encnonce); 329 } else { 330 this.nonce = this.crypto.decrypt(this.pacekey, Crypto.AES_ECB, encnonce); 331 } 332 } 333 334 335 336 /** 337 * Returns true, if the nonce is known. 338 * @return true if the nonce is known 339 * @type Boolean 340 */ 341 PACE.prototype.hasNonce = function() { 342 return (typeof(this.nonce) != "undefined"); 343 } 344 345 346 347 /** 348 * Generate ephemeral ECC key pair. 349 * 350 * @param domainParameter the domain parameter for the key pair 351 * @return the ephemeral public key 352 * @type Key 353 */ 354 PACE.prototype.generateEphemeralKeyPair = function(domainParameter) { 355 this.prk = new Key(domainParameter); 356 this.prk.setType(Key.PRIVATE); 357 358 this.puk = new Key(domainParameter); 359 this.puk.setType(Key.PUBLIC); 360 361 this.crypto.generateKeyPair(Crypto.EC, this.puk, this.prk); 362 363 return this.puk; 364 } 365 366 367 368 /** 369 * Generates and returns the mapping data for this instance 370 * @return the mapping data 371 * @type ByteString 372 */ 373 PACE.prototype.getMappingData = function() { 374 if (typeof(this.prk) == "undefined") { 375 this.generateEphemeralKeyPair(this.domparam); 376 } 377 378 var ecpk = new ByteString("04", HEX); 379 ecpk = ecpk.concat(this.puk.getComponent(Key.ECC_QX)); 380 ecpk = ecpk.concat(this.puk.getComponent(Key.ECC_QY)); 381 return ecpk; 382 } 383 384 385 386 /** 387 * Performs the mapping operation with mapping data from the other side 388 * 389 */ 390 PACE.prototype.performMapping = function(mappingData) { 391 if (mappingData.byteAt(0) != 0x04) 392 throw new GPError("PACE", GPError.INVALID_DATA, 0x6A80, "Public key must start with '04'"); 393 394 if (typeof(this.nonce) == "undefined") 395 throw new GPError("PACE", GPError.INVALID_MECH, 0x6985, "Nonce is not yet defined"); 396 397 var l = (mappingData.length - 1) >> 1; 398 if (l != this.prk.getComponent(Key.ECC_P).length) { 399 throw new GPError("PACE", GPError.INVALID_DATA, 0, "Public key size does not match private key size"); 400 } 401 402 var h = this.crypto.decrypt(this.prk, Crypto.ECDHP, mappingData.bytes(1)); 403 404 var l = h.length >> 1; 405 var H = new Key(this.domparam); 406 H.setComponent(Key.ECC_QX, h.bytes(0, l)); 407 H.setComponent(Key.ECC_QY, h.bytes(l, l)); 408 409 var G = new Key(this.domparam); 410 // Copy generator point into public key point 411 G.setComponent(Key.ECC_QX, G.getComponent(Key.ECC_GX)); 412 G.setComponent(Key.ECC_QY, G.getComponent(Key.ECC_GY)); 413 414 // Calculate G' = s * G + P, where P is initially stored in H and 415 // G' is finally stored in H. 416 this.crypto.deriveKey(G, Crypto.EC_MULTIPLY_ADD, this.nonce, H); 417 418 // Create new domain parameter with G' 419 this.ephDomParam = new Key(this.domparam); 420 this.ephDomParam.setComponent(Key.ECC_GX, H.getComponent(Key.ECC_QX)); 421 this.ephDomParam.setComponent(Key.ECC_GY, H.getComponent(Key.ECC_QY)); 422 } 423 424 425 426 /** 427 * Returns the ephemeral public key based on the new domain parameter 428 * 429 * @return the encoded public key 430 * @type ByteString 431 */ 432 PACE.prototype.getEphemeralPublicKey = function() { 433 this.generateEphemeralKeyPair(this.ephDomParam); 434 var ecpk = new ByteString("04", HEX); 435 ecpk = ecpk.concat(this.puk.getComponent(Key.ECC_QX)); 436 ecpk = ecpk.concat(this.puk.getComponent(Key.ECC_QY)); 437 return ecpk; 438 } 439 440 441 442 /** 443 * Performs the mapping operation with mapping data from the other side 444 * 445 * @param {ByteString} publicKey the public key in encoded format 446 */ 447 PACE.prototype.performKeyAgreement = function(publicKey) { 448 if (publicKey.byteAt(0) != 0x04) 449 throw new GPError("PACE", GPError.INVALID_DATA, 0x6A80, "Public key must start with '04'"); 450 451 if (typeof(this.nonce) == "undefined") 452 throw new GPError("PACE", GPError.INVALID_MECH, 0x6985, "Nonce is not yet defined"); 453 454 var l = (publicKey.length - 1) >> 1; 455 if (l != this.prk.getComponent(Key.ECC_P).length) { 456 throw new GPError("PACE", GPError.INVALID_DATA, 0, "Public key size does not match private key size"); 457 } 458 459 this.otherPuK = new Key(this.ephDomParam); 460 this.otherPuK.setComponent(Key.ECC_QX, publicKey.bytes( 1, l)); 461 this.otherPuK.setComponent(Key.ECC_QY, publicKey.bytes(l + 1, l)); 462 463 var k = this.crypto.decrypt(this.prk, Crypto.ECDH, publicKey.bytes(1)); 464 GPSystem.trace("Shared Secret K:"); 465 GPSystem.trace(k); 466 this.kenc = this.deriveKey(k, 1); 467 this.kmac = this.deriveKey(k, 2); 468 469 } 470 471 472 473 /** 474 * Strips leading zeros of a ByteString 475 * 476 * @param {ByteString} value the ByteString value 477 * @return the stripped ByteString object, may be an empty ByteString 478 * @type ByteString 479 */ 480 PACE.stripLeadingZeros = function(value) { 481 var i = 0; 482 for (; (i < value.length) && (value.byteAt(i) == 0); i++); 483 484 return value.right(value.length - i); 485 } 486 487 488 489 /** 490 * Encode an ECC public key in the format defined by the EAC 2.0 specification 491 * 492 * @param {String} oid the object identifier to encode 493 * @param {Key} key the EC public key 494 * @param {Boolean} withDP true to encode domain parameter as well 495 * @type ASN1 496 * @returns the ASN1 encoded public key object 497 */ 498 PACE.encodePublicKey = function(oid, key, withDP) { 499 500 var t = new ASN1("ecPublicKey", 0x7F49); 501 t.add(new ASN1("objectIdentifier", ASN1.OBJECT_IDENTIFIER, new ByteString(oid, OID))); 502 if (withDP) { 503 t.add(new ASN1("primeModulus", 0x81, key.getComponent(Key.ECC_P))); 504 t.add(new ASN1("firstCoefficient", 0x82, key.getComponent(Key.ECC_A))); 505 t.add(new ASN1("secondCoefficient", 0x83, key.getComponent(Key.ECC_B))); 506 507 var point = new ByteString("04", HEX); 508 point = point.concat(key.getComponent(Key.ECC_GX)); 509 point = point.concat(key.getComponent(Key.ECC_GY)); 510 t.add(new ASN1("basePoint", 0x84, point)); 511 512 t.add(new ASN1("orderOfTheBasePoint", 0x85, key.getComponent(Key.ECC_N))); 513 } 514 var point = new ByteString("04", HEX); 515 point = point.concat(key.getComponent(Key.ECC_QX)); 516 point = point.concat(key.getComponent(Key.ECC_QY)); 517 t.add(new ASN1("publicPoint", 0x86, point)); 518 519 if (withDP) { 520 var cofactor = key.getComponent(Key.ECC_H); 521 cofactor = PACE.stripLeadingZeros(cofactor); 522 523 t.add(new ASN1("cofactor", 0x87, cofactor)); 524 } 525 526 return t; 527 } 528 529 530 531 /** 532 * Calculate the authentication token over the public key received from 533 * the other side 534 * 535 * @return the MAC over the authentication data 536 * @type ByteString 537 */ 538 PACE.prototype.calculateAuthenticationToken = function() { 539 var t = PACE.encodePublicKey(this.algo, this.otherPuK, (this.version == 1)); 540 GPSystem.trace("Authentication Token:"); 541 GPSystem.trace(t); 542 543 if (this.symalgo == Key.DES) { 544 var inp = t.getBytes().pad(Crypto.ISO9797_METHOD_2); 545 var at = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, inp); 546 } else { 547 var at = this.crypto.sign(this.kmac, Crypto.AES_CMAC, t.getBytes()).left(8); 548 } 549 550 return at; 551 } 552 553 554 555 /** 556 * Calculate and verify the authentication token over the public key received from 557 * the other side 558 * 559 * @param {ByteString} the MAC over the authentication data 560 * @return true if the MAC is valid 561 * @type Boolean 562 */ 563 PACE.prototype.verifyAuthenticationToken = function(authToken) { 564 var t = PACE.encodePublicKey(this.algo, this.puk, (this.version == 1)); 565 GPSystem.trace("Authentication Token:"); 566 GPSystem.trace(t); 567 568 if (this.symalgo == Key.DES) { 569 var inp = t.getBytes().pad(Crypto.ISO9797_METHOD_2); 570 var at = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, inp); 571 } else { 572 var at = this.crypto.sign(this.kmac, Crypto.AES_CMAC, t.getBytes()).left(8); 573 } 574 575 return at.equals(authToken); 576 } 577 578 579 580 /** 581 * Returns true, if the mapping has been performed. 582 * @return true if the mapping has been performed 583 * @type Boolean 584 */ 585 PACE.prototype.hasMapping = function() { 586 return (typeof(this.ephDomParam) != "undefined"); 587 } 588 589 590 591 /** 592 * Describe key 593 * @param {Key} the key 594 * @return the string describing the key 595 * @type String 596 */ 597 PACE.keyToString = function(key) { 598 var str = ""; 599 var kval = key.getComponent(Key.AES); 600 if (kval) { 601 str += "(AES) " + kval + "\n"; 602 } 603 var kval = key.getComponent(Key.DES); 604 if (kval) { 605 str += "(DES) " + kval + "\n"; 606 } 607 return str; 608 } 609 610 611 612 /** 613 * Returns a human readable presentation of the current pace state. 614 * return {String} the object information 615 */ 616 PACE.prototype.toString = function() { 617 var str = "Algorithm " + this.algo + "\n"; 618 619 if (typeof(this.pacekey) != "undefined") { 620 str += "PACE Key " + PACE.keyToString(this.pacekey); 621 } 622 623 if (typeof(this.nonce) != "undefined") { 624 str += "Nonce " + this.nonce + "\n"; 625 } 626 627 if (typeof(this.ephDomParam) != "undefined") { 628 str += "Point G' " + this.ephDomParam.getComponent(Key.ECC_GX) + " " + this.ephDomParam.getComponent(Key.ECC_GY) + "\n"; 629 } 630 631 if (typeof(this.kenc) != "undefined") { 632 str += "Kenc " + PACE.keyToString(this.kenc); 633 } 634 635 if (typeof(this.kmac) != "undefined") { 636 str += "Kmac" + PACE.keyToString(this.kmac); 637 } 638 639 return str; 640 } 641 642 643 PACE.bsi_de = "0.4.0.127.0.7"; 644 PACE.id_PACE = PACE.bsi_de + ".2.2.4"; 645 PACE.id_PACE_ECDH_GM = PACE.id_PACE + ".2"; 646 PACE.id_PACE_ECDH_GM_3DES_CBC_CBC = PACE.id_PACE_ECDH_GM + ".1"; 647 PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_128 = PACE.id_PACE_ECDH_GM + ".2"; 648 PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_192 = PACE.id_PACE_ECDH_GM + ".3"; 649 PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_256 = PACE.id_PACE_ECDH_GM + ".4"; 650 651 PACE.id_roles = PACE.bsi_de + ".3.1.2"; 652 PACE.id_IS = PACE.id_roles + ".1"; 653 654