      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() + 7) >> 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