1 /** 2 * --------- 3 * |.##> <##.| Open Smart Card Development Platform (www.openscdp.org) 4 * |# #| 5 * |# #| Copyright (c) 1999-2011 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 Script classes to access Mifare DESFire cards 25 */ 26 27 28 29 /** 30 * Create a DESFire card object 31 * @class Class encapsulating access to a Mifare DESFire EV1 card 32 * @constructor 33 * @param {card} card the card object 34 */ 35 function DESFire(card) { 36 this.card = card; 37 this.crypto = new Crypto(); 38 39 var fci = card.sendApdu(0x00, 0xA4, 0x04, 0x00, DESFire.AID, 0, [0x9000]); 40 41 this.key = new Key(); 42 this.key.setComponent(Key.DES, new ByteString("01010101010101010101010101010101", HEX)); 43 } 44 45 DESFire.AID = new ByteString("D2760000850100", HEX); 46 47 DESFire.AUTHENTICATE = 0x0A; 48 DESFire.AUTHENTICATE_ISO = 0x1A; 49 DESFire.AUTHENTICATE_AES = 0xAA; 50 51 DESFire.AUTHENTICATE = 0x0A; 52 DESFire.GET_VERSION = 0x60; 53 DESFire.NEXT_FRAME = 0xAF; 54 55 56 57 DESFire.prototype.cmd = function(cmd, data) { 58 59 if (typeof(data) != "undefined") { 60 var rsp = this.card.sendApdu(0x90, cmd, 0x00, 0x00, data, 0); 61 } else { 62 var rsp = this.card.sendApdu(0x90, cmd, 0x00, 0x00, 0); 63 } 64 65 if (this.card.SW == 0x91AF) { 66 var rsp = [ rsp ]; 67 68 while (this.card.SW == 0x91AF) { 69 rsp.push(this.card.sendApdu(0x90, DESFire.NEXT_FRAME, 0x00, 0x00, 0)); 70 } 71 } 72 return rsp; 73 } 74 75 76 77 DESFire.prototype.authenticate = function() { 78 79 var rnd_ifd = this.crypto.generateRandom(8); 80 var rnd_icc = this.card.sendApdu(0x00, 0x84, 0x00, 0x00, 8); 81 82 var plain = rnd_ifd.concat(rnd_icc); 83 var cryptogram = this.crypto.encrypt(this.key, Crypto.DES_CBC, plain); 84 85 var rnd_icc = this.card.sendApdu(0x00, 0x82, 0x09, 0x00, cryptogram, 0); 86 87 } 88 89 90 91 DESFire.prototype.nativeAuthenticate = function(cmd, keyid) { 92 93 var dfc = new DESFireCipher(this.crypto, this.key, Crypto.DES); 94 95 var enc_rnd_icc = this.card.sendApdu(0x90, cmd, 0x00, 0x00, ByteString.valueOf(keyid), 0, [0x91AF, 0x9000] ); 96 97 dfc.resetIV(); 98 var rnd_icc = dfc.decryptReceived(enc_rnd_icc); 99 100 // print("rnd_icc : " + rnd_icc); 101 102 rnd_icc = rnd_icc.bytes(1).concat(rnd_icc.bytes(0, 1)); 103 // print("rnd_icc' : " + rnd_icc); 104 105 var rnd_ifd = this.crypto.generateRandom(8); 106 // print("rnd_ifd : " + rnd_ifd); 107 108 if (cmd == DESFire.AUTHENTICATE) { 109 dfc.resetIV(); 110 var enc_ifd = dfc.decryptSend(rnd_ifd.concat(rnd_icc)); 111 dfc.resetIV(); 112 } else { 113 var enc_ifd = dfc.encryptSend(rnd_ifd.concat(rnd_icc)); 114 } 115 116 var encicc = this.cmd(DESFire.NEXT_FRAME, enc_ifd); 117 if ((this.card.SW != 0x9100) && (this.card.SW != 0x9000)) { 118 GPSystem.trace("Authentication failed, cryptogram not accepted by card"); 119 return false; 120 } 121 122 var plain = dfc.decryptReceived(encicc); 123 // print("rnd_ifd' : " + plain); 124 125 var rnd_ifd2 = plain.bytes(7).concat(plain.bytes(0, 7)); 126 return rnd_ifd.equals(rnd_ifd2); 127 } 128 129 130 131 DESFire.prototype.dump = function() { 132 var rsp = this.cmd(DESFire.GET_VERSION); 133 134 for (var i = 0; i < rsp.length; i++) { 135 print(rsp[i]); 136 } 137 // Get Mode 138 // var mode = this.card.sendApdu(0x90, 0x60, 0x00, 0x00, 0); 139 } 140 141 142 143 function DESFireCipher(crypto, key, algorithm, iv) { 144 this.crypto = crypto; 145 this.key = key; 146 this.algorithm = algorithm; 147 if (typeof(iv) == "undefined") { 148 this.iv = new ByteString("0000000000000000", HEX); 149 } else { 150 this.iv = iv; 151 } 152 } 153 154 155 156 DESFireCipher.prototype.isAES = function() { 157 return this.algorithm == Crypto.AES; 158 } 159 160 161 162 DESFireCipher.prototype.resetIV = function(iv) { 163 if (typeof(iv) == "undefined") { 164 this.iv = new ByteString("0000000000000000", HEX); 165 } else { 166 this.iv = iv; 167 } 168 } 169 170 171 172 DESFireCipher.prototype.encryptSend = function(plain) { 173 if (this.isAES()) { 174 var cipher = this.crypto.encrypt(this.key, Crypto.AES_CBC, plain, this.iv); 175 this.iv = cipher.right(16); 176 } else { 177 var cipher = this.crypto.encrypt(this.key, Crypto.DES_CBC, plain, this.iv); 178 this.iv = cipher.right(8); 179 } 180 return cipher; 181 } 182 183 184 185 DESFireCipher.prototype.decryptReceived = function(cipher) { 186 if (this.isAES()) { 187 var plain = this.crypto.decrypt(this.key, Crypto.AES_CBC, cipher, this.iv); 188 this.iv = cipher.right(16); 189 } else { 190 var plain = this.crypto.decrypt(this.key, Crypto.DES_CBC, cipher, this.iv); 191 this.iv = cipher.right(8); 192 } 193 return plain; 194 } 195 196 197 198 DESFireCipher.prototype.decryptSend = function(cipher) { 199 var blksize = this.isAES() ? 16 : 8; 200 var obuf = new ByteBuffer(); 201 202 for (var ofs = 0; ofs < cipher.length; ofs += blksize) { 203 var blk = cipher.bytes(ofs, blksize); 204 205 blk = blk.xor(this.iv); 206 207 plain = this.crypto.decrypt(this.key, this.isAES() ? Crypto.AES_ECB : Crypto.DES_ECB, blk); 208 209 this.iv = plain; 210 obuf.append(plain); 211 } 212 return obuf.toByteString(); 213 } 214 215 216 217 DESFireCipher.prototype.encryptReceived = function(plain) { 218 var blksize = this.isAES() ? 16 : 8; 219 var obuf = new ByteBuffer(); 220 221 for (var ofs = 0; ofs < plain.length; ofs += blksize) { 222 var blk = plain.bytes(ofs, blksize); 223 224 plain = this.crypto.encrypt(this.key, this.isAES() ? Crypto.AES_ECB : Crypto.DES_ECB, blk); 225 226 plain = plain.xor(this.iv); 227 228 this.iv = blk; 229 obuf.append(plain); 230 } 231 return obuf.toByteString(); 232 } 233 234 235 236 var card = new Card(_scsh3.reader); 237 238 card.reset(Card.RESET_COLD); 239 240 var desfire = new DESFire(card); 241 242 desfire.dump(); 243 244 desfire.cmd(0x45); 245 desfire.cmd(0x6A); 246 desfire.cmd(0x6F); 247 248 var result = desfire.nativeAuthenticate(DESFire.AUTHENTICATE, 0); 249 250 print("Authentication " + (result ? "OK" : "FAILED")); 251 252