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 load("tools/eccutils.js"); 30 31 if (typeof(__ScriptingServer) == "undefined") { 32 load("cvc.js"); 33 } 34 35 36 /** 37 * Constructor for request generator 38 * 39 * @class Class implementing a generator for CVC requests 40 * 41 * @constructor 42 * @param {Crypto} Crypto object to use 43 */ 44 function EAC2CVRequestGenerator(crypto) { 45 this.crypto = crypto; 46 } 47 48 49 50 /* 51 * Convert x/y coordinates to uncompressed format 52 * 53 * x/y - coordinates of EC point 54 * 55 * return ByteString containing compressed format 56 * 57 * TODO: Move to ECUtils 58 */ 59 EAC2CVRequestGenerator.encodeUncompressedECPoint = function(x, y) { 60 61 bb = new ByteBuffer(); 62 63 // uncompressed encoding 64 bb.append(new ByteString("04", HEX)); 65 bb.append(new ByteString(x, HEX)); 66 bb.append(new ByteString(y, HEX)); 67 68 return bb.toByteString(); 69 } 70 71 72 73 /** 74 * Strips leading zeros of a ByteString 75 * 76 * @param {ByteString} value the ByteString value 77 * @return the stripped ByteString object, may be an empty ByteString 78 * @type ByteString 79 * 80 * TODO: Move to Utils 81 */ 82 EAC2CVRequestGenerator.stripLeadingZeros = function(value) { 83 var i = 0; 84 for (; (i < value.length) && (value.byteAt(i) == 0); i++); 85 86 return value.right(value.length - i); 87 } 88 89 90 91 /** 92 * Set the public key that should be encoded within the request 93 * 94 * @param {Key} publicKey Public Key 95 */ 96 EAC2CVRequestGenerator.prototype.setPublicKey = function(publicKey) { 97 this.publicKey = publicKey; 98 } 99 100 101 102 /** 103 * Set the certficate holder reference (CHR) for the request 104 * 105 * @param {String} chr CHR for the request 106 */ 107 EAC2CVRequestGenerator.prototype.setCHR = function(chr) { 108 if (chr instanceof ByteString) { 109 this.CHR = chr; 110 } else if (chr instanceof PublicKeyReference) { 111 this.CHR = chr.getBytes(); 112 } else { 113 this.CHR = new ByteString(chr.toString(), ASCII); 114 } 115 } 116 117 118 119 /** 120 * Reset the current generator object 121 * 122 * TODO: Implement me 123 */ 124 EAC2CVRequestGenerator.prototype.reset = function() { 125 } 126 127 128 129 /** 130 * Set the certificate profile identifier (CPI) for the request 131 * 132 * @param {Number} profileID CPI for the request 133 */ 134 EAC2CVRequestGenerator.prototype.setProfileIdentifier = function(profileID) { 135 this.profileIdentifier = profileID; 136 } 137 138 139 140 /** 141 * Set the certficate authorization reference (CAR) for the request 142 * 143 * The usage of this method is optional - if no CAR is set, there will be no 144 * "inner" CAR included within the certficate request 145 * 146 * @param {String} car CAR for the request 147 */ 148 EAC2CVRequestGenerator.prototype.setCAR = function(car) { 149 if (car instanceof ByteString) { 150 this.CAR = car; 151 } else if (car instanceof PublicKeyReference) { 152 this.CAR = car.getBytes(); 153 } else { 154 this.CAR = new ByteString(car.toString(), ASCII); 155 } 156 } 157 158 159 160 /** 161 * Set the extension values that should be included within the request 162 * 163 * @param {ByteString[]} extensions Array of DER-encoded extensions 164 */ 165 EAC2CVRequestGenerator.prototype.setExtensions = function(extensions) { 166 this.extensions = extensions; 167 } 168 169 170 171 /** 172 * Set the object identifier that should be included in the public key domain parameters 173 * 174 * @param {ByteString} oid Object identifier as specified in appendix A.6.4 175 */ 176 EAC2CVRequestGenerator.prototype.setTAAlgorithmIdentifier = function(oid) { 177 this.taOID = oid; 178 } 179 180 181 182 /** 183 * Get the CAR as ByteString object 184 * 185 * @private 186 */ 187 EAC2CVRequestGenerator.prototype.getCAR = function() { 188 var t = new ASN1("Certification Authority Reference", 0x42, this.CAR); 189 return t; 190 } 191 192 193 194 /** 195 * Get the CHR as ByteString object 196 * 197 * @private 198 */ 199 EAC2CVRequestGenerator.prototype.getCHR = function() { 200 var t = new ASN1("Certification Holder Reference", 0x5F20, this.CHR); 201 return t; 202 } 203 204 205 206 /** 207 * Get the encoded public key including domain parameters 208 * 209 * @private 210 */ 211 EAC2CVRequestGenerator.prototype.getPublicKey = function() { 212 213 var t = new ASN1("Public Key", 0x7F49); 214 t.add(new ASN1("Object Identifier", 0x06, this.taOID)); 215 216 if (typeof(this.publicKey.getComponent(Key.ECC_P)) != "undefined") { 217 t.add(new ASN1("Prime Modulus", 0x81, this.publicKey.getComponent(Key.ECC_P))); 218 t.add(new ASN1("First coefficient a", 0x82, this.publicKey.getComponent(Key.ECC_A))); 219 t.add(new ASN1("Second coefficient b", 0x83, this.publicKey.getComponent(Key.ECC_B))); 220 t.add(new ASN1("Base Point G", 0x84, EAC2CVRequestGenerator.encodeUncompressedECPoint(this.publicKey.getComponent(Key.ECC_GX), this.publicKey.getComponent(Key.ECC_GY)))); 221 t.add(new ASN1("Order of the base point", 0x85, this.publicKey.getComponent(Key.ECC_N))); 222 223 t.add(new ASN1("Public Point y", 0x86, EAC2CVRequestGenerator.encodeUncompressedECPoint(this.publicKey.getComponent(Key.ECC_QX), this.publicKey.getComponent(Key.ECC_QY)))); 224 225 t.add(new ASN1("Cofactor f", 0x87, EAC2CVRequestGenerator.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_H)))); 226 } else { 227 t.add(new ASN1("Composite Modulus", 0x81, this.publicKey.getComponent(Key.MODULUS))); 228 t.add(new ASN1("Public Exponent", 0x82, this.publicKey.getComponent(Key.EXPONENT))); 229 } 230 231 return t; 232 } 233 234 235 236 /** 237 * Get the encoded CPI as ByteString 238 * 239 * @private 240 */ 241 EAC2CVRequestGenerator.prototype.getProfileIdentifier = function() { 242 var bb = new ByteBuffer(); 243 bb.append(this.profileIdentifier); 244 245 var t = new ASN1("Certificate Profile Identifier", 0x5F29, bb.toByteString()); 246 return t; 247 } 248 249 250 251 /** 252 * Get the DER-encoded extension vector 253 * 254 * @private 255 */ 256 EAC2CVRequestGenerator.prototype.getExtensions = function() { 257 var t = new ASN1("Certificate Extensions", 0x7F49); 258 for (var i = 0; i < this.extensions.length; i++) 259 t.add(this.extensions[i]); 260 return t; 261 } 262 263 264 265 /** 266 * Get the encoded certificate request body 267 * 268 * @private 269 */ 270 EAC2CVRequestGenerator.prototype.getCertificateBody = function() { 271 272 var t = new ASN1("Certificate Body", 0x7F4E); 273 t.add(this.getProfileIdentifier()); 274 275 if (this.CAR) { 276 t.add(this.getCAR()); 277 } 278 279 t.add(this.getPublicKey()); 280 t.add(this.getCHR()); 281 282 if (this.extensions) { 283 t.add(this.getExtensions()); 284 } 285 return t; 286 } 287 288 289 290 /** 291 * Generate initial certificate request using the specified private key for signing 292 * 293 * @param {Key} privateKey Private key for signature creation 294 * @return The DER-encoded CV request 295 * @type ASN1 296 */ 297 EAC2CVRequestGenerator.prototype.generateCVRequest = function(privateKey) { 298 var request = new ASN1("CV Certificate", 0x7F21); 299 300 var body = this.getCertificateBody(); 301 302 request.add(body); 303 304 var mech = CVC.getSignatureMech(this.taOID); 305 var signature = this.crypto.sign(privateKey, mech, body.getBytes()); 306 if (CVC.isECDSA(this.taOID)) { 307 var keylen = privateKey.getSize() >> 3; 308 var signatureValue = new ASN1("Signature", 0x5F37, ECCUtils.unwrapSignature(signature, keylen)); 309 } else { 310 var signatureValue = new ASN1("Signature", 0x5F37, signature); 311 } 312 313 request.add(signatureValue); 314 315 return request; 316 } 317 318 319 320 /** 321 * Countersign request 322 * 323 * @param {Crypto} crypto the crypto provide to use for signing the request 324 * @param {CVC} request the self-signed request 325 * @param {Key} authenticationKey Private key for used for signing and authenticating the request 326 * @param {PublicKeyReference} authCHR CHR of the authenticating authority 327 * @param {ByteString} taOID the public key object identifier of the authentication key 328 * 329 * @return The DER-encoded authenticated CV request 330 * @type ASN1 331 */ 332 EAC2CVRequestGenerator.signAuthenticatedCVRequest = function(crypto, request, authenticationKey, authCHR, outertaOID) { 333 var authRequest = new ASN1("Authentication", 0x67); 334 335 var chr = new ASN1("Certification Authority Reference", 0x42, authCHR.getBytes()); 336 337 var signatureInput = request.getBytes().concat(chr.getBytes()); 338 339 var mech = CVC.getSignatureMech(outertaOID); 340 var signature = crypto.sign(authenticationKey, mech, signatureInput); 341 342 if (CVC.isECDSA(outertaOID)) { 343 var keylen = authenticationKey.getSize() >> 3; 344 var signatureValue = new ASN1("Signature", 0x5F37, ECCUtils.unwrapSignature(signature, keylen)); 345 } else { 346 var signatureValue = new ASN1("Signature", 0x5F37, signature); 347 } 348 349 authRequest.add(request); 350 authRequest.add(chr); 351 authRequest.add(signatureValue); 352 353 return authRequest; 354 } 355 356 357 358 /** 359 * Generate authenticated request 360 * 361 * @param {Key} requestKey Private key for the request signature 362 * @param {Key} authenticationKey Private key for used for signing and authenticating the request 363 * @param {PublicKeyReference} authCHR CHR of the authenticating authority 364 * @param {ByteString} taOID the public key object identifier of the authentication key 365 * 366 * @return The DER-encoded authenticated CV request 367 * @type ASN1 368 */ 369 EAC2CVRequestGenerator.prototype.generateAuthenticatedCVRequest = function(requestKey, authenticationKey, authCHR, outertaOID) { 370 371 var request = this.generateCVRequest(requestKey); 372 373 if (typeof(outertaOID) == "undefined") { 374 outertaOID = this.taOID; 375 } 376 return EAC2CVRequestGenerator.signAuthenticatedCVRequest(this.crypto, request, authenticationKey, authCHR, outertaOID); 377 } 378