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