1 /** 2 * --------- 3 * |.##> <##.| SmartCard-HSM Support Scripts 4 * |# #| 5 * |# #| Copyright (c) 2011-2012 CardContact Software & System Consulting 6 * |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de) 7 * --------- 8 * 9 * Consult your license package for usage terms and conditions. 10 * 11 * @fileoverview SmartCard-HSM Card Service 12 */ 13 14 if (typeof(__ScriptingServer) == "undefined") { 15 load("../../icao/cvc.js"); 16 load("../../icao/chipauthentication.js"); 17 } 18 19 20 /** 21 * Create a SmartCard-HSM access object 22 * @class Class implementing support for SmartCard-HSM access 23 * @constructor 24 * @param {Card} card the card object 25 */ 26 function SmartCardHSM(card) { 27 this.card = card; 28 this.maxAPDU = 1000; 29 // this.maxAPDU = 255; // Enable for MicroSD card or set in calling application 30 31 // Check if SmartCard-HSM is already selected 32 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0x20, 0x00, 0x81); 33 if ((this.card.SW == 0x6E00) || ((this.card.SW == 0x6900))) { 34 this.logout(); // Select application 35 } 36 37 this.namemap = []; 38 this.idmap = []; 39 } 40 41 42 SmartCardHSM.C_DevAut = new ByteString("2F02", HEX); 43 SmartCardHSM.PrK_DevAut = 0; 44 SmartCardHSM.PIN_User = 0x81; 45 46 SmartCardHSM.PRKDPREFIX = 0xC4; 47 SmartCardHSM.KEYMETAPREFIX = 0xCB; 48 SmartCardHSM.KEYPREFIX = 0xCC; 49 SmartCardHSM.CONFIDENTIALDATAPREFIX = 0xCD; 50 SmartCardHSM.EECERTIFICATEPREFIX = 0xCE; 51 SmartCardHSM.CACERTIFICATEPREFIX = 0xCA; 52 53 SmartCardHSM.rootCerts = { 54 DESRCACC100001: new CVC(new ByteString("7F218201B47F4E82016C5F290100420E44455352434143433130303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641046D025A8026CDBA245F10DF1B72E9880FFF746DAB40A43A3D5C6BEBF27707C30F6DEA72430EE3287B0665C1EAA6EAA4FA26C46303001983F82BD1AA31E03DA0628701015F200E44455352434143433130303030317F4C10060B2B0601040181C31F0301015301C05F25060102010100095F24060302010100085F37409DBB382B1711D2BAACB0C623D40C6267D0B52BA455C01F56333DC9554810B9B2878DAF9EC3ADA19C7B065D780D6C9C3C2ECEDFD78DEB18AF40778ADF89E861CA", HEX)), 55 UTSRCACC100001: new CVC(new ByteString("7F218201B47F4E82016C5F290100420E55545352434143433130303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104A041FEB2FD116B2AD19CA6B7EACD71C9892F941BB88D67DCEEC92501F070011957E22122BA6C2CF5FF02936F482E35A6129CCBBA8E9383836D3106879C408EF08701015F200E55545352434143433130303030317F4C10060B2B0601040181C31F0301015301C05F25060102010100095F24060302010100085F3740914DD0FA00615C44048D1467435400423A4AD1BD37FD98D6DE84FD8037489582325C72956D4FDFABC6EDBA48184A754F37F1BE5142DD1C27D66569308CE19AAF", HEX)) 56 } 57 58 SmartCardHSM.devAutPuk = new Key(); 59 SmartCardHSM.devAutPuk.setType(Key.PUBLIC); 60 SmartCardHSM.devAutPuk.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID)); 61 SmartCardHSM.devAutPuk.setComponent(Key.ECC_QX, new ByteString("4C01EA36C5065FF47E8F0676A77CDCED6C8F745E6784F7807F5520124F81ED05", HEX)); 62 SmartCardHSM.devAutPuk.setComponent(Key.ECC_QY, new ByteString("4112DCE471CA003442830A10C75B31F9BFADD60628F47131628C7254AD8B956A", HEX)); 63 64 65 66 /** 67 * Validate device certificate chain 68 * 69 * @param {Crypto} crypto the crypto provider to use 70 * @param {ByteString} devAutCert the device certificate chain read from EF.C_DevAut 71 * @type Key 72 * @return the device authentication public key 73 */ 74 SmartCardHSM.validateCertificateChain = function(crypto, devAutCert) { 75 // Device device certificate 76 var cvc = new CVC(devAutCert); 77 print("Device Certificate : " + cvc); 78 79 if (cvc.getCAR().toString() == "DECA00001") { // CA used for development version up to 0.17 80 if (!cvc.verifyWith(crypto, SmartCardHSM.devAutPuk)) { 81 print("Device certificate verification failed for CAR=DECA00001"); 82 return null; 83 } 84 var path = "/" + cvc.getCAR().getHolder() + "/" + cvc.getCHR().getHolder(); 85 return { devicecert: cvc, publicKey:cvc.getPublicKey(), path:path }; 86 } 87 88 // Decode device issuer certificate 89 var dica = new CVC(devAutCert.bytes(cvc.getASN1().size)); 90 print("Device Issuer CA : " + dica); 91 92 // Determine root certificate 93 var srca = SmartCardHSM.rootCerts[dica.getCAR()]; 94 print("SmartCard-HSM Root CA : " + srca); 95 96 // Validate chain 97 var srcapuk = srca.getPublicKey(); 98 var oid = srca.getPublicKeyOID(); 99 if (!dica.verifyWith(crypto, srcapuk, oid)) { 100 print("DICA certificate not verified"); 101 return null; 102 } 103 104 var dicapuk = dica.getPublicKey(srcapuk); 105 if (!cvc.verifyWith(crypto, dicapuk, oid)) { 106 print("Device certificate verification failed"); 107 return null; 108 } 109 110 var path = "/" + srca.getCHR().getHolder() + "/" + dica.getCHR().getHolder() + "/" + cvc.getCHR().getHolder(); 111 return { srca: srca, dica: dica, devicecert: cvc, publicKey:cvc.getPublicKey(srcapuk), path:path }; 112 } 113 114 115 116 /** 117 * Validate device certificate chain 118 * 119 * @param {Crypto} crypto the crypto provider to use 120 * @type Key 121 * @return the device authentication public key 122 */ 123 SmartCardHSM.prototype.validateCertificateChain = function(crypto) { 124 // Read concatenation of both certificates 125 var devAutCert = this.readBinary(SmartCardHSM.C_DevAut); 126 var chain = SmartCardHSM.validateCertificateChain(crypto, devAutCert); 127 if (chain == null) { 128 return null; 129 } 130 return chain.publicKey; 131 } 132 133 134 135 /** 136 * Open a secure channel using device authentication 137 * 138 * @param {Crypto} crypto the crypto provider to use 139 * @param {Key} devAuthPK the device authentication public key 140 * @type ISOSecureChannel 141 * @return the initialized secure channel 142 */ 143 SmartCardHSM.prototype.openSecureChannel = function(crypto, devAuthPK) { 144 145 var protocol = new ByteString("id-CA-ECDH-3DES-CBC-CBC", OID); 146 var ca = new ChipAuthentication(crypto, protocol, devAuthPK); // For domain parameter 147 ca.noPadding = true; 148 ca.generateEphemeralCAKeyPair(); 149 150 // Perform chip authentication 151 152 var bb = new ByteBuffer(); 153 bb.append(new ASN1(0x80, protocol).getBytes()); 154 155 this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, bb.toByteString(), [0x9000]); 156 157 var ephemeralPublicKeyIfd = ca.getEphemeralPublicKey(); 158 159 var dado = new ASN1(0x7C, new ASN1(0x80, ephemeralPublicKeyIfd)); 160 161 var dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]); 162 163 // print(dadobin); 164 165 var dado = new ASN1(dadobin); 166 assert(dado.tag == 0x7C); 167 assert(dado.elements == 2); 168 var nonceDO = dado.get(0); 169 assert(nonceDO.tag == 0x81); 170 var nonce = nonceDO.value; 171 172 var authTokenDO = dado.get(1); 173 assert(authTokenDO.tag == 0x82); 174 var authToken = authTokenDO.value; 175 176 var enc = new ByteString("04", HEX); 177 enc = enc.concat(devAuthPK.getComponent(Key.ECC_QX)); 178 enc = enc.concat(devAuthPK.getComponent(Key.ECC_QY)); 179 180 GPSystem.trace("Encoded CA public key: " + enc); 181 ca.performKeyAgreement(enc, nonce); 182 var result = ca.verifyAuthenticationToken(authToken); 183 184 if (!result) { 185 GPSystem.trace("Authentication token invalid"); 186 throw new Error("Authentication token invalid"); 187 } 188 GPSystem.trace("Authentication token valid"); 189 var sm = new IsoSecureChannel(crypto); 190 sm.setEncKey(ca.kenc); 191 sm.setMacKey(ca.kmac); 192 sm.setMACSendSequenceCounter(new ByteString("0000000000000000", HEX)); 193 194 this.card.setCredential(sm); 195 return sm; 196 } 197 198 199 200 /** 201 * Update transparent EF referenced by file identifier 202 * 203 * @param {ByteString} fid the two byte file identifier 204 * @param {Number} offset the offset into the EF 205 * @param {ByteString} data the data to write 206 */ 207 SmartCardHSM.prototype.updateBinary = function(fid, offset, data) { 208 209 var bytesLeft = data.length; 210 var offset = 0; 211 212 while (bytesLeft > 0) { 213 var toSend = bytesLeft >= this.maxAPDU ? this.maxAPDU : bytesLeft; 214 215 var t54 = new ASN1(0x54, ByteString.valueOf(offset, 2)); 216 var t53 = new ASN1(0x53, data.bytes(offset, toSend)); 217 218 var cdata = t54.getBytes().concat(t53.getBytes()); 219 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xD7, fid.byteAt(0), fid.byteAt(1), cdata, [0x9000]); 220 221 bytesLeft -= toSend; 222 offset += toSend; 223 } 224 } 225 226 227 228 /** 229 * Read transparent EF referenced by file identifier 230 * 231 * @param {ByteString} fid the two byte file identifier 232 * @param {Number} offset the offset into the EF (optional) 233 * @param {Number} length the number of byte to read (optional) 234 * @type ByteString 235 * @return the data read from the EF 236 */ 237 SmartCardHSM.prototype.readBinary = function(fid, offset, length) { 238 if (typeof(offset) == "undefined") { 239 offset = 0; 240 } 241 242 var rsp = new ByteBuffer(); 243 do { 244 var t54 = new ASN1(0x54, ByteString.valueOf(offset, 2)); 245 246 if (length) { // Is a length defined ? 247 var le = length > this.maxAPDU ? this.maxAPDU : length; // Truncate if larger than maximum APDU size ? 248 } else { 249 var le = this.maxAPDU < 256 ? 0 : 65536; // Get all with Le=0 in either short or extended APDU mode 250 } 251 252 var data = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xB1, fid.byteAt(0), fid.byteAt(1), t54.getBytes(), le, [0x9000, 0x6282]); 253 254 rsp.append(data); 255 offset += data.length; 256 257 if (le == 65536) { // Only a single command required when send as extended length APDU 258 break; 259 } 260 if (length) { // Length was defined, see if we already got everything 261 length -= data.length; 262 if (length <= 0) { 263 break; 264 } 265 } 266 } while ((this.card.SW == 0x9000) && (data.length > 0)); 267 268 return rsp.toByteString(); 269 } 270 271 272 273 /** 274 * Delete file system object (EF or key) 275 * 276 * @param {ByteString} fid the two byte file object identifier 277 */ 278 SmartCardHSM.prototype.deleteFile = function(fid) { 279 return this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xE4, 0x02, 0x00, fid, [0x9000]); 280 } 281 282 283 284 /** 285 * Strips leading zeros of a ByteString 286 * 287 * @param {ByteString} value the ByteString value 288 * @return the stripped ByteString object, may be an empty ByteString 289 * @type ByteString 290 */ 291 SmartCardHSM.stripLeadingZeros = function(value) { 292 var i = 0; 293 for (; (i < value.length) && (value.byteAt(i) == 0); i++); 294 295 return value.right(value.length - i); 296 } 297 298 299 300 /** 301 * Build input for Generate Asymmetric Key Pair command for generating an ECC key pair 302 * 303 * @param {PublicKeyReference} innerCAR the CA the request shall be directed to 304 * @param {ByteString} algo the public key algorithm 305 * @param {PublicKeyReference} chr the certificate holder reference associated with this key 306 * @param {Key} dp the domain parameter for the key 307 * @param {PublicKeyReference} outerCAR the certificate holder reference of the public key for verifying the outer signature 308 * @param {Key} privateKey optional parameter to supply a private key value for import. This only works with the development version 309 * of the SmartCard-HSM. 310 * @type ByteString 311 * @return the encoded C-Data for GENERATE ASYMMETRIC KEY PAIR 312 */ 313 SmartCardHSM.buildGAKPwithECC = function(innerCAR, algo, chr, dp, outerCAR, priKey) { 314 315 // Encode G 316 var bb = new ByteBuffer(); 317 // uncompressed encoding 318 bb.append(new ByteString("04", HEX)); 319 bb.append(dp.getComponent(Key.ECC_GX)); 320 bb.append(dp.getComponent(Key.ECC_GY)); 321 var G = bb.toByteString(); 322 323 var t = new ASN1(0x30, 324 new ASN1("CPI", 0x5F29, new ByteString("00", HEX)), 325 new ASN1("CAR", 0x42, innerCAR.getBytes()), 326 new ASN1("Public Key", 0x7F49, 327 new ASN1("Object Identifier", 0x06, algo), 328 new ASN1("Prime Modulus", 0x81, dp.getComponent(Key.ECC_P)), 329 new ASN1("First coefficient a", 0x82, dp.getComponent(Key.ECC_A)), 330 new ASN1("Second coefficient b", 0x83, dp.getComponent(Key.ECC_B)), 331 new ASN1("Base Point G", 0x84, G), 332 new ASN1("Order of the base point", 0x85, dp.getComponent(Key.ECC_N)), 333 new ASN1("Cofactor f", 0x87, SmartCardHSM.stripLeadingZeros(dp.getComponent(Key.ECC_H))) 334 ), 335 new ASN1("CHR", 0x5F20, chr.getBytes()) 336 ); 337 338 if (typeof(outerCAR) != "undefined") { 339 t.add(new ASN1("OuterCAR", 0x45, outerCAR.getBytes())); 340 } 341 342 if (priKey != undefined) { 343 var d = new ASN1("Private Key", 0x8A, priKey.getComponent(Key.ECC_D)); 344 t.get(2).add(d); 345 // print(t); 346 } 347 return t.value; 348 } 349 350 351 352 /** 353 * Build input for Generate Asymmetric Key Pair command for generating a RSA key pair 354 * 355 * @param {PublicKeyReference} innerCAR the CA the request shall be directed to 356 * @param {ByteString} algo the public key algorithm 357 * @param {PublicKeyReference} chr the certificate holder reference associated with this key 358 * @param {Number} keysize the module size in bits (1024, 1536 or 2048) 359 * @param {PublicKeyReference} outerCAR the certificate holder reference of the public key for verifying the outer signature 360 * @type ByteString 361 * @return the encoded C-Data for GENERATE ASYMMETRIC KEY PAIR 362 */ 363 SmartCardHSM.buildGAKPwithRSA = function(innerCAR, algo, chr, keysize, outerCAR) { 364 365 var t = new ASN1(0x30, 366 new ASN1("CPI", 0x5F29, new ByteString("00", HEX)), 367 new ASN1("CAR", 0x42, innerCAR.getBytes()), 368 new ASN1("Public Key", 0x7F49, 369 new ASN1("Object Identifier", 0x06, algo), 370 new ASN1("Public Key Exponent", 0x82, ByteString.valueOf(65537)), 371 new ASN1("Key Size", 0x02, ByteString.valueOf(keysize)) 372 ), 373 new ASN1("CHR", 0x5F20, chr.getBytes()) 374 ); 375 376 if (typeof(outerCAR) != "undefined") { 377 t.add(new ASN1("OuterCAR", 0x45, outerCAR.getBytes())); 378 } 379 return t.value; 380 } 381 382 383 384 /** 385 * Create a PKCS#15 PrivateECCKey description 386 * 387 * @param {Number} keyid the key identifier 388 * @param {String} label the key label 389 * @type ASN1 390 * @return the PrivateECCKey description 391 */ 392 SmartCardHSM.buildPrkDforECC = function(keyid, label, keysize) { 393 var prkd = new ASN1(0xA0, 394 new ASN1(ASN1.SEQUENCE, 395 new ASN1(ASN1.UTF8String, new ByteString(label, UTF8)) 396 // new ASN1(ASN1.BIT_STRING, new ByteString("0780", HEX)), 397 // new ASN1(ASN1.OCTET_STRING, new ByteString("01", HEX)) 398 ), 399 new ASN1(ASN1.SEQUENCE, 400 new ASN1(ASN1.OCTET_STRING, ByteString.valueOf(keyid)), 401 new ASN1(ASN1.BIT_STRING, new ByteString("072080", HEX)) 402 ), 403 new ASN1(0xA1, 404 new ASN1(ASN1.SEQUENCE, 405 new ASN1(ASN1.SEQUENCE, 406 new ASN1(ASN1.OCTET_STRING, new ByteString("", HEX)) 407 ) 408 ) 409 ) 410 ); 411 412 if (keysize != undefined) { 413 assert(keysize > 0); 414 var tlvint = ByteString.valueOf(keysize); 415 if (tlvint.byteAt(0) >= 0x80) { 416 tlvint = (new ByteString("00", HEX)).concat(tlvint); 417 } 418 prkd.get(2).get(0).add(new ASN1(ASN1.INTEGER, tlvint)); 419 } 420 421 // print(prkd); 422 return prkd; 423 } 424 425 426 427 /** 428 * Create a PKCS#15 PrivateRSAKey description 429 * 430 * @param {Number} keyid the key identifier 431 * @param {String} label the key label 432 * @param {Number} modulussize 433 * @type ASN1 434 * @return the PrivateECCKey description 435 */ 436 SmartCardHSM.buildPrkDforRSA = function(keyid, label, modulussize) { 437 var prkd = new ASN1(0x30, 438 new ASN1(ASN1.SEQUENCE, 439 new ASN1(ASN1.UTF8String, new ByteString(label, UTF8)) 440 // new ASN1(ASN1.BIT_STRING, new ByteString("0780", HEX)), 441 // new ASN1(ASN1.OCTET_STRING, new ByteString("01", HEX)) 442 ), 443 new ASN1(ASN1.SEQUENCE, 444 new ASN1(ASN1.OCTET_STRING, ByteString.valueOf(keyid)), 445 new ASN1(ASN1.BIT_STRING, new ByteString("0274", HEX)) 446 ), 447 new ASN1(0xA1, 448 new ASN1(ASN1.SEQUENCE, 449 new ASN1(ASN1.SEQUENCE, 450 new ASN1(ASN1.OCTET_STRING, new ByteString("", HEX)) 451 ), 452 new ASN1(ASN1.INTEGER, ByteString.valueOf(modulussize)) 453 ) 454 ) 455 ); 456 // print(prkd); 457 return prkd; 458 } 459 460 461 462 /** 463 * Dump C-Data of Generate Asymmetric Key Pair command 464 * 465 * @param {ByteString} keydata the content of C-Data 466 */ 467 SmartCardHSM.dumpKeyData = function(keydata) { 468 print(keydata); 469 var a = new ASN1(0x30, keydata); 470 var a = new ASN1(a.getBytes()); 471 for (var i = 0; i < a.elements; i++) { 472 print(a.get(i)); 473 } 474 } 475 476 477 478 /** 479 * Generate an asymmetric key pair 480 * 481 * @param {Number} newkid key identifier for new key 482 * @param {Number} signkid key identifier for signing the new public key 483 * @param {ByteString} keydata the key data template 484 * @type ByteString 485 * @return the certificate signing request containing the new public key 486 */ 487 SmartCardHSM.prototype.generateAsymmetricKeyPair = function(newkid, signkid, keydata) { 488 489 if (this.maxAPDU > 255) { // Use extended length 490 var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0x46, newkid, signkid, keydata, 65536, [0x9000]); 491 } else { 492 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0x46, newkid, signkid, keydata, [0x9000]); 493 var rsp = this.readBinary(ByteString.valueOf(0xCE00 + newkid), 0); 494 } 495 496 return rsp; 497 } 498 499 500 501 /** 502 * Initialize device and clear all keys and files 503 * 504 * @param {ByteString} options two byte option mask 505 * @param {ByteString} initialPIN initial user PIN value 506 * @param {ByteString} initializationCode secret code for device initialization (set during first use) 507 * @param {Number} retryCounterInitial retry counter for user PIN 508 * @param {Number} keyshares number of device key encryption key shares (optional) 509 */ 510 SmartCardHSM.prototype.initDevice = function(options, initialPIN, initializationCode, retryCounterInitial, keyshares) { 511 var s = new ASN1(0x30, 512 new ASN1(0x80, options), 513 new ASN1(0x81, initialPIN), 514 new ASN1(0x82, initializationCode), 515 new ASN1(0x91, ByteString.valueOf(retryCounterInitial)) 516 ); 517 518 if (typeof(keyshares) != "undefined") { 519 s.add(new ASN1(0x92, ByteString.valueOf(keyshares))); 520 } 521 this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x50, 0x00, 0x00, s.value, [0x9000]); 522 } 523 524 525 526 /** 527 * Import DKEK share or query status 528 * 529 * @param {ByteString} keyshare 32 byte key share 530 * @type Object 531 * @return object with properties sw{Number}, shares{Number}, outstanding{Number} and kcv{ByteString} 532 */ 533 SmartCardHSM.prototype.importKeyShare = function(keyshare) { 534 if (typeof(keyshare) != "undefined") { 535 var status = this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x52, 0x00, 0x00, keyshare, 0); 536 } else { 537 var status = this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x52, 0x00, 0x00, 0); 538 } 539 if (status.length == 0) { 540 return { sw: this.card.SW }; 541 } 542 return { sw: this.card.SW, shares: status.byteAt(0), outstanding: status.byteAt(1), kcv: status.bytes(2) }; 543 } 544 545 546 547 /** 548 * Wrap key under DKEK 549 * 550 * @param {Number} id key id 551 * @type ByteString 552 * @return key blob with encrypted key value 553 */ 554 SmartCardHSM.prototype.wrapKey = function(id) { 555 var keyblob = this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x72, id, 0x92, 65536, [0x9000]); 556 return keyblob; 557 } 558 559 560 561 /** 562 * Unwrap key with DKEK 563 * 564 * @param {Number} id key id 565 * @param {ByteString} keyblob the wrapped key 566 */ 567 SmartCardHSM.prototype.unwrapKey = function(id, keyblob) { 568 this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x74, id, 0x93, keyblob, [0x9000]); 569 } 570 571 572 573 /** 574 * Verify User PIN 575 * 576 * @param {ByteString} userPIN user PIN value 577 * @return the status word SW1/SW2 returned by the device 578 */ 579 SmartCardHSM.prototype.verifyUserPIN = function(userPIN) { 580 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0x20, 0x00, 0x81, userPIN); 581 return this.card.SW; 582 } 583 584 585 586 /** 587 * Logout 588 * 589 */ 590 SmartCardHSM.prototype.logout = function() { 591 this.card.sendApdu(0x00, 0xA4, 0x04, 0x04, new ByteString("E82B0601040181C31F0201", HEX), [0x9000]); 592 } 593 594 595 596 /** 597 * Change User PIN 598 * 599 * @param {ByteString} currentPIN current user PIN value 600 * @param {ByteString} newPIN new user PIN value 601 */ 602 SmartCardHSM.prototype.changeUserPIN = function(currentPIN, newPIN) { 603 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0x24, 0x00, 0x81, currentPIN.concat(newPIN), [0x9000]); 604 } 605 606 607 608 /** 609 * Request PIN Status Information 610 * 611 * @type Number 612 * @return the status word SW1/SW2 returned by the device 613 */ 614 SmartCardHSM.prototype.queryUserPINStatus = function() { 615 this.card.sendSecMsgApdu(Card.ALL, 0x00, 0x20, 0x00, 0x81, 0, [0x9000, 0x63C7, 0x63C6, 0x63C5, 0x63C4, 0x63C3, 0x63C2, 0x63C1, 0x6983, 0x6984]); 616 return this.card.SW; 617 } 618 619 620 621 /** 622 * Enumerate Objects 623 * 624 * @return the enumeration 625 */ 626 SmartCardHSM.prototype.enumerateObjects = function() { 627 var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x58, 0x00, 0x00, 65536, [0x9000]); 628 return rsp; 629 } 630 631 632 633 /** 634 * Generate random data 635 * 636 * @param {Number} length number of bytes 637 * @return the random bytes 638 */ 639 SmartCardHSM.prototype.generateRandom = function(length) { 640 var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0x84, 0x00, 0x00, length, [0x9000]); 641 return rsp; 642 } 643 644 645 646 /** 647 * Sign data using referenced key 648 * 649 * @param {Number} keyid the key identifier for signing 650 * @param {algo} algo the algorithm identifier 651 * @param {ByteString} data the data to be signed 652 * @return the signature value 653 */ 654 SmartCardHSM.prototype.sign = function(keyid, algo, data) { 655 var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x68, keyid, algo, data, 0x00, [0x9000]); 656 return rsp; 657 } 658 659 660 661 /** 662 * Decipher cryptogram or agree shared secret using Diffie-Hellman 663 * 664 * @param {Number} keyid the key identifier 665 * @param {Number} algo the algorithm identifier 666 * @param {ByteString} data the the cryptogram or concatenation of x || y of ECC public key 667 * @return the plain output 668 */ 669 SmartCardHSM.prototype.decipher = function(keyid, algo, data) { 670 var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x62, keyid, algo, data, 0x00, [0x9000]); 671 return rsp; 672 } 673 674 675 676 /** 677 * Enumerate key objects in the SmartCard-HSM and build the map of keys 678 * 679 * @type String[] 680 * @return the list of key labels 681 */ 682 SmartCardHSM.prototype.enumerateKeys = function() { 683 this.namemap = []; 684 this.idmap = []; 685 686 var fobs = this.enumerateObjects(); 687 688 // Process keys 689 for (var i = 0; i < fobs.length; i += 2) { 690 if (fobs.byteAt(i) == SmartCardHSM.KEYPREFIX) { 691 var kid = fobs.byteAt(i + 1); 692 if (kid > 0) { 693 // print("Found key: " + kid); 694 this.idmap[kid] = new SmartCardHSMKey(this, kid); 695 } 696 } 697 } 698 699 var keylist = []; 700 // Process PKCS#15 private key descriptions 701 for (var i = 0; i < fobs.length; i += 2) { 702 if (fobs.byteAt(i) == SmartCardHSM.PRKDPREFIX) { 703 var kid = fobs.byteAt(i + 1); 704 var descbin = this.readBinary(fobs.bytes(i, 2)); 705 var desc = new ASN1(descbin); 706 var key = this.idmap[kid]; 707 if (key) { 708 key.setDescription(desc); 709 var label = key.getLabel(); 710 // print(key.getId() + " - " + label); 711 keylist.push(label); 712 this.namemap[label] = key; 713 } 714 } 715 } 716 717 return keylist; 718 } 719 720 721 722 /** 723 * Determine an unused key identifier 724 * 725 * @type Number 726 * @return a free key identifier or -1 if all key identifier in use 727 */ 728 SmartCardHSM.prototype.determineFreeKeyId = function() { 729 for (var i = 1; i < 256; i++) { 730 if (this.idmap[i] == undefined) { 731 return i; 732 } 733 } 734 return -1; 735 } 736 737 738 739 /** 740 * Add a new key to the map of keys 741 * 742 * @param {HSMKey} key the HSM key 743 */ 744 SmartCardHSM.prototype.addKeyToMap = function(key) { 745 var label = key.getLabel(); 746 var id = key.getId(); 747 this.namemap[label] = key; 748 this.idmap[id] = key; 749 } 750 751 752 753 /** 754 * Get a key reference object 755 * 756 * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM") 757 * @returns the key or null if not found 758 * @type Key 759 */ 760 SmartCardHSM.prototype.getKey = function(label) { 761 var key = this.namemap[label]; 762 if (key == undefined) { 763 return null; 764 } 765 return key; 766 } 767 768 769 770 /** 771 * Get crypto object 772 * 773 * @type HSMCrypto 774 * @return the HSMCrypto object 775 */ 776 SmartCardHSM.prototype.getCrypto = function() { 777 if (this.crypto == undefined) { 778 this.crypto = new SmartCardHSMCrypto(new Crypto()); 779 } 780 return this.crypto; 781 } 782 783 784 785 /** 786 * Create crypto object implementing access to the SmartCard-HSM 787 * 788 * @class Wrapper to provide Crypto interface to SmartCard-HSM 789 * @constructor 790 * @param {Crypto} crypto the backend crypto provider 791 */ 792 function SmartCardHSMCrypto(crypto) { 793 this.crypto = crypto; 794 } 795 796 797 798 /** 799 * Sign a message using the defined mechanism and key 800 * 801 * @param {HSMKey} key the private key 802 * @param {Number} mech the mechanism (e.g. Crypto.ECDSA) 803 * @param {ByteString} message the message to be signed 804 * @type ByteString 805 * @return the signature 806 */ 807 SmartCardHSMCrypto.prototype.sign = function(key, mech, message) { 808 if (key instanceof SmartCardHSMKey) { 809 return key.sign(mech, message); 810 } else { 811 return this.crypto.sign(key, mech, message); 812 } 813 } 814 815 816 817 /** 818 * Verify a message using the defined mechanism and key 819 * 820 * @param {Key} key the public key 821 * @param {Number} mech the mechanism (e.g. Crypto.ECDSA) 822 * @param {ByteString} message the message to be signed 823 * @param {ByteString} signature the signature to verify 824 * @type Boolean 825 * @return true if signature is valid 826 */ 827 SmartCardHSMCrypto.prototype.verify = function(key, mech, message, signature) { 828 return this.crypto.verify(key, mech, message, signature); 829 } 830 831 832 833 /** 834 * Create a key access object 835 * 836 * @class Class implementing key access 837 * @param {SmartCardHSM} sc the card access object 838 * @param {Number} id the key identifier 839 */ 840 function SmartCardHSMKey(sc, id) { 841 this.sc = sc; 842 this.id = id; 843 } 844 845 846 847 /** 848 * Set the PKCS#15 private key description 849 * 850 * @param {ASN1} desc the description 851 */ 852 SmartCardHSMKey.prototype.setDescription = function(desc) { 853 this.desc = desc; 854 } 855 856 857 858 /** 859 * Return the key identifier 860 * 861 * @type Number 862 * @return the key identifier 863 */ 864 SmartCardHSMKey.prototype.getId = function() { 865 return this.id; 866 } 867 868 869 870 /** 871 * Return the key label as encoded in the PKCS#15 structure 872 * 873 * @type String 874 * @return the key label 875 */ 876 SmartCardHSMKey.prototype.getLabel = function() { 877 if (this.desc == undefined) { 878 return undefined; 879 } 880 return this.desc.get(0).get(0).value.toString(UTF8); 881 } 882 883 884 885 /** 886 * Return the key size in bits 887 * 888 * @type Number 889 * @return the key size in bits 890 */ 891 SmartCardHSMKey.prototype.getSize = function() { 892 if (this.desc == undefined) { 893 return undefined; 894 } 895 // print(this.desc); 896 if (this.desc.get(2).elements > 1) { // Fix a bug from early versions 897 return this.desc.get(2).get(1).value.toUnsigned(); 898 } else { 899 return this.desc.get(2).get(0).get(1).value.toUnsigned(); 900 } 901 } 902 903 904 905 /** 906 * Sign data using a key in the SmartCard-HSM 907 * 908 * @param {ByteString} data to be signed 909 * @param {Number} mech the signing mechanism 910 * @type ByteString 911 * @return the signature 912 */ 913 SmartCardHSMKey.prototype.sign = function(mech, data) { 914 if (mech) { 915 switch(mech) { 916 case Crypto.RSA: 917 algo = 0x20; 918 break; 919 case Crypto.RSA_SHA1: 920 algo = 0x31; 921 break; 922 case Crypto.RSA_SHA256: 923 algo = 0x33; 924 break; 925 case Crypto.RSA_PSS_SHA1: 926 algo = 0x41; 927 break; 928 case Crypto.RSA_PSS_SHA256: 929 algo = 0x43; 930 break; 931 case Crypto.ECDSA: 932 algo = 0x70; 933 break; 934 case Crypto.ECDSA_SHA1: 935 algo = 0x71; 936 break; 937 case Crypto.ECDSA_SHA224: 938 algo = 0x72; 939 break; 940 case Crypto.ECDSA_SHA256: 941 algo = 0x73; 942 break; 943 default: 944 throw new GPError("SmartCardHSMKey", GPError.INVALID_DATA, mech, "Unsupported crypto mechanism"); 945 } 946 } 947 948 return this.sc.sign(this.id, algo, data); 949 } 950 951 952 953 /** 954 * Return human readable string 955 */ 956 SmartCardHSMKey.prototype.toString = function() { 957 return "SmartCardHSMKey(id=" + this.id + ")"; 958 } 959 960 961 962 SmartCardHSM.test = function() { 963 var crypto = new Crypto(); 964 var card = new Card(_scsh3.reader); 965 var sc = new SmartCardHSM(card); 966 967 var pubKey = sc.validateCertificateChain(crypto); 968 sc.openSecureChannel(crypto, pubKey); 969 970 sc.verifyUserPIN(new ByteString("648219", ASCII)); 971 var list = sc.enumerateKeys(); 972 print("Keys on device: " + list); 973 974 var crypto = sc.getCrypto(); 975 var message = new ByteString("Hello World", ASCII); 976 var key = sc.getKey(list[0]); 977 var signature = crypto.sign(key, Crypto.ECDSA, message); 978 print("Signature: " + signature); 979 } 980