1 /** 2 * --------- 3 * |.##> <##.| Open Smart Card Development Platform (www.openscdp.org) 4 * |# #| 5 * |# #| Copyright (c) 1999-2010 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 Simple CV request generator based on 25 * TR-03110 "Advanced Security Mechanisms for Machine Readable Travel Documents", Version 2.0 26 * 27 */ 28 29 // Imports 30 var CVC = require('scsh/eac/CVC').CVC; 31 var PublicKeyReference = require('scsh/eac/PublicKeyReference').PublicKeyReference; 32 33 34 35 /** 36 * Constructor for request generator 37 * 38 * @class Class implementing a generator for CVC requests 39 * 40 * @constructor 41 * @param {Crypto} Crypto object to use 42 */ 43 function EAC2CVRequestGenerator(crypto) { 44 this.crypto = crypto; 45 } 46 47 exports.EAC2CVRequestGenerator = EAC2CVRequestGenerator; 48 49 50 51 /* 52 * Convert x/y coordinates to uncompressed format 53 * 54 * x/y - coordinates of EC point 55 * 56 * return ByteString containing compressed format 57 * 58 * TODO: Move to ECUtils 59 */ 60 EAC2CVRequestGenerator.encodeUncompressedECPoint = function(x, y) { 61 62 var bb = new ByteBuffer(); 63 64 // uncompressed encoding 65 bb.append(new ByteString("04", HEX)); 66 bb.append(new ByteString(x, HEX)); 67 bb.append(new ByteString(y, HEX)); 68 69 return bb.toByteString(); 70 } 71 72 73 74 /** 75 * Strips leading zeros of a ByteString 76 * 77 * @param {ByteString} value the ByteString value 78 * @return the stripped ByteString object, may be an empty ByteString 79 * @type ByteString 80 * 81 * TODO: Move to Utils 82 */ 83 EAC2CVRequestGenerator.stripLeadingZeros = function(value) { 84 var i = 0; 85 for (; (i < value.length) && (value.byteAt(i) == 0); i++); 86 87 return value.right(value.length - i); 88 } 89 90 91 92 /** 93 * Set the public key that should be encoded within the request 94 * 95 * @param {Key} publicKey Public Key 96 */ 97 EAC2CVRequestGenerator.prototype.setPublicKey = function(publicKey) { 98 this.publicKey = publicKey; 99 } 100 101 102 103 /** 104 * Set the certficate holder reference (CHR) for the request 105 * 106 * @param {String} chr CHR for the request 107 */ 108 EAC2CVRequestGenerator.prototype.setCHR = function(chr) { 109 if (chr instanceof ByteString) { 110 this.CHR = chr; 111 } else if (chr instanceof PublicKeyReference) { 112 this.CHR = chr.getBytes(); 113 } else { 114 this.CHR = new ByteString(chr.toString(), ASCII); 115 } 116 } 117 118 119 120 /** 121 * Reset the current generator object 122 * 123 * TODO: Implement me 124 */ 125 EAC2CVRequestGenerator.prototype.reset = function() { 126 } 127 128 129 130 /** 131 * Set the certificate profile identifier (CPI) for the request 132 * 133 * @param {Number} profileID CPI for the request 134 */ 135 EAC2CVRequestGenerator.prototype.setProfileIdentifier = function(profileID) { 136 this.profileIdentifier = profileID; 137 } 138 139 140 141 /** 142 * Set the certficate authorization reference (CAR) for the request 143 * 144 * The usage of this method is optional - if no CAR is set, there will be no 145 * "inner" CAR included within the certficate request 146 * 147 * @param {String} car CAR for the request 148 */ 149 EAC2CVRequestGenerator.prototype.setCAR = function(car) { 150 if (car instanceof ByteString) { 151 this.CAR = car; 152 } else if (car instanceof PublicKeyReference) { 153 this.CAR = car.getBytes(); 154 } else { 155 this.CAR = new ByteString(car.toString(), ASCII); 156 } 157 } 158 159 160 161 /** 162 * Set the extension values that should be included within the request 163 * 164 * @param {ByteString[]} extensions Array of DER-encoded extensions 165 */ 166 EAC2CVRequestGenerator.prototype.setExtensions = function(extensions) { 167 this.extensions = extensions; 168 } 169 170 171 172 /** 173 * Set the object identifier that should be included in the public key domain parameters 174 * 175 * @param {ByteString} oid Object identifier as specified in appendix A.6.4 176 */ 177 EAC2CVRequestGenerator.prototype.setTAAlgorithmIdentifier = function(oid) { 178 this.taOID = oid; 179 } 180 181 182 183 /** 184 * Get the CAR as ByteString object 185 * 186 * @private 187 */ 188 EAC2CVRequestGenerator.prototype.getCAR = function() { 189 var t = new ASN1("Certification Authority Reference", 0x42, this.CAR); 190 return t; 191 } 192 193 194 195 /** 196 * Get the CHR as ByteString object 197 * 198 * @private 199 */ 200 EAC2CVRequestGenerator.prototype.getCHR = function() { 201 var t = new ASN1("Certification Holder Reference", 0x5F20, this.CHR); 202 return t; 203 } 204 205 206 207 /** 208 * Get the encoded public key including domain parameters 209 * 210 * @private 211 */ 212 EAC2CVRequestGenerator.prototype.getPublicKey = function() { 213 214 var t = new ASN1("Public Key", 0x7F49); 215 t.add(new ASN1("Object Identifier", 0x06, this.taOID)); 216 217 if (typeof(this.publicKey.getComponent(Key.ECC_P)) != "undefined") { 218 t.add(new ASN1("Prime Modulus", 0x81, EAC2CVRequestGenerator.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_P)))); 219 t.add(new ASN1("First coefficient a", 0x82, EAC2CVRequestGenerator.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_A)))); 220 t.add(new ASN1("Second coefficient b", 0x83, EAC2CVRequestGenerator.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_B)))); 221 t.add(new ASN1("Base Point G", 0x84, EAC2CVRequestGenerator.encodeUncompressedECPoint(this.publicKey.getComponent(Key.ECC_GX), this.publicKey.getComponent(Key.ECC_GY)))); 222 t.add(new ASN1("Order of the base point", 0x85, EAC2CVRequestGenerator.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_N)))); 223 224 t.add(new ASN1("Public Point y", 0x86, EAC2CVRequestGenerator.encodeUncompressedECPoint(this.publicKey.getComponent(Key.ECC_QX), this.publicKey.getComponent(Key.ECC_QY)))); 225 226 t.add(new ASN1("Cofactor f", 0x87, EAC2CVRequestGenerator.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_H)))); 227 } else { 228 t.add(new ASN1("Composite Modulus", 0x81, EAC2CVRequestGenerator.stripLeadingZeros(this.publicKey.getComponent(Key.MODULUS)))); 229 t.add(new ASN1("Public Exponent", 0x82, EAC2CVRequestGenerator.stripLeadingZeros(this.publicKey.getComponent(Key.EXPONENT)))); 230 } 231 232 return t; 233 } 234 235 236 237 /** 238 * Get the encoded CPI as ByteString 239 * 240 * @private 241 */ 242 EAC2CVRequestGenerator.prototype.getProfileIdentifier = function() { 243 var bb = new ByteBuffer(); 244 bb.append(this.profileIdentifier); 245 246 var t = new ASN1("Certificate Profile Identifier", 0x5F29, bb.toByteString()); 247 return t; 248 } 249 250 251 252 /** 253 * Get the DER-encoded extension vector 254 * 255 * @private 256 */ 257 EAC2CVRequestGenerator.prototype.getExtensions = function() { 258 var t = new ASN1("Certificate Extensions", 0x65); 259 for (var i = 0; i < this.extensions.length; i++) 260 t.add(this.extensions[i]); 261 return t; 262 } 263 264 265 266 /** 267 * Get the encoded certificate request body 268 * 269 * @private 270 */ 271 EAC2CVRequestGenerator.prototype.getCertificateBody = function() { 272 273 var t = new ASN1("Certificate Body", 0x7F4E); 274 t.add(this.getProfileIdentifier()); 275 276 if (this.CAR) { 277 t.add(this.getCAR()); 278 } 279 280 t.add(this.getPublicKey()); 281 t.add(this.getCHR()); 282 283 if (this.extensions) { 284 t.add(this.getExtensions()); 285 } 286 return t; 287 } 288 289 290 291 /** 292 * Generate initial certificate request using the specified private key for signing 293 * 294 * @param {Key} privateKey Private key for signature creation 295 * @return The DER-encoded CV request 296 * @type ASN1 297 */ 298 EAC2CVRequestGenerator.prototype.generateCVRequest = function(privateKey) { 299 var request = new ASN1("CV Certificate", 0x7F21); 300 301 var body = this.getCertificateBody(); 302 303 request.add(body); 304 305 var mech = CVC.getSignatureMech(this.taOID); 306 var signature = this.crypto.sign(privateKey, mech, body.getBytes()); 307 if (CVC.isECDSA(this.taOID)) { 308 var keylen = privateKey.getSize() >> 3; 309 var signatureValue = new ASN1("Signature", 0x5F37, CVC.unwrapSignature(signature, keylen)); 310 } else { 311 var signatureValue = new ASN1("Signature", 0x5F37, signature); 312 } 313 314 request.add(signatureValue); 315 316 return request; 317 } 318 319 320 321 /** 322 * Countersign request 323 * 324 * @param {Crypto} crypto the crypto provide to use for signing the request 325 * @param {CVC} request the self-signed request 326 * @param {Key} authenticationKey Private key for used for signing and authenticating the request 327 * @param {PublicKeyReference} authCHR CHR of the authenticating authority 328 * @param {ByteString} taOID the public key object identifier of the authentication key 329 * 330 * @return The DER-encoded authenticated CV request 331 * @type ASN1 332 */ 333 EAC2CVRequestGenerator.signAuthenticatedCVRequest = function(crypto, request, authenticationKey, authCHR, outertaOID) { 334 var authRequest = new ASN1("Authentication", 0x67); 335 336 var chr = new ASN1("Certification Authority Reference", 0x42, authCHR.getBytes()); 337 338 var signatureInput = request.getBytes().concat(chr.getBytes()); 339 340 var mech = CVC.getSignatureMech(outertaOID); 341 var signature = crypto.sign(authenticationKey, mech, signatureInput); 342 343 if (CVC.isECDSA(outertaOID)) { 344 var keylen = authenticationKey.getSize() >> 3; 345 var signatureValue = new ASN1("Signature", 0x5F37, CVC.unwrapSignature(signature, keylen)); 346 } else { 347 var signatureValue = new ASN1("Signature", 0x5F37, signature); 348 } 349 350 authRequest.add(request); 351 authRequest.add(chr); 352 authRequest.add(signatureValue); 353 354 return authRequest; 355 } 356 357 358 359 /** 360 * Generate authenticated request 361 * 362 * @param {Key} requestKey Private key for the request signature 363 * @param {Key} authenticationKey Private key for used for signing and authenticating the request 364 * @param {PublicKeyReference} authCHR CHR of the authenticating authority 365 * @param {ByteString} taOID the public key object identifier of the authentication key 366 * 367 * @return The DER-encoded authenticated CV request 368 * @type ASN1 369 */ 370 EAC2CVRequestGenerator.prototype.generateAuthenticatedCVRequest = function(requestKey, authenticationKey, authCHR, outertaOID) { 371 372 var request = this.generateCVRequest(requestKey); 373 374 if (typeof(outertaOID) == "undefined") { 375 outertaOID = this.taOID; 376 } 377 return EAC2CVRequestGenerator.signAuthenticatedCVRequest(this.crypto, request, authenticationKey, authCHR, outertaOID); 378 } 379 380 381 382 EAC2CVRequestGenerator.test = function() { 383 var test = function(crypto, priKey, pubKey, taOID) { 384 var reqGenerator = new EAC2CVRequestGenerator(crypto); 385 386 // Set CPI 387 reqGenerator.setProfileIdentifier(0x00); 388 389 // Set "inner" CAR 390 var CAR = "decvca00000"; 391 reqGenerator.setCAR(CAR); 392 393 // Set public key for request 394 reqGenerator.setPublicKey(pubKey); 395 396 // Set oid of algorithm 397 reqGenerator.setTAAlgorithmIdentifier(taOID); 398 399 // Set some dummy extensions 400 var ext1 = new ASN1("ext1", new ByteString("06022A11", HEX)); 401 var ext2 = new ASN1("ext2", new ByteString("06022A12", HEX)); 402 reqGenerator.setExtensions([ext1, ext2]); 403 404 // Set CHR for the request 405 var CHR = "dedvca00001"; 406 reqGenerator.setCHR(CHR); 407 408 // Generate the request 409 var req = reqGenerator.generateAuthenticatedCVRequest(priKey, priKey, new PublicKeyReference("dedvca00000"), taOID); 410 GPSystem.trace(req); 411 412 var cvreq = new CVC(req); 413 GPSystem.trace(cvreq); 414 415 assert(cvreq.verifyWith(crypto, pubKey, taOID)); 416 assert(cvreq.verifyATWith(crypto, pubKey, taOID)); 417 } 418 419 var crypto = new Crypto(); 420 421 var priKey = new Key(); 422 var pubKey = new Key(); 423 priKey.setType(Key.PRIVATE); 424 pubKey.setType(Key.PUBLIC); 425 priKey.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256t1", OID)); 426 pubKey.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256t1", OID)); 427 crypto.generateKeyPair(Crypto.EC, pubKey, priKey); 428 429 test(crypto, priKey, pubKey, new ByteString("id-TA-ECDSA-SHA-256", OID)); 430 431 432 var priKey = new Key(); 433 var pubKey = new Key(); 434 priKey.setType(Key.PRIVATE); 435 pubKey.setType(Key.PUBLIC); 436 priKey.setComponent(Key.ECC_CURVE_OID, new ByteString("secp521r1", OID)); 437 pubKey.setComponent(Key.ECC_CURVE_OID, new ByteString("secp521r1", OID)); 438 crypto.generateKeyPair(Crypto.EC, pubKey, priKey); 439 440 test(crypto, priKey, pubKey, new ByteString("id-TA-ECDSA-SHA-512", OID)); 441 442 443 var priKey = new Key(); 444 var pubKey = new Key(); 445 priKey.setType(Key.PRIVATE); 446 pubKey.setType(Key.PUBLIC); 447 pubKey.setSize(1024); 448 crypto.generateKeyPair(Crypto.RSA, pubKey, priKey); 449 450 test(crypto, priKey, pubKey, new ByteString("id-TA-RSA-v1-5-SHA-256", OID)); 451 } 452