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 * 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 DKEK key wrapper 25 */ 26 27 28 29 /** 30 * Class supporting DKEK functions outside the SmartCard-HSM 31 * 32 * @constructor 33 * @param {Crypto} crypto the crypto provider 34 */ 35 function DKEK(crypto) { 36 this.crypto = crypto; 37 this.dkek = new ByteString("0000000000000000000000000000000000000000000000000000000000000000", HEX); 38 } 39 40 exports.DKEK = DKEK; 41 42 43 44 /** 45 * Import a DKEK share 46 * 47 * @param {ByteString} share a 32 byte share 48 */ 49 DKEK.prototype.importDKEKShare = function(share) { 50 assert(share instanceof ByteString, "Share must be of type ByteString"); 51 assert(share.length == 32, "Share must be 32 byte long"); 52 var ndkek = this.dkek.xor(share); 53 this.dkek.clear(); 54 this.dkek = ndkek; 55 } 56 57 58 59 /** 60 * Zeroize DKEK 61 * 62 */ 63 DKEK.prototype.clear = function() { 64 this.dkek.clear(); 65 } 66 67 68 69 /** 70 * Return the Key Check Value (KCV) of the internal DKEK 71 * 72 * @type ByteString 73 * @return the KCV 74 */ 75 DKEK.prototype.getKCV = function() { 76 return this.crypto.digest(Crypto.SHA_256, this.dkek).left(8); 77 } 78 79 80 81 /** 82 * Derive the encryption key from the DKEK 83 * 84 * @type ByteString 85 * @return the encryption key 86 */ 87 DKEK.prototype.getKENC = function() { 88 var kencval = this.crypto.digest(Crypto.SHA_256, this.dkek.concat(new ByteString("00000001", HEX))); 89 var kenc = new Key(); 90 kenc.setComponent(Key.AES, kencval); 91 return kenc; 92 } 93 94 95 96 /** 97 * Derive the message authentication key from the DKEK 98 * 99 * @type ByteString 100 * @return the message authentication key 101 */ 102 DKEK.prototype.getKMAC = function() { 103 var kmacval = this.crypto.digest(Crypto.SHA_256, this.dkek.concat(new ByteString("00000002", HEX))); 104 var kmac = new Key(); 105 kmac.setComponent(Key.AES, kmacval); 106 return kmac; 107 } 108 109 110 111 /** 112 * Derive DKEK share encryption key from password 113 * 114 * @param {ByteString} password the password 115 * @type ByteString 116 * @return the derived key (32 Byte) concatenated with the IV (16 Byte) 117 */ 118 DKEK.deriveDKEKShareKey = function(salt, password) { 119 var crypto = new Crypto(); 120 121 var d = new ByteString("", HEX); 122 var keyivbuff = new ByteBuffer(48); 123 124 for (j = 0; j < 3; j++) { 125 print("Derive DKEK share encryption key (Step " + (j + 1) + " of 3)..."); 126 var nd = d.concat(password); 127 d.clear(); 128 d = nd; 129 130 var nd = d.concat(salt); 131 d.clear(); 132 d = nd; 133 134 try { 135 // Try the fast hash available in scdp4j 3.8 136 var h = crypto.digest(Crypto.MD5, d, 10000000); 137 d.clear(); 138 d = h; 139 } 140 catch(e) { 141 // Fallback to slow variant 142 for (var i = 10000000; i > 0; i--) { 143 d = crypto.digest(Crypto.MD5, d); 144 } 145 } 146 keyivbuff.append(d); 147 } 148 149 var keyiv = keyivbuff.toByteString(); 150 keyivbuff.clear(); 151 return keyiv; 152 } 153 154 155 156 /** 157 * Encrypt a DKEK share 158 * 159 * @param {ByteString} keyshare the key share 160 * @param {ByteString} password the password 161 * @type ByteString 162 * @return Encrypted DKEK share value 163 */ 164 DKEK.encryptKeyShare = function(keyshare, password) { 165 assert(keyshare instanceof ByteString, "Argument keyshare must be ByteString"); 166 assert(keyshare.length == 32, "Argument keyshare must be 32 bytes"); 167 assert(password instanceof ByteString, "Argument password must be ByteString"); 168 169 var crypto = new Crypto(); 170 var salt = crypto.generateRandom(8); 171 172 var keyiv = DKEK.deriveDKEKShareKey(salt, password); 173 174 var k = new Key(); 175 k.setComponent(Key.AES, keyiv.bytes(0, 32)); 176 var iv = keyiv.bytes(32, 16); 177 keyiv.clear(); 178 179 var plain = keyshare.concat(new ByteString("10101010101010101010101010101010", HEX)); 180 var cipher = crypto.encrypt(k, Crypto.AES_CBC, plain, iv); 181 plain.clear(); 182 k.getComponent(Key.AES).clear(); 183 184 var blob = (new ByteString("Salted__", ASCII)).concat(salt).concat(cipher); 185 return blob; 186 } 187 188 189 190 /** 191 * Decrypt a DKEK share 192 * 193 * @param {ByteString} keyshare the encrypted key share as read from the .pbe file 194 * @param {ByteString} password the password 195 * @type ByteString 196 * @return plain DKEK value 197 */ 198 DKEK.decryptKeyShare = function(keyshare, password) { 199 assert(password instanceof ByteString, "Argument password must be ByteString"); 200 if ((keyshare.length != 64) || !keyshare.bytes(0, 8).toString(ASCII).equals("Salted__")) { 201 throw new GPError(module.id, GPError.INVALID_DATA, 0, "Does not seem to be an encrypted key share"); 202 } 203 204 var crypto = new Crypto(); 205 var salt = keyshare.bytes(8, 8); 206 207 var keyiv = DKEK.deriveDKEKShareKey(salt, password); 208 209 var k = new Key(); 210 k.setComponent(Key.AES, keyiv.bytes(0, 32)); 211 var iv = keyiv.bytes(32, 16); 212 keyiv.clear(); 213 214 var plain = crypto.decrypt(k, Crypto.AES_CBC, keyshare.bytes(16), iv); 215 k.getComponent(Key.AES).clear(); 216 217 if (!(new ByteString("10101010101010101010101010101010", HEX)).equals(plain.right(16))) { 218 throw new GPError(module.id, GPError.INVALID_DATA, 0, "Decryption of DKEK failed. Wrong password ?"); 219 } 220 221 var val = plain.left(32); 222 plain.clear(); 223 224 return val; 225 } 226 227 228 229 /** 230 * Test DKEK share encryption / decryption 231 * 232 * @private 233 */ 234 DKEK.testDKEK = function() { 235 var crypto = new Crypto(); 236 var dkek = crypto.generateRandom(32); 237 var password = new ByteString("Password", ASCII); 238 var enc = DKEK.encryptKeyShare(dkek, password); 239 print(enc); 240 var plain = DKEK.decryptKeyShare(enc, password); 241 assert(dkek.equals(plain), "Reference does not match"); 242 } 243 244 245 246 /** 247 * Wrap AES key 248 * 249 * @param {Key} key the secret key 250 * @type ByteString 251 * @return the secret key wrapped with the DKEK 252 */ 253 DKEK.prototype.encodeAESKey = function(key) { 254 var bb = new ByteBuffer(); 255 bb.append(this.getKCV()); 256 257 assert(key.getType() == Key.SECRET) 258 259 bb.append(15); 260 261 var daid = new ByteString("2.16.840.1.101.3.4.1", OID); 262 bb.append(ByteString.valueOf(daid.length, 2)); 263 bb.append(daid); 264 265 var aaid = new ByteString("10 11 18 99", HEX); 266 bb.append(ByteString.valueOf(aaid.length, 2)); 267 bb.append(aaid); 268 269 bb.append(ByteString.valueOf(0, 2)); 270 bb.append(ByteString.valueOf(0, 2)); 271 272 var kb = new ByteBuffer(256); 273 kb.append(this.crypto.generateRandom(8)); 274 275 kb.append(ByteString.valueOf(key.getSize() / 8, 2)); 276 kb.append(key.getComponent(Key.AES)); 277 278 var unpadded = kb.toByteString(); 279 var plain = unpadded.pad(Crypto.ISO9797_METHOD_2); 280 unpadded.clear(); 281 282 if (plain.length & 0xF) { // pad() padds to 8 byte blocks, but we 16 byte blocks 283 var nplain = plain.concat(new ByteString("0000000000000000", HEX)); 284 plain.clear(); 285 plain = nplain; 286 } 287 288 var iv = new ByteString("00000000000000000000000000000000", HEX); 289 var kenc = this.getKENC(); 290 var cipher = this.crypto.encrypt(kenc, Crypto.AES_CBC, plain, iv); 291 kenc.getComponent(Key.AES).clear(); 292 plain.clear(); 293 294 bb.append(cipher); 295 296 var kmac = this.getKMAC(); 297 bb.append(this.crypto.sign(kmac, Crypto.AES_CMAC, bb.toByteString())); 298 kmac.getComponent(Key.AES).clear(); 299 300 return bb.toByteString(); 301 } 302 303 304 305 /** 306 * Wrap RSA or ECC key 307 * 308 * @param {Key} pri the private key in CRT format 309 * @param {Key} pub the public key 310 * @type Key 311 * @return the private key in private exponent / modulus format 312 */ 313 DKEK.prototype.convertCRT2PEM = function(pri, pub) { 314 var n = pub.getComponent(Key.MODULUS); 315 var r = ByteString.valueOf(0).concat(n); 316 r = r.sub(ByteString.valueOf(0).concat(pri.getComponent(Key.CRT_P))); 317 r = r.sub(ByteString.valueOf(0).concat(pri.getComponent(Key.CRT_Q))); 318 r = r.biAdd(ByteString.valueOf(1)); 319 var e = pub.getComponent(Key.EXPONENT); 320 var d = e.modInverse(r); 321 322 // Strip leading zero in private exponent 323 if (d.byteAt(0) == 0) { 324 d = d.bytes(1); 325 } 326 327 var nk = new Key(); 328 nk.setType(Key.PRIVATE); 329 nk.setComponent(Key.MODULUS, n); 330 nk.setComponent(Key.EXPONENT, d); 331 return nk; 332 } 333 334 335 336 /** 337 * Wrap RSA or ECC key 338 * 339 * @param {Key} pri the private key 340 * @param {Key} pub the public key 341 * @type ByteString 342 * @return the private key wrapped with the DKEK 343 */ 344 DKEK.prototype.encodeKey = function(pri, pub) { 345 var bb = new ByteBuffer(); 346 bb.append(this.getKCV()); 347 348 var mod = pub.getComponent(Key.MODULUS); 349 if (mod) { 350 // Convert RSA keys larger than 2048 bit and in CRT format 351 if ((mod.length > 256) && pri.getComponent(Key.CRT_P)) { 352 pri = this.convertCRT2PEM(pri, pub); 353 } 354 if (pri.getComponent(Key.CRT_P)) { 355 bb.append(6); 356 } else { 357 bb.append(5); 358 } 359 var algo = new ByteString("id-TA-RSA-v1-5-SHA-256", OID); 360 } else { 361 bb.append(12); 362 var algo = new ByteString("id-TA-ECDSA-SHA-256", OID); 363 } 364 365 bb.append(ByteString.valueOf(algo.length, 2)); 366 bb.append(algo); 367 368 bb.append(ByteString.valueOf(0, 2)); 369 bb.append(ByteString.valueOf(0, 2)); 370 bb.append(ByteString.valueOf(0, 2)); 371 372 var kb = new ByteBuffer(4096); 373 kb.append(this.crypto.generateRandom(8)); 374 375 if (pub.getComponent(Key.MODULUS)) { 376 kb.append(ByteString.valueOf(pub.getComponent(Key.MODULUS).length << 3, 2)); 377 378 if (pri.getComponent(Key.CRT_P)) { 379 var c = pri.getComponent(Key.CRT_DP1); 380 kb.append(ByteString.valueOf(c.length, 2)); 381 kb.append(c); 382 383 var c = pri.getComponent(Key.CRT_DQ1); 384 kb.append(ByteString.valueOf(c.length, 2)); 385 kb.append(c); 386 387 var c = pri.getComponent(Key.CRT_P); 388 kb.append(ByteString.valueOf(c.length, 2)); 389 kb.append(c); 390 391 var c = pri.getComponent(Key.CRT_PQ); 392 kb.append(ByteString.valueOf(c.length, 2)); 393 kb.append(c); 394 395 var c = pri.getComponent(Key.CRT_Q); 396 kb.append(ByteString.valueOf(c.length, 2)); 397 kb.append(c); 398 } else { 399 var c = pri.getComponent(Key.EXPONENT); 400 kb.append(ByteString.valueOf(c.length, 2)); 401 kb.append(c); 402 } 403 404 var c = pub.getComponent(Key.MODULUS); 405 kb.append(ByteString.valueOf(c.length, 2)); 406 kb.append(c); 407 408 var c = pub.getComponent(Key.EXPONENT); 409 kb.append(ByteString.valueOf(c.length, 2)); 410 kb.append(c); 411 } else { 412 kb.append(ByteString.valueOf(pub.getComponent(Key.ECC_P).length << 3, 2)); 413 414 var c = pri.getComponent(Key.ECC_A); 415 kb.append(ByteString.valueOf(c.length, 2)); 416 kb.append(c); 417 418 var c = pri.getComponent(Key.ECC_B); 419 kb.append(ByteString.valueOf(c.length, 2)); 420 kb.append(c); 421 422 var c = pri.getComponent(Key.ECC_P); 423 kb.append(ByteString.valueOf(c.length, 2)); 424 kb.append(c); 425 426 var c = pri.getComponent(Key.ECC_N); 427 kb.append(ByteString.valueOf(c.length, 2)); 428 kb.append(c); 429 430 var c = (new ByteString("04", HEX)).concat(pri.getComponent(Key.ECC_GX)).concat(pri.getComponent(Key.ECC_GY)); 431 kb.append(ByteString.valueOf(c.length, 2)); 432 kb.append(c); 433 434 var c = pri.getComponent(Key.ECC_D); 435 kb.append(ByteString.valueOf(c.length, 2)); 436 kb.append(c); 437 438 var c = (new ByteString("04", HEX)).concat(pub.getComponent(Key.ECC_QX)).concat(pub.getComponent(Key.ECC_QY)); 439 kb.append(ByteString.valueOf(c.length, 2)); 440 kb.append(c); 441 } 442 443 var unpadded = kb.toByteString(); 444 var plain = unpadded.pad(Crypto.ISO9797_METHOD_2); 445 unpadded.clear(); 446 447 if (plain.length & 0xF) { // pad() padds to 8 byte blocks, but we 16 byte blocks 448 var nplain = plain.concat(new ByteString("0000000000000000", HEX)); 449 plain.clear(); 450 plain = nplain; 451 } 452 // print(plain); 453 454 var iv = new ByteString("00000000000000000000000000000000", HEX); 455 var kenc = this.getKENC(); 456 var cipher = this.crypto.encrypt(kenc, Crypto.AES_CBC, plain, iv); 457 kenc.getComponent(Key.AES).clear(); 458 plain.clear(); 459 460 bb.append(cipher); 461 462 var kmac = this.getKMAC(); 463 bb.append(this.crypto.sign(kmac, Crypto.AES_CMAC, bb.toByteString())); 464 kmac.getComponent(Key.AES).clear(); 465 466 // this.dumpKeyBLOB(bb.toByteString()); 467 return bb.toByteString(); 468 } 469 470 DKEK.prototype.encodeRSAKey = DKEK.prototype.encodeKey; 471 472 473 474 DKEK.prototype.dumpKeyBLOB = function(keyblob) { 475 // Verify MAC on last 16 byte of blob 476 477 var macok = this.crypto.verify(this.getKMAC(), Crypto.AES_CMAC, keyblob.bytes(0, keyblob.length - 16), keyblob.right(16)); 478 479 var keytype = keyblob.byteAt(8); 480 print("Values from key blob:"); 481 print("---------------------"); 482 print("Checking the MAC : " + (macok ? "Passed" : "Failed")); 483 print("KCV : " + keyblob.bytes(0, 8).toString(HEX) + " [Must match the KCV of the DKEK for import]"); 484 print("Key type : " + keytype + " [5=RSA, 6=RSA-CRT, 12=ECC, 15=AES]"); 485 486 var ofs = 9; 487 var len = keyblob.bytes(ofs, 2).toUnsigned(); 488 489 if ((keytype == 15) && (keyblob.byteAt(ofs + 2) != 0x60)) { 490 print("Default Algorithm ID : " + keyblob.bytes(ofs + 2, len).toString(HEX) + " (" + len + ") [Wrong encoding in V3.0 to V3.2]"); 491 } else { 492 print("Default Algorithm ID : " + keyblob.bytes(ofs + 2, len).toString(OID) + " (" + len + ") [Default algorithm]"); 493 } 494 495 ofs += len + 2; 496 var len = keyblob.bytes(ofs, 2).toUnsigned(); 497 498 print("Allowed Algorithm IDs : " + keyblob.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 499 500 ofs += len + 2; 501 var len = keyblob.bytes(ofs, 2).toUnsigned(); 502 503 print("Access Conditions : " + keyblob.bytes(ofs + 2, len).toString(HEX) + " (" + len + ") [Not used]"); 504 505 ofs += len + 2; 506 var len = keyblob.bytes(ofs, 2).toUnsigned(); 507 508 print("Key OID : " + keyblob.bytes(ofs + 2, len).toString(HEX) + " (" + len + ") [Not used]"); 509 510 ofs += len + 2; 511 512 513 // Decrypt key data 514 515 var iv = new ByteString("00000000000000000000000000000000", HEX); 516 var plainkey = this.crypto.decrypt(this.getKENC(), Crypto.AES_CBC, keyblob.bytes(ofs, keyblob.length - 16 - ofs), iv); 517 // print(plainkey); 518 519 print("Randomize : " + plainkey.bytes(0, 8).toString(HEX) + " [Random data prepended at export]"); 520 521 var keysize = plainkey.bytes(8, 2).toUnsigned(); 522 print("Key size : " + keysize + " [Key size in bits (ECC/RSA) or bytes (AES)]"); 523 524 var ofs = 10; 525 526 if (keytype == 5) { 527 var len = plainkey.bytes(ofs, 2).toUnsigned(); 528 529 print("Private Exponent : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 530 531 ofs += len + 2; 532 var len = plainkey.bytes(ofs, 2).toUnsigned(); 533 534 print("Modulus : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 535 536 ofs += len + 2; 537 var len = plainkey.bytes(ofs, 2).toUnsigned(); 538 539 print("Public Exponent : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 540 } else if (keytype == 6) { 541 var len = plainkey.bytes(ofs, 2).toUnsigned(); 542 543 print("DP1 = d mod (p - 1) : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 544 545 ofs += len + 2; 546 var len = plainkey.bytes(ofs, 2).toUnsigned(); 547 548 print("DQ1 = d mod (q - 1) : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 549 550 ofs += len + 2; 551 var len = plainkey.bytes(ofs, 2).toUnsigned(); 552 553 print("Prime factor p : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 554 555 ofs += len + 2; 556 var len = plainkey.bytes(ofs, 2).toUnsigned(); 557 558 print("PQ = q - 1 mod p : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 559 560 ofs += len + 2; 561 var len = plainkey.bytes(ofs, 2).toUnsigned(); 562 563 print("Prime factor q : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 564 565 ofs += len + 2; 566 var len = plainkey.bytes(ofs, 2).toUnsigned(); 567 568 print("Modulus : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 569 570 ofs += len + 2; 571 var len = plainkey.bytes(ofs, 2).toUnsigned(); 572 573 print("Public Exponent : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 574 } else if (keytype == 12) { 575 var len = plainkey.bytes(ofs, 2).toUnsigned(); 576 577 print("A : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 578 579 ofs += len + 2; 580 var len = plainkey.bytes(ofs, 2).toUnsigned(); 581 582 print("B : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 583 584 ofs += len + 2; 585 var len = plainkey.bytes(ofs, 2).toUnsigned(); 586 587 print("Prime factor P : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 588 589 ofs += len + 2; 590 var len = plainkey.bytes(ofs, 2).toUnsigned(); 591 592 print("Order : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 593 594 ofs += len + 2; 595 var len = plainkey.bytes(ofs, 2).toUnsigned(); 596 597 print("Generator G : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 598 599 ofs += len + 2; 600 var len = plainkey.bytes(ofs, 2).toUnsigned(); 601 602 print("Secret D : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 603 604 ofs += len + 2; 605 var len = plainkey.bytes(ofs, 2).toUnsigned(); 606 607 print("Public Point Q : " + plainkey.bytes(ofs + 2, len).toString(HEX) + " (" + len + ")"); 608 } else if (keytype == 15) { 609 print("Key Value : " + plainkey.bytes(ofs, keysize).toString(HEX)); 610 } else { 611 throw new GPError(module.id, GPError.INVALID_DATA, 0, "Unknown key type " + keytype); 612 } 613 } 614