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