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