1 /**
  2  *  ---------
  3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
  4  * |#       #|  
  5  * |#       #|  Copyright (c) 1999-2006 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 EAC2CVCertificateGenerator - Simple CV certificate generator class
 25  * based on "Advanced Security Mechanisms for Machine Readable Travel Documents", Version 2.0
 26  *
 27  *
 28  * TODO: For now we only support ECC crypto
 29  *
 30  */
 31 
 32 load("tools/eccutils.js");
 33 
 34 if (typeof(__ScriptingServer) == "undefined") {
 35 	load("cvc.js");
 36 }
 37 
 38 
 39 
 40 /**
 41  * Define a generator object for CV certificates
 42  * 
 43  * @class Class implementing a generator for CV certificates according to EAC 1.1 and EAC 2.0 specifications.
 44  * 
 45  * @constructor
 46  * @param {Crypto} crypto the crypto provider to be used
 47  */
 48 function EAC2CVCertificateGenerator(crypto) {
 49 	this.crypto = crypto;
 50 }
 51 
 52 
 53 
 54 /**
 55  * Convert x/y coordinates to uncompressed format
 56  *
 57  * @param {ByteString} x the x-coordinate of the point
 58  * @param {ByteString} y the y-coordinate of the point
 59  * @return the point in uncompressed format
 60  * @type ByteString
 61  */
 62 EAC2CVCertificateGenerator.encodeUncompressedECPoint = function(x,y) {
 63 
 64 	bb = new ByteBuffer();
 65 
 66 	// uncompressed encoding
 67 	bb.append(new ByteString("04", HEX));
 68 	bb.append(new ByteString(x, HEX));
 69 	bb.append(new ByteString(y, HEX));
 70 
 71 	return bb.toByteString();
 72 }
 73 
 74 
 75 
 76 /**
 77  * Decode x/y coordinates from uncompressed format
 78  *
 79  * @param {ByteString} uncompressedPoint the uncompressed point
 80  * @return the x-/y-coordinate of the point
 81  * @type ByteString
 82  */
 83 EAC2CVCertificateGenerator.decodeUncompressedECPoint = function(uncompressedPoint) {
 84 	
 85 	// Determine the size of the coordinates ignoring the indicator byte '04'
 86 	var length = uncompressedPoint.length - 1;
 87 
 88 	var sizeOfCoordinate = length / 2;
 89 
 90 	var xValue = uncompressedPoint.bytes(1, sizeOfCoordinate);
 91 	var yValue = uncompressedPoint.bytes(1 + sizeOfCoordinate, sizeOfCoordinate);
 92 
 93 	return { x:xValue, y:yValue };
 94 } 
 95 
 96 
 97 
 98 /**
 99  * Convert integer to fixed length string with leading zeros.
100  *
101  * @private
102  * @param {Number} value the value to convert to a string.
103  * @param {Number} digits the number of digits in output string. Must be <= 20.
104  * @return the 0-padded string
105  * @type String
106  */
107 EAC2CVCertificateGenerator.itos = function(value, digits) {
108 	if (digits > 20) {
109 		throw new Error("Digits must be <= 20");
110 	}
111 	var str = "" + value;
112 	str = "0000000000000000000".substr(19 - (digits - str.length)).concat(str);
113 	return str;
114 }
115 
116 
117 
118 /**
119  * Convert date to string with format YYMMDD.
120  *
121  * @param {Date} d the date object.
122  * @return the date/time string.
123  * @type String
124  */
125 EAC2CVCertificateGenerator.dtos = function(d) {
126 	var s = EAC2CVCertificateGenerator.itos(d.getFullYear() % 100, 2) +
127 			EAC2CVCertificateGenerator.itos(d.getMonth() + 1, 2) +
128 			EAC2CVCertificateGenerator.itos(d.getDate(), 2);
129 	return s;
130 }
131 
132 
133 
134 /**
135  * Set the profile identifier
136  *
137  * @param {Number} profileID the profile identifier
138  */
139 EAC2CVCertificateGenerator.prototype.setProfileIdentifier = function(profileID) {
140 	this.profileIdentifier = profileID;
141 }
142 
143 
144 
145 /**
146  * Set the certification authority reference
147  *
148  * @param {String} CAR the CAR value
149  * @param {ByteString} CAR the CAR value
150  * @param {PublicKeyReference} CAR the CAR value
151  */
152 EAC2CVCertificateGenerator.prototype.setCAR = function(CAR) {
153 	if (CAR instanceof ByteString) {
154 		this.CAR = CAR;
155 	} else if (CAR instanceof PublicKeyReference) {
156 		this.CAR = CAR.getBytes();
157 	} else {
158 		this.CAR = new ByteString(CAR.toString(), ASCII);
159 	}
160 }
161 
162 
163 
164 /**
165  * Set the certificate holder reference
166  *
167  * @param {String} CHR the CHR value
168  * @param {ByteString} CHR the CHR value
169  * @param {PublicKeyReference} CHR the CHR value
170  */
171 EAC2CVCertificateGenerator.prototype.setCHR = function(CHR) {
172 	if (CHR instanceof ByteString) {
173 		this.CHR = CHR;
174 	} else if (CHR instanceof PublicKeyReference) {
175 		this.CHR = CHR.getBytes();
176 	} else {
177 		this.CHR = new ByteString(CHR.toString(), ASCII);
178 	}
179 }
180 
181 
182 
183 /**
184  * Set the effective date
185  *
186  * @param {String} effectiveDate the effective date in the format YYMMDD
187  * @param {Date} effectiveDate the effective date as Date object
188  */
189 EAC2CVCertificateGenerator.prototype.setEffectiveDate = function(effectiveDate) {
190 	if (effectiveDate instanceof Date) {
191 		this.effectiveDate = EAC2CVCertificateGenerator.dtos(effectiveDate);
192 	} else {
193 		this.effectiveDate = effectiveDate;
194 	}
195 }
196 
197 
198 
199 /**
200  * Set the expiry date
201  *
202  * @param {String} expiryDate the expiry date in the format YYMMDD
203  * @param {Date} expiryDate the expiry date as Date object
204  */
205 EAC2CVCertificateGenerator.prototype.setExpiryDate = function(expiryDate) {
206 	if (expiryDate instanceof Date) {
207 		this.expiryDate = EAC2CVCertificateGenerator.dtos(expiryDate);
208 	} else {
209 		this.expiryDate = expiryDate;
210 	}
211 }
212 
213 
214 
215 /**
216  * Set the object identifier of the authorization template for the generated certificate
217  *
218  * @param {ByteString} oid the object identifier for the chat
219  */
220 EAC2CVCertificateGenerator.prototype.setChatOID = function(oid) {
221 	this.chatOID = oid;
222 }
223 
224 
225 
226 /**
227  * Set the authorization level of the authorization template for the generated certificate
228  *
229  * @param {ByteString} authLevel the encoded authorization level
230  */
231 EAC2CVCertificateGenerator.prototype.setChatAuthorizationLevel = function(authLevel) {
232 	this.chatAuthorizationLevel = authLevel;
233 }
234 
235 
236 
237 /**
238  * Set the algorithm identifier for terminal authentication
239  *
240  * @param {ByteString} oid the object identifier as specified in appendix A.6.4
241  */
242 EAC2CVCertificateGenerator.prototype.setTAAlgorithmIdentifier = function(oid) {
243 	this.taOID = oid;
244 }
245 
246 
247 
248 /**
249  * Set some additional extensions 
250  *
251  * @param {Array of ASN.1 objects} extensions array containing the ASN.1 encoded extensions for the certificate
252  */
253 EAC2CVCertificateGenerator.prototype.setExtensions = function(extensions) {
254 	this.extensions = extensions;
255 }
256 
257 
258 
259 /**
260  * Set the public key to be included in the certificate
261  *
262  * @param {Key} publicKey the public key object to be certified
263  */
264 EAC2CVCertificateGenerator.prototype.setPublicKey = function(publicKey) {
265 	this.publicKey = publicKey;
266 }
267 
268 
269 
270 /**
271  * Set whether to include domain parameters in the certificate or not
272  *
273  * @param {Boolean} value the flag indicator
274  */
275 EAC2CVCertificateGenerator.prototype.setIncludeDomainParameters = function(value) {
276 	this.includeDomainParameters = value;
277 }
278 
279 
280 
281 /**
282  * Internal functions for the generation of a certificate
283  * @private
284  */
285 EAC2CVCertificateGenerator.prototype.getCAR = function() {
286 	var t = new ASN1("Certification Authority Reference", 0x42, this.CAR);
287 	return t;
288 }
289 
290 
291 
292 /**
293  * Internal functions for the generation of a certificate
294  * @private
295  */
296 EAC2CVCertificateGenerator.prototype.getCHR = function() {
297 	var t = new ASN1("Certification Holder Reference", 0x5F20, this.CHR);
298 	return t;
299 }
300 
301 
302 
303 /**
304  * Internal functions for the generation of a certificate
305  * @private
306  */
307 EAC2CVCertificateGenerator.convertDate = function(date) {
308 
309 	var temp = new ByteString(date, ASCII);
310 	var bb = new ByteBuffer();
311 	var singleByte;
312 	
313 	for (var i = 0; i < temp.length; i++) {
314 		bb.append(temp.byteAt(i) - 0x30);
315 	}
316 	
317 	return bb.toByteString();
318 }
319 
320 
321 
322 /**
323  * Internal functions for the generation of a certificate
324  * @private
325  */
326 EAC2CVCertificateGenerator.prototype.getEffectiveDate = function() {
327 	var t = new ASN1("Certificate Effective Date", 0x5F25, 
328 			EAC2CVCertificateGenerator.convertDate(this.effectiveDate));
329 	return t;
330 }
331 
332 
333 
334 /**
335  * Internal functions for the generation of a certificate
336  * @private
337  */
338 EAC2CVCertificateGenerator.prototype.getExpiryDate = function() {
339 	var t = new ASN1("Certificate Expiration Date", 0x5F24, 
340 			EAC2CVCertificateGenerator.convertDate(this.expiryDate));
341 	return t;
342 }
343 
344 
345 
346 /**
347  * Internal functions for the generation of a certificate
348  * @private
349  */
350 EAC2CVCertificateGenerator.prototype.getCHAT = function() {
351 	var t = new ASN1("Certificate Holder Authorization Template", 0x7F4C);
352 
353 	var oid = new ASN1("Object Identifier", ASN1.OBJECT_IDENTIFIER, this.chatOID);
354 	var authLevel = new ASN1("Authorization Level", 0x53, this.chatAuthorizationLevel);
355 
356 	t.add(oid);
357 	t.add(authLevel);
358 
359 	return t;
360 }
361 
362 
363 
364 /**
365  * Strips leading zeros of a ByteString
366  *
367  * @param {ByteString} value the ByteString value
368  * @return the stripped ByteString object, may be an empty ByteString
369  * @type ByteString
370  */
371 EAC2CVCertificateGenerator.prototype.stripLeadingZeros = function(value) {
372 	var i = 0;
373 	for (; (i < value.length) && (value.byteAt(i) == 0); i++);
374 	
375 	return value.right(value.length - i);
376 }
377 
378 
379 
380 /**
381  * Get the encoded public key including domain parameters
382  *
383  * @private
384  */
385 EAC2CVCertificateGenerator.prototype.getPublicKey = function() {
386 
387 	var t = new ASN1("Public Key", 0x7F49);
388 	t.add(new ASN1("Object Identifier", 0x06, this.taOID));
389 
390 	if (typeof(this.publicKey.getComponent(Key.ECC_P)) != "undefined") {
391 		if (this.includeDomainParameters == true) {
392 
393 			t.add(new ASN1("Prime Modulus", 0x81, this.publicKey.getComponent(Key.ECC_P)));
394 			t.add(new ASN1("First coefficient a", 0x82, this.publicKey.getComponent(Key.ECC_A)));
395 			t.add(new ASN1("Second coefficient b", 0x83, this.publicKey.getComponent(Key.ECC_B)));
396 
397 			t.add(new ASN1("Base Point G", 0x84, EAC2CVCertificateGenerator.encodeUncompressedECPoint(this.publicKey.getComponent(Key.ECC_GX), this.publicKey.getComponent(Key.ECC_GY))));
398 
399 			t.add(new ASN1("Order of the base point", 0x85, this.publicKey.getComponent(Key.ECC_N)));
400 		}
401 
402 		t.add(new ASN1("Public Point y", 0x86, EAC2CVCertificateGenerator.encodeUncompressedECPoint(this.publicKey.getComponent(Key.ECC_QX), this.publicKey.getComponent(Key.ECC_QY))));
403 
404 		if (this.includeDomainParameters == true) {
405 			t.add(new ASN1("Cofactor f", 0x87, this.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_H))));
406 		}
407 	} else {
408 		t.add(new ASN1("Composite Modulus", 0x81, this.publicKey.getComponent(Key.MODULUS)));
409 		t.add(new ASN1("Public Exponent", 0x82, this.publicKey.getComponent(Key.EXPONENT)));
410 	}
411 
412 	return t;
413 }
414 
415 
416 
417 /**
418  * Internal functions for the generation of a certificate
419  * @private
420  */
421 EAC2CVCertificateGenerator.prototype.getProfileIdentifier = function() {
422 
423 	var bb = new ByteBuffer();
424 	bb.append(this.profileIdentifier);
425 	
426 	var t = new ASN1("Certificate Profile Identifier", 0x5F29, bb.toByteString());
427 	return t;
428 }
429 
430 
431 
432 /**
433  * Internal functions for the generation of a certificate
434  * @private
435  */
436 EAC2CVCertificateGenerator.prototype.getExtensions = function() {
437 	var t = new ASN1("Certificate Extensions", 0x65);
438 	for (var i = 0; i < this.extensions.length; i++)
439 		t.add(this.extensions[i]);
440 	return t;
441 }
442 
443 
444 
445 /**
446  * Internal functions for the generation of a certificate
447  * @private
448  */
449 EAC2CVCertificateGenerator.prototype.getCertificateBody = function() {
450 	
451 	var t = new ASN1("Certificate Body", 0x7F4E);
452 
453 	t.add(this.getProfileIdentifier());
454 
455 	t.add(this.getCAR());
456 
457 	t.add(this.getPublicKey());
458 
459 	t.add(this.getCHR());
460 
461 	t.add(this.getCHAT());
462 
463 	t.add(this.getEffectiveDate());
464 
465 	t.add(this.getExpiryDate());
466 
467 	if (this.extensions) {
468 		t.add(this.getExtensions());
469 	}
470 	
471 	return t;
472 }
473 
474 
475 
476 /**
477  * Generate a certificate based on the parameter set using the setter methods.
478  *
479  * @param {Key} signingKey the key to be used for signing the certificate
480  * @param {ByteString} taOID the object identifier associated with the signing key
481  * @return the CVC certificate
482  * @type CVC
483  */
484 EAC2CVCertificateGenerator.prototype.generateCVCertificate = function(signingKey, outertaOID) {
485 	
486 	var certificate = new ASN1("CV Certificate", 0x7F21);
487 	
488 	var body = this.getCertificateBody();
489 	
490 	if (typeof(outertaOID) == "undefined") {
491 		outertaOID = this.taOID;
492 	}
493 	var mech = CVC.getSignatureMech(outertaOID);
494 	var signature = this.crypto.sign(signingKey, mech, body.getBytes());
495 
496 	if (CVC.isECDSA(outertaOID)) {
497 		var keylen = signingKey.getSize() >> 3;
498 		var signatureValue = new ASN1("Signature", 0x5F37, ECCUtils.unwrapSignature(signature, keylen));
499 	} else {
500 		var signatureValue = new ASN1("Signature", 0x5F37, signature);
501 	}
502 	
503 	certificate.add(body);
504 
505 	certificate.add(signatureValue);
506 	
507 	return new CVC(certificate);
508 }
509