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