agreeKey.js

Summary

Agree a common key between two SmartCard-HSM (requires V2.1)


Class Summary
Endpoint  

/**
 *  ---------
 * |.##> <##.|  SmartCard-HSM Support Scripts
 * |#       #|
 * |#       #|  Copyright (c) 2011-2015 CardContact Software & System Consulting
 * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
 *  ---------
 *
 * Consult your license package for usage terms and conditions.
 *
 * @fileoverview Agree a common key between two SmartCard-HSM (requires V2.1)
 */

CVC = require("scsh/eac/CVC").CVC;
SmartCardHSM = require("scsh/sc-hsm/SmartCardHSM").SmartCardHSM;
SmartCardHSMKeySpecGenerator = require("scsh/sc-hsm/SmartCardHSM").SmartCardHSMKeySpecGenerator;
HSMKeyStore = require("scsh/sc-hsm/HSMKeyStore").HSMKeyStore;



/**
 * A communication endpoint with a SmartCard-HSM V2.1
 */
function Endpoint(readername,name) {
	this.card = new Card(readername);
	this.crypto = new Crypto();

	this.sc = new SmartCardHSM(this.card);
	this.ks = new HSMKeyStore(this.sc);
	this.ephemeralKeyName = "ephemeralKey:" + name;

	var devAutCert = this.sc.readBinary(SmartCardHSM.C_DevAut);
	var chain = SmartCardHSM.validateCertificateChain(this.crypto, devAutCert);
	if (chain == null) {
		throw new GPError("Endpoint", GPError.DEVICE_ERROR, 0, "Card is not a valid SmartCard-HSM");
	}
	this.chain = chain;
}



/**
 * Perform user authentication
 */
Endpoint.prototype.login = function(pin) {
	var sw = this.sc.verifyUserPIN(new ByteString(pin, ASCII));

	if (sw != 0x9000) {
		print(SmartCardHSM.describePINStatus(sw, "User PIN"));
	}

	return sw == 0x9000;
}



/**
 * Generate ephemeral key for communication session
 */
Endpoint.prototype.generateEphemeralKey = function() {
	if (this.ks.hasKey(this.ephemeralKeyName)) {
		this.ks.deleteKey(this.ephemeralKeyName);
	}

	var dp = new Key();
	dp.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID));

	var gen = new SmartCardHSMKeySpecGenerator(Crypto.EC, dp);
	gen.algorithms = new ByteString("83", HEX);

	print("Generating key");
	var req = this.ks.generateKeyPair(this.ephemeralKeyName, gen);

	var a = new ASN1(0x30);
	a.add(req.getASN1());
	a.add(this.chain.devicecert.getASN1());
	a.add(this.chain.dica.getASN1());

//	print(a);
	return (a.getBytes());
}



/**
 * Verify certificate chain from communication partner
 */
Endpoint.prototype.verifyCertificate = function(publicKey) {
	assert(publicKey.byteAt(0) == 0x30, "Not a public key structure");

	var a = new ASN1(publicKey);
	assert(a.elements == 3, "Must contain three elements");

	var dev = new CVC(a.get(1));
	var dica = new CVC(a.get(2));

	print("Verify certificate");
	this.sc.verifyCertificate(dica);
	print("Verify certificate");
	this.sc.verifyCertificate(dev);

	var ext = dev.getExtension(new ByteString("iso(1) org(3) dod(6) internet(1) private(4) enterprise(1) cardcontact(24991) ca(3) extensions(2) 1", OID));
	if (ext) {
		print("Identity of peer " + ext.get(1).value.toString(UTF8));
	}
}



/**
 * Derive key based on authenticated public key from communication partner
 */
Endpoint.prototype.deriveKey = function(publicKey, deriveParam) {
	assert(publicKey.byteAt(0) == 0x30, "Not a public key structure");

	var a = new ASN1(publicKey);
	assert(a.elements == 3, "Must contain three elements");

	print("Derive key");

	var prk = this.ks.getKey(this.ephemeralKeyName);

	var pub = new CVC(a.get(0));

	var car = pub.getOuterCAR().getBytes();

	var pukrefdo = new ASN1(0x83, car);
	var pukref = pukrefdo.getBytes();

	this.sc.card.sendSecMsgApdu(Card.ALL, 0x00, 0x22, 0x81, 0xB6, pukref, [0x9000]);

	// Extract value of 67
	var tl = new TLVList(pub.getBytes(), TLV.EMV);
	var t = tl.index(0);
	var pubo = t.getValue();

	var cdata = pubo.concat((new ASN1(0x50, deriveParam)).getBytes());

	// Derive Key
	var ss = this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x62, prk.id, 0x83, cdata, 0, [0x9000] );
	assert(ss.length == 32, "Shared secret length must be 32 bytes");

	return ss;
}



var pin = "648219";

var rl = Card.getReaderList();

if (typeof(r1) != "string") {
	var r1 = rl[0];
}

if (typeof(r2) != "string") {
	if (rl.length == 1) {
		var r2 = r1;
	}
	var r2 = rl[1];
}

var r1 = Dialog.prompt("Select reader 1", r1, rl);
assert(r1 != null);

var r2 = Dialog.prompt("Select reader 2", r2, rl);
assert(r2 != null);

// Change reader names accordingly
var ep1 = new Endpoint(r1, "ep1");
var ep2 = new Endpoint(r2, "ep2");

assert(ep1.login(pin), "Login failed");
assert(ep2.login(pin), "Login failed");

pub1 = ep1.generateEphemeralKey();
pub2 = ep2.generateEphemeralKey();

ep1.verifyCertificate(pub2);
ep2.verifyCertificate(pub1);

var deriveParam = new ByteString("Label", ASCII);

var k1 = ep1.deriveKey(pub2, deriveParam);
print("Key on ep1: " + k1);

var k2 = ep2.deriveKey(pub1, deriveParam);
print("Key on ep2: " + k2);



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