EACKeyStore.js

Summary

Keystore class for EAC keys and certificates


Class Summary
EACKeyStore  

/**
 *  ---------
 * |.##> <##.|  SmartCard-HSM Support Scripts
 * |#       #|
 * |#       #|  Copyright (c) 2011-2012 CardContact Software & System Consulting
 * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
 *  ---------
 *
 * Consult your license package for usage terms and conditions.
 *
 * @fileoverview Keystore class for EAC keys and certificates
 */

SmartCardHSM = require("scsh/sc-hsm/SmartCardHSM").SmartCardHSM;
CVC = require("scsh/eac/CVC").CVC;
PublicKeyReference = require('scsh/eac/PublicKeyReference').PublicKeyReference;



/**
 * A simple EAC Key Store using a SmartCard-HSM
 *
 */
function EACKeyStore(sc, certstore, path) {
	this.sc = sc;
	this.certstore = certstore;
	this.path = path;
}

exports.EACKeyStore = EACKeyStore;



/**
 * Return the associated certificate store
 *
 * @type CVCertificateStore
 * @return the certificate store
 */
EACKeyStore.prototype.getCertificateStore = function() {
	return this.certstore;
}



/**
 * Return the device authentication certificate
 *
 * @type CVC
 * @return the device authentication certificate for this key store
 */
EACKeyStore.prototype.getC_DevAut = function() {
	var rsp = sc.readBinary(SmartCardHSM.C_DevAut);
	return new CVC(rsp);
}



/**
 * Determine FID of current CVC certificate
 *
 * @type ByteString
 * @return the file identifier of the current certificate
 */
EACKeyStore.prototype.getCurrentCertificateFID = function() {
	var i = 0;
	while (i <= 1) {
		try	{
			var fid = ByteString.valueOf(0xC001 + i);
			var cvcbin = sc.readBinary(fid);
			var cvc = new CVC(cvcbin);
			return fid;
		}
		catch(e) {
			// Ignore
		}
		i++;
	}
	return null;
}



/**
 * Return the CVC certificate stored under the given FID
 *
 * @type CVC
 * @return the CVC stored under the given FID
 */
EACKeyStore.prototype.getCertificateByFID = function(fid) {
	try	{
		var cvcbin = sc.readBinary(fid);
		var cvc = new CVC(cvcbin);
		return cvc;
	}
	catch(e) {
		GPSystem.trace("No certificate found in " + fid.toString(HEX) + " or certificate invalid");
	}
	return null;
}



/**
 * Generate the next sequence number for the certificate request
 *
 * @type String
 * @return the 5 digit sequence number
 */
EACKeyStore.prototype.getNextSequence = function() {
	var nextseq = 1;
	var fid = new ByteString("C100", HEX);
	try	{
		var seqno = sc.readBinary(fid);
		var nextseq = seqno.toUnsigned() + 1;
	}
	catch(e) {
		GPSystem.trace("No sequence no found in " + fid.toString(HEX));
	}

	sc.updateBinary(fid, 0, ByteString.valueOf(nextseq), 4);
	var seq = "" + nextseq;
	seq = "00000".substr(0, 5 - seq.length) + seq;
	return seq;
}



/**
 * Generate certificate request
 *
 * @param {boolean} forceInitial true to force a new initial request
 * @type CVC
 * @return the certificate request
 */
EACKeyStore.prototype.generateRequest = function(forceInitial) {
	// Determine identity of the key store
	var c_devaut = this.getC_DevAut();
	var chr = c_devaut.getCHR();
	var outerCAR;
	var initialRequest = false;

	var fid = this.getCurrentCertificateFID();
	if (fid && (!forceInitial)) {					// Current certificate and not forced into initial request
		var signkid = fid.byteAt(1);				// Use current key for outer signature
		var keyid = 3 - signkid;				// Select key id for new key
		var currentcvc = this.getCertificateByFID(fid);
		outerCAR = currentcvc.getCHR();
	} else {
		var keyid = 1;						// New card
		var signkid = 0;
		initialRequest = true;
		if (fid) {
			if (this.sc.hasFile(fid)) {
				this.sc.deleteFile(fid);
			}
		}
	}

	// Determine current DVCA certificate
	var car = this.certstore.getCurrentCHR(this.path);
	if (car == null) {
		print("Can't locate current DVCA certificate");
		return;
	}
	var dvca = this.certstore.getCertificate(this.path, car);

	// Determine next public key reference
	var chr = new PublicKeyReference(chr.getHolder() + this.getNextSequence());

	// Determine parameter for request
	var algo = dvca.getPublicKeyOID();
	if (CVC.isECDSA(algo)) {
		var dp = this.certstore.getDomainParameter(this.path, car);
		var cdata = SmartCardHSM.buildGAKPwithECC(car, algo, chr, dp, outerCAR);
	} else {
		var cdata = SmartCardHSM.buildGAKPwithRSA(car, algo, chr, 1024, outerCAR);
	}

	print("Generating new key and certificate signing request...");

	var fid = ByteString.valueOf((SmartCardHSM.KEYPREFIX << 8) + keyid);
	this.sc.deleteFile(fid);

	var req = this.sc.generateAsymmetricKeyPair(keyid, signkid, cdata);

	if (initialRequest) {			// Strip outer signature on initial request as the DVCA does currently not support authenticated initial requests
		var a = new ASN1(req);
		req = a.get(0);
	}

	return new CVC(req);
}



/**
 * Update certificate on device with certificate received from DVCA
 */
EACKeyStore.prototype.updateCardCertificate = function() {
	var c_devaut = this.getC_DevAut();
	var chr = c_devaut.getCHR();
	var path = this.path + "/" + chr.getHolder();
	var chr = this.certstore.getCurrentCHR(path);
	var cvc = this.certstore.getCertificate(path, chr);
	var fid = this.getCurrentCertificateFID();
	if (!fid) {
		var newfid = new ByteString("C001", HEX);
	} else {
		var newfid = ByteString.valueOf(0xC000 + 3 - fid.byteAt(1));
	}
	this.sc.updateBinary(newfid, 0, cvc.getBytes());
	print("Certificate written to EF " + newfid.toString(HEX));
	if (fid) {
		this.sc.deleteFile(fid);
		print("EF " + fid.toString(HEX) + " deleted");
	}
}



Documentation generated by JSDoc on Sat Feb 24 15:17:19 2024