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