securechannel.js
Summary
Implementation of a secure messaging channel as defined in ISO 7814-4 and eSign-K
function SecureChannel(crypto) {
this.crypto = crypto;
this.policy = IsoSecureChannel.SSC_DEFAULT_POLICY;
}
SecureChannel.prototype.setSendSequenceCounterPolicy = function(policy) {
this.policy = policy;
}
SecureChannel.prototype.setEncKey = function(key) {
this.encKey = key;
if (key.getComponent(Key.AES)) {
this.encBlockSize = 16;
this.encMechanism = Crypto.AES_CBC;
this.iv = new ByteString("00000000000000000000000000000000", HEX);
} else {
this.encBlockSize = 8;
this.encMechanism = Crypto.DES_CBC;
this.iv = new ByteString("0000000000000000", HEX);
}
}
SecureChannel.prototype.setMacKey = function(key) {
this.macKey = key;
if (key.getComponent(Key.AES)) {
this.macBlockSize = 16;
this.macMechanism = Crypto.AES_CMAC;
} else {
this.macBlockSize = 8;
this.macMechanism = Crypto.DES_MAC_EMV;
}
}
SecureChannel.prototype.setMACSendSequenceCounter = function(ssc) {
this.macSendSequenceCounter = ssc;
}
SecureChannel.prototype.setEncSendSequenceCounter = function(ssc) {
this.encSendSequenceCounter = ssc;
}
SecureChannel.prototype.getIV = function() {
var iv = this.iv;
if (this.policy == IsoSecureChannel.SSC_SYNC_ENC_POLICY) {
iv = this.crypto.encrypt(this.encKey, this.encMechanism, this.macSendSequenceCounter, iv);
} else if (this.policy == IsoSecureChannel.SSC_SYNC_POLICY) {
iv = this.macSendSequenceCounter;
} else {
if (typeof(this.encSendSequenceCounter) != "undefined") {
iv = this.encSendSequenceCounter
}
}
return iv;
}
SecureChannel.prototype.unwrap = function(apdu) {
var decoder = new SecureMessagingCommandAPDUDecoder(this, apdu);
if (!decoder.verifyMAC(this.macKey)) {
throw new GPError("SecureChannel", GPError.CRYPTO_FAILED, APDU.SW_INCSMDATAOBJECT, "MAC verification failed");
}
var le = decoder.getLe();
if (le >= 0) {
apdu.ne = le;
}
var plain = decoder.decryptBody(this.encKey);
apdu.setCData(plain);
}
SecureChannel.prototype.wrap = function(apdu) {
var rdata = new ByteBuffer();
var macinp = new ByteBuffer();
if (typeof(this.macSendSequenceCounter) != "undefined") {
var ssc = this.macSendSequenceCounter.add(1);
this.macSendSequenceCounter = ssc;
macinp.append(ssc);
}
if (apdu.hasRData()) {
var padbuff = new ByteBuffer(apdu.getRData());
SecureChannel.pad(padbuff, this.encBlockSize);
var iv = this.getIV();
var cryptogram = this.crypto.encrypt(this.encKey, this.encMechanism, padbuff.toByteString(), iv);
var padind = new ByteString("01", HEX);
var do87 = new TLV(0x87, padind.concat(cryptogram), TLV.EMV);
rdata.append(do87.getTLV());
}
rdata.append(0x99);
rdata.append(2);
rdata.append(apdu.getSW() >> 8);
rdata.append(apdu.getSW() & 0xFF);
macinp.append(rdata);
SecureChannel.pad(macinp, this.macBlockSize);
var mac = this.crypto.sign(this.macKey, this.macMechanism, macinp.toByteString());
rdata.append(0x8E);
rdata.append(0x08);
rdata.append(mac.left(8));
apdu.setRData(rdata.toByteString());
}
SecureChannel.pad = function(buffer, blocksize) {
buffer.append(0x80);
while (buffer.length % blocksize) {
buffer.append(0x00);
}
return buffer;
}
SecureChannel.removePadding = function(buffer) {
var i = buffer.length - 1;
while ((i >= 0) && (buffer.byteAt(i) == 0x00)) {
i--;
}
if ((i < 0) || (buffer.byteAt(i) != 0x80)) {
throw new GPError("SecureMessagingCommandAPDUDecoder", GPError.CRYPTO_FAILED, APDU.SW_INCSMDATAOBJECT, "Invalid ISO padding");
}
return buffer.left(i);
}
function SecureMessagingCommandAPDUDecoder(channel, apdu) {
this.channel = channel;
this.apdu = apdu;
this.tlvlist = apdu.getCDataAsTLVList();
}
SecureMessagingCommandAPDUDecoder.prototype.verifyMAC = function() {
var macinp = this.buildMACInput();
var mac = this.tlvlist.find(0x8E);
if (mac == null) {
throw new GPError("SecureMessagingCommandAPDUDecoder", GPError.INVALID_DATA, APDU.SW_SMOBJMISSING, "MAC data object (8E) not found");
}
return this.channel.crypto.verify(this.channel.macKey, this.channel.macMechanism, macinp, mac.getValue());
}
SecureMessagingCommandAPDUDecoder.prototype.buildMACInput = function() {
var macinp = new ByteBuffer();
if (typeof(this.channel.macSendSequenceCounter) != "undefined") {
var ssc = this.channel.macSendSequenceCounter.add(1);
this.channel.macSendSequenceCounter = ssc;
macinp.append(ssc);
}
if (this.apdu.isAuthenticatedHeader()) {
macinp.append(this.apdu.getCLA());
macinp.append(this.apdu.getINS());
macinp.append(this.apdu.getP1());
macinp.append(this.apdu.getP2());
SecureChannel.pad(macinp, this.channel.macBlockSize);
}
var someadded = false;
for (var i = 0; i < this.tlvlist.length; i++) {
var tlv = this.tlvlist.index(i);
if (tlv.getTag() & 0x01) {
macinp.append(tlv.getTLV());
someadded = true;
}
}
if (someadded) {
SecureChannel.pad(macinp, this.channel.macBlockSize);
}
return macinp.toByteString();
}
SecureMessagingCommandAPDUDecoder.prototype.decryptBody = function(key) {
var body = this.tlvlist.find(0x87);
var ofs = 1;
if (body == null) {
var body = this.tlvlist.find(0x85);
if (body == null) {
return null;
}
var ofs = 0;
} else {
var paddingIndicator = body.getValue().byteAt(0);
if (paddingIndicator != 0x01) {
throw new GPError("SecureMessagingCommandAPDUDecoder", GPError.INVALID_DATA, APDU.SW_INCSMDATAOBJECT, "Padding indicator " + paddingIndicator + " not supported");
}
}
var cryptogram = body.getValue().bytes(ofs);
var iv = this.channel.getIV();
var plain = this.channel.crypto.decrypt(this.channel.encKey, this.channel.encMechanism, cryptogram, iv);
plain = SecureChannel.removePadding(plain);
return plain;
}
SecureMessagingCommandAPDUDecoder.prototype.getLe = function() {
var le = this.tlvlist.find(0x97);
if (le == null) {
return -1;
}
return le.getValue().toUnsigned();
}
Documentation generated by
JSDoc on Tue Sep 3 22:29:41 2013