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