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 	return le.getValue().toUnsigned();
156 }
157 
158 
159 
160 /**
161  * Applies ISO padding to the input buffer
162  *
163  * @param {ByteBuffer} buffer the input buffer
164  * @param {Number} blocksize the block size
165  * @type ByteBuffer
166  * @return the buffer argument
167  */
168 SecureMessagingCommandAPDUDecoder.pad = function(buffer, blocksize) {
169 	buffer.append(0x80);
170 	while (buffer.length % blocksize) {
171 		buffer.append(0x00);
172 	}
173 	return buffer;
174 }
175 
176 
177 
178 /**
179  * Removes the ISO padding
180  *
181  * @param {ByteString} buffer the input with with padding
182  * @type ByteString
183  * @return the buffer without padding
184  */
185 SecureMessagingCommandAPDUDecoder.removePadding = function(buffer) {
186 	var i = buffer.length - 1;
187 
188 	while ((i >= 0) && (buffer.byteAt(i) == 0x00)) {
189 		i--;
190 	}
191 
192 	if ((i < 0) || (buffer.byteAt(i) != 0x80)) {
193 		throw new GPError("SecureMessagingCommandAPDUDecoder", GPError.CRYPTO_FAILED, APDU.SW_INCSMDATAOBJECT, "Invalid ISO padding");
194 	}
195 
196 	return buffer.left(i);
197 }
198