/**
 *  ---------
 * |.##> <##.|  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 Personalization Client
 */

var CardTask = require("scsh/perso/CardTask").CardTask;
var File = require("scsh/file/File").File;



function SCPAuthenticator(keys) {
	this.crypto = new Crypto();
	this.keys = keys;
	this.locateKMC();
}

exports.SCPAuthenticator = SCPAuthenticator;



SCPAuthenticator.prototype.deriveKeyAESKMC = function(key, ddc, size, dd) {
	var context = ByteString.valueOf(ddc, 4).concat(ByteString.valueOf(0)).concat(dd);

	var dd = new ByteBuffer();
	var os = size;
	var iter = 1;
	while (os > 0) {
		var dp = new ByteBuffer();
		dp.append(ByteString.valueOf(iter));
		dp.append(context);

		var mac = key.sign(Crypto.AES_CMAC, dp.toByteString());

		dd.append(mac.left(os > mac.length ? mac.length : os));
		os -= mac.length;
		iter++;
	}
	return dd.toByteString();
}



SCPAuthenticator.prototype.getGPScpKeySet = function(scp, version, derivationData, forReplacement) {
	print("SCP0" + scp + " Key Version " + version);

	var kt = scp == 2 ? Key.DES : Key.AES;

	var kenc = new Key();
	var kmac = new Key();
	var kdek = new Key();

	if (version == -1) {		// The NXP Transport Key
		if (typeof(this.transportKeys) == "undefined") {
			throw new GPError(module.id, GPError.OBJECT_NOT_FOUND, 0, "No transport key defined");
		}

		kenc.setComponent(kt, this.transportKeys.bytes(0, 16));
		kmac.setComponent(kt, this.transportKeys.bytes(16, 16));
		kdek.setComponent(kt, this.transportKeys.bytes(32, 16));
	} else if (version == 1) {	// The Key for Secure Transport (KST)
		if (!forReplacement) {
			var a = new ASN1(this.keys.KST);
		} else {
			var a = new ASN1(this.keys.NewKST);
		}
		kenc.setComponent(kt, a.get(0).value);
		kmac.setComponent(kt, a.get(1).value);
		kdek.setComponent(kt, a.get(2).value);
	} else if ((version == 2) && (scp == 2)) {	// The old KMC key derivation scheme using a DES KMC
		if (typeof(this.keys.KMC) == "undefined") {
			throw new GPError(module.id, GPError.OBJECT_NOT_FOUND, 0, "No DES KMC for version 2 defined");
		}
		var kmc = new Key();
		kmc.setComponent(Key.DES, this.keys.KMC);
		var dd = derivationData.right(6);
		var derivationParam = dd.concat(new ByteString("F001", HEX)).concat(dd).concat(new ByteString("0F01", HEX));
		this.crypto.deriveKey(kmc, Crypto.DES_ECB, derivationParam, kenc);
		var derivationParam = dd.concat(new ByteString("F002", HEX)).concat(dd).concat(new ByteString("0F02", HEX));
		this.crypto.deriveKey(kmc, Crypto.DES_ECB, derivationParam, kmac);
		var derivationParam = dd.concat(new ByteString("F003", HEX)).concat(dd).concat(new ByteString("0F03", HEX));
		this.crypto.deriveKey(kmc, Crypto.DES_ECB, derivationParam, kdek);
	} else if ((version == 3) || ((version == 2) && (scp == 3))) {	// The new KMC key derivation scheme using a AES KMC on a SC-HSM
		if (typeof(this.kmc) == "undefined") {
			throw new GPError(module.id, GPError.OBJECT_NOT_FOUND, 0, "No KMC for version 3 defined");
		}
		kenc.setComponent(kt, this.deriveKeyAESKMC(this.kmc, 1, 16, derivationData));
		kmac.setComponent(kt, this.deriveKeyAESKMC(this.kmc, 2, 16, derivationData));
		kdek.setComponent(kt, this.deriveKeyAESKMC(this.kmc, 3, 16, derivationData));
	} else {
		throw new GPError(module.id, GPError.OBJECT_NOT_FOUND, 0, "Unsupported key version");
	}

	return new GPScpKeySet(version, kenc, kmac, kdek);
}



SCPAuthenticator.prototype.locateKMC = function() {
	if (typeof(km) == "undefined") {
		return;
	}

	if (typeof(km.managementToken) == "undefined") {
		return;
	}

	if (typeof(km.managementToken.tmk) == "undefined") {
		return;
	}

	this.kmc = km.managementToken.tmk;
	print("Found KMC for V3");
}
