      1 /**
      2  *  ---------
      3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
      4  * |#       #|
      5  * |#       #|  Copyright (c) 1999-2018 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 Implementation of a secure messaging channel as defined in ISO 7814-4 and eSign-K
     25  */
     26
     27 var APDU            = require('scsh/cardsim/APDU').APDU;
     28
     29
     30
     31 /**
     32  * Creates a decoder for a single secure messaging command APDU
     33  *
     34  * @class Decoder for a secure messaging APDU
     35  * @constructor
     36  * @param {SecureChannel} channel the secure channel object
     37  * @param {APDU} apdu the secure messaging APDU
     38  */
     39 function SecureMessagingCommandAPDUDecoder(channel, apdu) {
     40 	this.channel = channel;
     41 	this.apdu = apdu;
     42 	this.tlvlist = apdu.getCDataAsTLVList();
     43 }
     44
     45 exports.SecureMessagingCommandAPDUDecoder = SecureMessagingCommandAPDUDecoder;
     46
     47
     48
     49 /**
     50  * Verify the message authentication code (MAC)
     51  *
     52  * @type boolean
     53  * @return true if the MAC is valid
     54  */
     55 SecureMessagingCommandAPDUDecoder.prototype.verifyMAC = function() {
     56 	var macinp = this.buildMACInput();
     57
     58 	var mac = this.tlvlist.find(0x8E);
     59
     60 	if (mac == null) {
     61 		throw new GPError("SecureMessagingCommandAPDUDecoder", GPError.INVALID_DATA, APDU.SW_SMOBJMISSING, "MAC data object (8E) not found");
     62 	}
     63
     64 	return this.channel.crypto.verify(this.channel.macKey, this.channel.macMechanism, macinp, mac.getValue());
     65 }
     66
     67
     68
     69 /**
     70  * Build the MAC input block
     71  *
     72  * @type ByteString
     73  * @return the MAC calculation input block
     74  */
     75 SecureMessagingCommandAPDUDecoder.prototype.buildMACInput = function() {
     76 	var macinp = new ByteBuffer();
     77
     78 	if (typeof(this.channel.macSendSequenceCounter) != "undefined") {
     79 		var ssc = this.channel.macSendSequenceCounter.add(1);
     80 		this.channel.macSendSequenceCounter = ssc;
     81 		macinp.append(ssc);
     82 	}
     83
     84 	if (this.apdu.isAuthenticatedHeader()) {
     85 		macinp.append(this.apdu.getCLA());
     86 		macinp.append(this.apdu.getINS());
     87 		macinp.append(this.apdu.getP1());
     88 		macinp.append(this.apdu.getP2());
     89 		SecureMessagingCommandAPDUDecoder.pad(macinp, this.channel.macBlockSize);
     90 	}
     91
     92 	var someadded = false;
     93 	for (var i = 0; i < this.tlvlist.length; i++) {
     94 		var tlv = this.tlvlist.index(i);
     95
     96 		if (tlv.getTag() & 0x01) {
     97 			macinp.append(tlv.getTLV());
     98 			someadded = true;
     99 		}
    100 	}
    101 	if (someadded) {
    102 		SecureMessagingCommandAPDUDecoder.pad(macinp, this.channel.macBlockSize);
    103 	}
    104 	return macinp.toByteString();
    105 }
    106
    107
    108
    109 /**
    110  * Decrypt the body of a secure messaging APDU
    111  *
    112  * @param {Key} key the encryption key
    113  * @type ByteString
    114  * @return the plain body or undefined if no encrypted body
    115  */
    116 SecureMessagingCommandAPDUDecoder.prototype.decryptBody = function(key) {
    117 	var body = this.tlvlist.find(0x87);
    118 	var ofs = 1;
    119 	if (body == null) {
    120 		var body = this.tlvlist.find(0x85);
    121 		if (body == null) {
    122 			return undefined;
    123 		}
    124 		var ofs = 0;
    125 	} else {
    126 		var paddingIndicator = body.getValue().byteAt(0);
    127 		if (paddingIndicator != 0x01) {
    128 			throw new GPError("SecureMessagingCommandAPDUDecoder", GPError.INVALID_DATA, APDU.SW_INCSMDATAOBJECT, "Padding indicator " + paddingIndicator + " not supported");
    129 		}
    130 	}
    131
    132 	var cryptogram = body.getValue().bytes(ofs);
    133
    134 	var iv = this.channel.getIV();
    135 	var plain = this.channel.crypto.decrypt(this.channel.encKey, this.channel.encMechanism, cryptogram, iv);
    136
    137 	plain = SecureMessagingCommandAPDUDecoder.removePadding(plain);
    138
    139 	return plain;
    140 }
    141
    142
    143
    144 /**
    145  * Return value of optional Le element with tag '97'
    146  *
    147  * @type Number
    148  * @return the value of the Le element
    149  */
    150 SecureMessagingCommandAPDUDecoder.prototype.getLe = function() {
    151 	var le = this.tlvlist.find(0x97);
    152 	if (le == null) {
    153 		return -1;
    154 	}
    155 	var value = le.getValue().toUnsigned();
    156 	if (le.getL() == 2 && value == 0) {
    157 		return 65536;
    158 	}
    159 	return value;
    160 }
    161
    162
    163
    164 /**
    165  * Applies ISO padding to the input buffer
    166  *
    167  * @param {ByteBuffer} buffer the input buffer
    168  * @param {Number} blocksize the block size
    169  * @type ByteBuffer
    170  * @return the buffer argument
    171  */
    172 SecureMessagingCommandAPDUDecoder.pad = function(buffer, blocksize) {
    173 	buffer.append(0x80);
    174 	while (buffer.length % blocksize) {
    175 		buffer.append(0x00);
    176 	}
    177 	return buffer;
    178 }
    179
    180
    181
    182 /**
    183  * Removes the ISO padding
    184  *
    185  * @param {ByteString} buffer the input with with padding
    186  * @type ByteString
    187  * @return the buffer without padding
    188  */
    189 SecureMessagingCommandAPDUDecoder.removePadding = function(buffer) {
    190 	var i = buffer.length - 1;
    191
    192 	while ((i >= 0) && (buffer.byteAt(i) == 0x00)) {
    193 		i--;
    194 	}
    195
    196 	if ((i < 0) || (buffer.byteAt(i) != 0x80)) {
    197 		throw new GPError("SecureMessagingCommandAPDUDecoder", GPError.CRYPTO_FAILED, APDU.SW_INCSMDATAOBJECT, "Invalid ISO padding");
    198 	}
    199
    200 	return buffer.left(i);
    201 }
    202