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