      1 /**
      2  *  ---------
      3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
      4  * |#       #|
      5  * |#       #|  Copyright (c) 1999-2009 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 A card verifiable certificate store using a PKCS#11 device as key store
     25  */
     26
     27 // Imports
     28
     29 var PublicKeyReference	 = require('scsh/eac/PublicKeyReference').PublicKeyReference;
     30 var CVC			 = require('scsh/eac/CVC').CVC;
     31 var CVCertificateStore	 = require('scsh/eac/CVCertificateStore').CVCertificateStore;
     32 var CVCCA		 = require('scsh/eac/CVCCA').CVCCA;
     33
     34
     35
     36 /**
     37  * Create a CV certificate store using a PKCS#11 device as secure key store
     38  *
     39  * @class CV certificate store with PKCS#11 as secure key store
     40  * @constructor
     41  * @param {DAOFactory} daof the factory that can create data access objects for persistent information
     42  * @param {PKCS11Session} p11session logged in PKCS#11 session with device
     43  */
     44 function P11CVCertificateStore(daof, p11session) {
     45 	CVCertificateStore.call(this, daof);
     46 	this.p11session = p11session;
     47 	this.crypto = new P11Crypto(p11session);
     48 }
     49
     50 P11CVCertificateStore.prototype = Object.create(CVCertificateStore.prototype);
     51 P11CVCertificateStore.constructor = P11CVCertificateStore;
     52
     53 exports.P11CVCertificateStore = P11CVCertificateStore;
     54
     55
     56
     57 /**
     58  * Get crypto object
     59  *
     60  * @type HSMCrypto
     61  * @return the HSMCrypto object
     62  */
     63 P11CVCertificateStore.prototype.getCrypto = function() {
     64 	return this.crypto;
     65 }
     66
     67
     68
     69 /**
     70  * Transform path and certificate holder into a label
     71  *
     72  * @param {String} path the path
     73  * @param {PublicKeyReference} chr the certificate holder reference
     74  * @type String
     75  * @return the key label
     76  */
     77 P11CVCertificateStore.path2label = function(path, chr) {
     78 	return path.substr(1) + chr.getSequenceNo();
     79 }
     80
     81
     82
     83 /**
     84  * Get a private key in the certificate store. Overrides method in CVCertificateStore.
     85  *
     86  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
     87  * @param {PublicKeyReference} chr the public key reference for this key
     88  * @returns the private key or null if not found
     89  * @type Key
     90  */
     91 P11CVCertificateStore.prototype.getPrivateKey = function(path, car) {
     92 	var label = P11CVCertificateStore.path2label(path, car);
     93
     94 	var attr = new Array();
     95 	attr[PKCS11Object.CKA_CLASS] = PKCS11Object.CKO_PRIVATE_KEY;
     96 	attr[PKCS11Object.CKA_LABEL] = new ByteString(label, ASCII);
     97 	var o = this.p11session.enumerateObjects(attr);
     98
     99 	print(o.length);
    100 	for (var i = 0; i < o.length; i++) {
    101 		print(o[i].getAttribute(PKCS11Object.CKA_LABEL));
    102 	}
    103 	if (o.length == 0) {
    104 		return null;
    105 	}
    106
    107 	if (o.length != 1) {
    108 		throw new GPError("P11CVCertificateStore", GPError.INVALID_ARGUMENT, o.length, "Duplicate key with label " + label + " found");
    109 	}
    110
    111 	var p11key = o[0];
    112
    113 	var key = new Key();
    114 	key.setType(Key.PRIVATE);
    115
    116 	var curve = p11key.getAttribute(PKCS11Object.CKA_EC_PARAMS);
    117 	if (curve) {
    118 		var a = new ASN1(curve);
    119
    120 		key.setComponent(Key.ECC_CURVE_OID, a.value);
    121 	}
    122 	key.p11 = p11key;
    123 	return key;
    124 }
    125
    126
    127
    128 /**
    129  * Determine curve from key parameter
    130  *
    131  * @param {Key} key the key
    132  * @type ByteString
    133  * @return the curve OID
    134  */
    135 P11CVCertificateStore.determineCurve = function(key) {
    136 	var curves = [
    137 		new ByteString("brainpoolP192r1", OID),
    138 		new ByteString("brainpoolP224r1", OID),
    139 		new ByteString("brainpoolP256r1", OID),
    140 		new ByteString("brainpoolP384r1", OID),
    141 		new ByteString("brainpoolP512r1", OID)
    142 	];
    143
    144 	if (key.getComponent(Key.ECC_CURVE_OID)) {
    145 		return key.getComponent(Key.ECC_CURVE_OID);
    146 	}
    147
    148 	for (var i = 0; i < curves.length; i++) {
    149 		var spec = new Key();
    150 		spec.setComponent(Key.ECC_CURVE_OID, curves[i]);
    151 		if (key.getComponent(Key.ECC_P).equals(spec.getComponent(Key.ECC_P))) {
    152 			return curves[i];
    153 		}
    154 	}
    155 	throw new GPError("P11CVCertificateStore", GPError.INVALID_ARGUMENT, 0, "Unknown curve");
    156 }
    157
    158
    159
    160 /**
    161  * Generate key pair
    162  *
    163  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    164  * @param {PublicKeyReference} chr the public key reference for this key pair
    165  * @param {Number} algo the key generation algorithm (Crypto.EC or Crypto.RSA)
    166  * @param {Key} prk the private key template
    167  * @param {Key} puk the public key template
    168  */
    169 P11CVCertificateStore.prototype.generateKeyPair = function(path, chr, algo, prk, puk) {
    170 	var label = P11CVCertificateStore.path2label(path, chr);
    171
    172 	label = new ByteString(label, ASCII);
    173
    174 	var priAttr = new Array();
    175 	priAttr[PKCS11Object.CKA_TOKEN] = true;
    176 	priAttr[PKCS11Object.CKA_SIGN] = true;
    177 	priAttr[PKCS11Object.CKA_SENSITIVE] = true;
    178 	priAttr[PKCS11Object.CKA_PRIVATE] = true;
    179 	priAttr[PKCS11Object.CKA_LABEL] = label;
    180
    181 	var pubAttr = new Array();
    182 	pubAttr[PKCS11Object.CKA_TOKEN] = true;
    183 	pubAttr[PKCS11Object.CKA_VERIFY] = true;
    184 	pubAttr[PKCS11Object.CKA_LABEL] = label;
    185
    186 	if (algo == Crypto.RSA) {
    187 		pubAttr[PKCS11Object.CKA_MODULUS_BITS] = puk.getSize();
    188 		pubAttr[PKCS11Object.CKA_PUBLIC_EXPONENT] = new ByteString("010001", HEX);
    189 		var keys = this.p11session.generateKeyPair(PKCS11Session.CKM_RSA_PKCS_KEY_PAIR_GEN, null, pubAttr, priAttr);
    190
    191 		var value = keys[0].getAttribute(PKCS11Object.CKA_VALUE);
    192 		var pk = new ASN1(value);
    193 		print(pk);
    194 		puk.setComponent(Key.MODULUS, pk.get(0).value.right(puk.getSize() >> 3));
    195 		puk.setComponent(Key.EXPONENT, pk.get(1).value);
    196 	} else {
    197 		var curve = P11CVCertificateStore.determineCurve(puk);
    198 		var curveasn = new ASN1(ASN1.OBJECT_IDENTIFIER, curve);
    199
    200 		pubAttr[PKCS11Object.CKA_EC_PARAMS] = curveasn.getBytes();
    201 		var keys = this.p11session.generateKeyPair(PKCS11Session.CKM_EC_KEY_PAIR_GEN, null, pubAttr, priAttr);
    202
    203 		var value = keys[0].getAttribute(PKCS11Object.CKA_VALUE);
    204 		var pk = new ASN1(value);
    205 		var point = pk.value.bytes(1);
    206
    207 		prk.setComponent(Key.ECC_CURVE_OID, curve);
    208 		puk.setComponent(Key.ECC_QX, point.left(point.length >> 1));
    209 		puk.setComponent(Key.ECC_QY, point.right(point.length >> 1));
    210 	}
    211
    212 	puk.p11 = keys[0];
    213 	prk.p11 = keys[1];
    214 }
    215
    216
    217
    218 function P11Crypto(p11session) {
    219 	this.p11session = p11session;
    220 	this.crypto = new Crypto();
    221 }
    222
    223
    224
    225 P11Crypto.prototype.sign = function(key, mech, data, iv) {
    226 	var ckm_sign = 0;
    227 	var mech_hash = 0;
    228 	var wrap = false;
    229
    230 	switch(mech) {
    231 		case Crypto.ECDSA_SHA1:
    232 			ckm_sign = PKCS11Session.CKM_ECDSA;
    233 			mech_hash = Crypto.SHA_1;
    234 			wrap = true;
    235 			break;
    236 		case Crypto.ECDSA_SHA224:
    237 			ckm_sign = PKCS11Session.CKM_ECDSA;
    238 			mech_hash = Crypto.SHA_224;
    239 			wrap = true;
    240 			break;
    241 		case Crypto.ECDSA_SHA256:
    242 			ckm_sign = PKCS11Session.CKM_ECDSA;
    243 			mech_hash = Crypto.SHA_256;
    244 			wrap = true;
    245 			break;
    246 		case Crypto.ECDSA_SHA384:
    247 			ckm_sign = PKCS11Session.CKM_ECDSA;
    248 			mech_hash = Crypto.SHA_384;
    249 			wrap = true;
    250 			break;
    251 		case Crypto.ECDSA_SHA512:
    252 			ckm_sign = PKCS11Session.CKM_ECDSA;
    253 			mech_hash = Crypto.SHA_512;
    254 			wrap = true;
    255 			break;
    256 		case Crypto.ECDSA:
    257 			ckm_sign = PKCS11Session.CKM_ECDSA;
    258 			wrap = true;
    259 			break;
    260 		case Crypto.RSA_SHA1:
    261 			ckm_sign = PKCS11Session.CKM_SHA1_RSA_PKCS;
    262 			break;
    263 		case Crypto.RSA_SHA256:
    264 			ckm_sign = PKCS11Session.CKM_SHA256_RSA_PKCS;
    265 			break;
    266 		case Crypto.RSA_SHA512:
    267 			ckm_sign = PKCS11Session.CKM_SHA512_RSA_PKCS;
    268 			break;
    269 		case Crypto.RSA_PSS_SHA1:
    270 			ckm_sign = PKCS11Session.CKM_SHA1_RSA_PKCS_PSS;
    271 			break;
    272 		case Crypto.RSA_PSS_SHA256:
    273 			ckm_sign = PKCS11Session.CKM_SHA256_RSA_PKCS_PSS;
    274 			break;
    275 		case Crypto.RSA_PSS_SHA512:
    276 			ckm_sign = PKCS11Session.CKM_SHA512_RSA_PKCS_PSS;
    277 			break;
    278 		default:
    279 			throw new GPError("P11Crypto", GPError.INVALID_ARGUMENT, 2, "Unsupported mechanism");
    280 	}
    281
    282 	if (mech_hash) {
    283 		data = this.crypto.digest(mech_hash, data);
    284 	}
    285
    286 	// Test with single step C_Sign
    287 	this.p11session.signInit(ckm_sign, key.p11);
    288
    289 	var signature = this.p11session.sign(data);
    290
    291 	if (wrap) {
    292 		signature = CVC.wrapSignature(signature);
    293 	}
    294
    295 	return signature;
    296 }
    297
    298
    299
    300 P11Crypto.prototype.verify = function(key, mech, data, sig) {
    301 	return this.crypto.verify(key, mech, data, sig);
    302 }
    303
    304
    305
    306 P11CVCertificateStore.testPath = GPSystem.mapFilename("testca", GPSystem.CWD);
    307
    308 P11CVCertificateStore.test = function() {
    309 	var p = new PKCS11Provider("/usr/local/lib/opensc-pkcs11.so");
    310
    311 	try	{
    312 		var slots = p.getSlots();
    313 		print(slots);
    314 		var slot = slots[1];
    315
    316 		print("Using slot: " + slot.getId());
    317
    318 		// Open R/W session
    319 		var s = new PKCS11Session(p, slot.getId(), true);
    320
    321 		// Login with USER PIN
    322 		s.login("648219");
    323
    324 		var cs = new P11CVCertificateStore(P11CVCertificateStore.testPath + "/cvca", s);
    325
    326 		var crypto = cs.getCrypto();
    327
    328 		var prk = new Key();
    329 		prk.setType(Key.PRIVATE);
    330 		prk.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID));
    331 		var puk = new Key();
    332 		puk.setType(Key.PUBLIC);
    333 		puk.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID));
    334
    335 		var chr = new PublicKeyReference("DE00001");
    336 		cs.generateKeyPair("/test", chr, Crypto.ECC, prk, puk);
    337 		var msg = new ByteString("Hello World", ASCII);
    338
    339 		var sig = crypto.sign(prk, Crypto.ECDSA_SHA1, msg);
    340 		assert(crypto.verify(puk, Crypto.ECDSA_SHA1, msg, sig));
    341
    342 		var sig = crypto.sign(prk, Crypto.ECDSA_SHA224, msg);
    343 		assert(crypto.verify(puk, Crypto.ECDSA_SHA224, msg, sig));
    344
    345 		var sig = crypto.sign(prk, Crypto.ECDSA_SHA256, msg);
    346 		assert(crypto.verify(puk, Crypto.ECDSA_SHA256, msg, sig));
    347
    348 		var sig = crypto.sign(prk, Crypto.ECDSA_SHA384, msg);
    349 		assert(crypto.verify(puk, Crypto.ECDSA_SHA384, msg, sig));
    350
    351 		var sig = crypto.sign(prk, Crypto.ECDSA_SHA512, msg);
    352 		assert(crypto.verify(puk, Crypto.ECDSA_SHA512, msg, sig));
    353
    354
    355 		var cvca = new CVCCA(crypto, cs, null, null, "/UTCVCA");
    356
    357 		// Create a new request
    358 		var car = new PublicKeyReference("UTCVCA00000");
    359
    360 		var req = cvca.generateRequest(car, false);
    361 		print("Request: " + req);
    362 		print(req.getASN1());
    363
    364 		assert(req.verifyWith(crypto, req.getPublicKey()));
    365
    366 		// Create self-signed or link certificate based on request
    367 		var policy = { certificateValidityDays: 2,
    368 				   chatRoleOID: new ByteString("id-IS", OID),
    369 				   chatRights: new ByteString("E3", HEX),
    370 				   includeDomainParameter: true,
    371 				   extensions: []
    372 				 };
    373 		var cert = cvca.generateCertificate(req, policy);
    374 		print("Certificate: " + cert);
    375 		print(cert.getASN1());
    376
    377 		// Import certificate into store, making it the most current certificate
    378 		cvca.storeCertificate(cert);
    379
    380 		// Generate additional self-signed root certificate
    381 		// This must be done after the link certificate has been imported
    382 		var policy = { certificateValidityDays: 2,
    383 				   chatRoleOID: new ByteString("id-IS", OID),
    384 				   chatRights: new ByteString("E3", HEX),
    385 				   includeDomainParameter: true,
    386 				   extensions: []
    387 				 };
    388 		var cert = cvca.generateCertificate(req, policy);
    389 		print("Certificate: " + cert);
    390 		print(cert.getASN1());
    391
    392 		// Import certificate into store, making it the most current certificate
    393 		cvca.storeCertificate(cert);
    394 	}
    395 	finally {
    396 		p.cleanup();
    397 	}
    398 }
    399