1 /**
  2  *  ---------
  3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
  4  * |#       #|  
  5  * |#       #|  Copyright (c) 1999-2009 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 The class DataAuthentication supports Static Data Authentication and Dynamic Data Authentication.
 25  */
 26 
 27 /**
 28  * DataAuthentication class constructor
 29  * @class This class implements data authentication
 30  * @constructor
 31  * @requires EMV
 32  * @param {EMV} emv an instance of the EMV class
 33  */
 34 function DataAuthentication(emv) {	
 35 	this.emv = emv;
 36 	this.crypto= emv.crypto;
 37 	this.schemePublicKeyTable = [];
 38 }
 39 
 40 /**
 41  * Get the Registered Application Provider Identifier from EMV data model
 42  *
 43  * @type ByteString
 44  * @return the 5 byte RID
 45  */
 46 DataAuthentication.prototype.getRID = function() {
 47 	var aid = this.emv.cardDE[EMV.AID];
 48 	var rid = aid.left(5);
 49 	return(rid);
 50 }
 51 
 52 /**
 53  * Get the Public Key Index
 54  *
 55  * @type Number
 56  * @return the Public Key Index
 57  */
 58 DataAuthentication.prototype.getPubKeyIndex = function() {
 59 	var index = this.emv.cardDE[0x8F];
 60 	var index = index.toUnsigned();
 61 	return(index);
 62 }
 63 
 64 /**
 65  * Add a new public key to the array
 66  *
 67  * @param {ByteString} rid the Registered Application Provider Identifier
 68  * @param {Number} index the public key index
 69  * @param {Key} key the public key
 70  */
 71 DataAuthentication.prototype.addSchemePublicKey = function(rid, index, key) {
 72 	if(typeof(this.schemePublicKeyTable[rid.toString(HEX)]) == "undefined") {	
 73 		this.schemePublicKeyTable[rid.toString(HEX)] = [];
 74 	}
 75 	this.schemePublicKeyTable[rid.toString(HEX)][index] = key;
 76 }
 77 
 78 /**
 79  * Get the public key
 80  *
 81  *@type Key
 82  *@return the public key
 83 */
 84 DataAuthentication.prototype.getSchemePublicKey = function() {
 85 	var rid = this.getRID();
 86 	var index = this.getPubKeyIndex();
 87 	
 88 	if (typeof(this.schemePublicKeyTable[rid.toString(HEX)]) == "undefined") {
 89 		throw new GPError("DataAuthentication", GPError.OBJECT_NOT_FOUND, 0, "No scheme public key found for RID " + rid.toString(HEX));
 90 	}
 91 	var key = this.schemePublicKeyTable[rid.toString(HEX)][index];
 92 	return(key);
 93 }
 94 
 95 /**
 96  * Decryption of the Issuer Public Key Certificate
 97  *
 98  * @return the decrypted Issuer Public Key Certificate
 99 */
100 DataAuthentication.prototype.decryptIssuerPKCertificate = function() {
101 	var certificate = this.emv.cardDE[0x90];
102 	var key = this.getSchemePublicKey();
103 	var decryptedCertificate = crypto.decrypt(key, Crypto.RSA, certificate);
104 	return(decryptedCertificate);
105 }
106 
107 /**
108  * Retrieval of Issuer Public Key
109  *
110  * @type Key
111  * @return the Issuer Public Key
112 */
113 DataAuthentication.prototype.retrieveIssuerPublicKey = function() {
114 	var key = this.getSchemePublicKey();
115 	var modulus = key.getComponent(Key.MODULUS);
116 	var cert = this.decryptIssuerPKCertificate();		
117 
118 	// Step 1: Issuer Public Key Certificate and Certification Authority Public Key Modulus have the same length
119 	assert(cert.length == modulus.length);
120 
121 	// Step 2: The Recovered Data Trailer is equal to 'BC'
122 	assert(cert.byteAt(modulus.length - 1) == 0xBC);
123 	
124 	// Step 3: The Recovered Data Header is equal to '6A'	
125 	assert(cert.byteAt(0) == 0x6A);
126 	
127 	// Step 4: The Certificate Format is equal to '02'	
128 	assert(cert.byteAt(1) == 0x02);
129 	
130 	// Step 5: Concatenation
131 	var list;
132 	list = cert.bytes(1, 14 + (modulus.length - 36));
133 	var remainder = this.emv.cardDE[0x92];
134 	var exponent = this.emv.cardDE[0x9F32];
135 	var remex = remainder.concat(exponent);
136 	
137 	list = list.concat(remex);
138 		
139 	// Step 6: Generate hash from concatenation
140 	var hashConcat = this.crypto.digest(Crypto.SHA_1, list);
141 
142 	// Step 7: Compare the hash result with the recovered hash result. They have to be equal 
143 	var hashCert = cert.bytes(15 + (modulus.length - 36), 20); 
144 	assert(hashCert.equals(hashConcat));
145 
146 	// Step 8: Verify that the Issuer Identifier matches the lefmost 3-8 PAN digits	
147 	var pan = this.emv.cardDE[0x5A];
148 	pan = pan.left(4);
149 	var panCert = cert.bytes(2, 4);
150 
151 	var panCert = panCert.toString(HEX);
152 	var pan = pan.toString(HEX);
153 	for(var i = 0; i < 8; i++) {
154 		if(panCert.charAt(i) == 'F') {
155 			var panCert = panCert.substr(0, i);
156 			var pan = pan.substr(0, i);
157 		}
158 	}
159 	assert(pan == panCert);
160 
161 	// Step 9: Verify that the last day of the month specified in the Certification Expiration Date is equal to or later than today's date.  
162 
163 	// Step 10: Optional step
164 
165 	// Step 11: Check the Issuer Public Key Algorithm Indicator
166 	var pkAlgorithmIndicator = cert.byteAt(12);
167 
168 	// Step 12: Concatenate the Leftmost Digits of the Issuer Public Key and the Issuer Public Key Remainder (if present) to obtain the Issuer Public Key Modulus
169 	var leftmostDigits = cert.bytes(15, (modulus.length - 36));
170 	var issuerPublicKeyModulus = leftmostDigits.concat(remainder);
171 	return(issuerPublicKeyModulus);
172 }
173 
174 /**
175  * Verification of Signed Static Application Data
176  *
177  * @param {Key} key the Issuer Public Key
178 */
179 DataAuthentication.prototype.verifySSAD = function(issuerPublicKeyModulus) {
180 	var issuerPublicKeyModulus =  issuerPublicKeyModulus;
181 	var key = new Key();
182 	key.setType(Key.PUBLIC);
183 	key.setComponent(Key.MODULUS, issuerPublicKeyModulus);
184 	key.setComponent(Key.EXPONENT, this.emv.cardDE[0x9F32]);
185 	var SSAD = this.emv.cardDE[0x93];
186 	
187 	// Step 1: Signed Static Application Data and Issuer Public Key Modulus have the same length
188 	assert(SSAD.length == issuerPublicKeyModulus.length);
189 
190 	// Step 2: The Recovered Data Trailer is equal to 'BC'
191 	var decryptedSSAD = crypto.decrypt(key, Crypto.RSA, SSAD);
192 	assert(decryptedSSAD.byteAt(decryptedSSAD.length -1) == 0xBC);
193 
194 	// Step 3: The Recovered Data Header is equal to '6A'
195 	assert(decryptedSSAD.byteAt(0) == 0x6A);
196 
197 	// Step 4: The Signed Data Format is equal to '03'
198 	assert(decryptedSSAD.byteAt(1) == 0x03);
199 
200 	// Step 5: Concatenation
201 	var list = decryptedSSAD.bytes(1, (decryptedSSAD.length - 22));
202 	var daInput = this.emv.getDAInput();
203 	var sdaTagList = this.emv.cardDE[0x9F4A];
204 	var value = new ByteBuffer();
205 	if(typeof(sdaTagList != "undefined")) {
206 		for(var i = 0; i < sdaTagList.length; i++) {
207 			var tag = sdaTagList.byteAt(i);			
208 			value = value.append(this.emv.cardDE[tag]);
209 		}
210 	}
211 	
212 	list = list.concat(daInput);
213 	if(value != 0) {
214 		value = value.toByteString();
215 		list = list.concat(value);
216 	}
217 
218 	// Step 6: Generate hash from concatenation
219 	var hashConcat = this.crypto.digest(Crypto.SHA_1, list);
220 	
221 	// Step 7: Compare recovered hash with generated hash. Store the Data Authentication Code from SSAD in tag '9F45'
222 	var hashSSAD  = decryptedSSAD.bytes(decryptedSSAD.length - 21, 20);
223 	assert(hashConcat.equals(hashSSAD));
224 	this.emv.cardDE[0x9F45] = decryptedSSAD.bytes(3, 2);
225 	
226 	print("<-----------------------------SDA was successful------------------------------>\n");
227 }
228 
229 /**
230  * Retrieval of ICC Public Key
231  *
232  * @param {Key} key the Issuer Public Key
233  * @type Key
234  * @return the ICC Public Key
235 */
236 DataAuthentication.prototype.retrieveICCPublicKey = function(issuerPublicKeyModulus) {
237 	var issuerPublicKeyModulus =  issuerPublicKeyModulus;
238 	var key = new Key();
239 	key.setType(Key.PUBLIC);
240 	key.setComponent(Key.MODULUS, issuerPublicKeyModulus);
241 	key.setComponent(Key.EXPONENT, this.emv.cardDE[0x9F32]);
242 	var iccCert = this.emv.cardDE[0x9F46];
243 	
244 	// Step 1: ICC Public Key Certificate and Issuer Public Key Modulus have the same length
245 	assert(iccCert.length == issuerPublicKeyModulus.length);
246 
247 	// Step 2: The Recovered Data Trailer is equal to 'BC'
248 	var decryptedICC = crypto.decrypt(key, Crypto.RSA, iccCert);
249 	assert(decryptedICC.byteAt(decryptedICC.length - 1) == 0xBC);
250 	
251 	// Step 3: The Recovered Data Header is equal to '6A'	
252 	assert(decryptedICC.byteAt(0) == 0x6A);
253 	
254 	// Step 4: The Certificate Format is equal to '04'	
255 	assert(decryptedICC.byteAt(1) == 0x04);
256 	
257 	// Step 5: Concatenation
258 	var list = decryptedICC.bytes(1, (decryptedICC.length - 22));
259 	var remainder = this.emv.cardDE[0x9F48];
260 	var exponent = this.emv.cardDE[0x9F47];
261 	var remex = remainder.concat(exponent);
262 	list = list.concat(remex);	
263 	var daInput = this.emv.getDAInput();
264 	list = list.concat(daInput);
265 
266 	var sdaTagList = this.emv.cardDE[0x9F4A];
267 	if(typeof(sdaTagList != "undefined")) {
268 		var value = new ByteBuffer();
269 		for(var i = 0; i < sdaTagList.length; i++) {
270 			var tag = sdaTagList.byteAt(i);			
271 			value = value.append(this.emv.cardDE[tag]);
272 		}
273 		value = value.toByteString();
274 		list = list.concat(value);
275 	}
276 		
277 	// Step 6: Generate hash from concatenation
278 	var hashConcat = this.crypto.digest(Crypto.SHA_1, list);	
279 	
280 	// Step 7: Compare recovered hash with generated hash
281 	var hashICC  = decryptedICC.bytes(decryptedICC.length - 21, 20);
282 	assert(hashConcat.equals(hashICC));
283 
284 	// Step 8: Verify that the Issuer Identifier matches the lefmost 3-8 PAN digits	
285 	var pan = this.emv.cardDE[0x5A];	
286 	var panCert = decryptedICC.bytes(2, 10);
287 
288 	var panCert = panCert.toString(HEX);
289 	var pan = pan.toString(HEX);
290 	for(var i = 0; i < 20; i++) {
291 		if(panCert.charAt(i) == 'F') {
292 			var panCert = panCert.substr(0, i);
293 			var pan = pan.substr(0, i);
294 		}
295 	}
296 	assert(pan == panCert);
297 
298 	// Step 9: Verify that the last day of the month specified in the Certification Expiration Date is equal to or later than today's date.  
299 
300 	// Step 10: Check the ICC Public Key Algorithm Indicator
301 	var pkAlgorithmIndicator = decryptedICC.byteAt(18);
302 
303 
304 	// Step 11: Concatenate the Leftmost Digits of the ICC Public Key and the ICC Public Key Remainder (if present) to obtain the ICC Public Key Modulus
305 	
306 	var modulus = key.getComponent(Key.MODULUS);
307 	var leftmostDigits = decryptedICC.bytes(21, (modulus.length - 42));
308 	var iccPublicKeyModulus = leftmostDigits.concat(remainder);
309 	return(iccPublicKeyModulus)
310 }	
311 
312 /**
313  * Generation and verification of the dynamic signature.
314  * A successfully retrieval of the ICC Public Key is required.
315  *
316  * @param {Key} key the ICC Public Key
317 */
318 DataAuthentication.prototype.dynamicDataAuthentication = function(iccPublicKeyModulus) {
319 	var iccPublicKeyModulus = iccPublicKeyModulus;
320 	
321 	var Data = crypto.generateRandom(4);
322 	var internalAuthenticate = card.sendApdu(0x00, 0x88, 0x00, 0x00, Data, 0x00);
323 	var asn = new ASN1(internalAuthenticate);
324 	var tag = asn.find(0x9F4B);
325 	var SDAD = tag.value;
326 	
327 	var picKey = new Key();
328 	picKey.setType(Key.PUBLIC);
329 	picKey.setComponent(Key.MODULUS, iccPublicKeyModulus);
330 	picKey.setComponent(Key.EXPONENT, this.emv.cardDE[0x9F47]);
331 	var decryptedSDAD = crypto.decrypt(picKey, Crypto.RSA, SDAD);
332 	// Step 1: SDAD and ICC Public Key Modulus have the same length
333 	assert(SDAD.length == iccPublicKeyModulus.length);
334 	
335 	// Step 2: The Recovered Data Trailer is equal to 'BC'
336 	assert(decryptedSDAD.byteAt(decryptedSDAD.length - 1) == 0xBC);
337 	
338 	// Step 3: The Recovered Data Header is equal to '6A'
339 	assert(decryptedSDAD.byteAt(0) == 0x6A);
340 	
341 	// Step 4: The Signed Data Format is equal to '05'
342 	assert(decryptedSDAD.byteAt(1) == 0x05);
343 	
344 	// Step 5: Concatenation of Signed Data Format, Hash Algorithm Indicator, ICC Dynamic Data Length, ICC Dynamic Data, Pad Pattern, random number
345 	var LDD = decryptedSDAD.byteAt(3);
346 	var list = decryptedSDAD.bytes(1, 3 + LDD + decryptedSDAD.length - LDD - 25);
347 	list = list.concat(Data);
348 	
349 	// Step 6: Genereate hash from concatenation
350 	var hashConcat = this.crypto.digest(Crypto.SHA_1, list);
351 	
352 	// Step 7: Compare recovered hash with generated hash
353 	var hashSDAD = decryptedSDAD.bytes(decryptedSDAD.length - 21, 20);
354 	assert(hashConcat.equals(hashSDAD));
355 	print("<-----------------------------DDA was successful------------------------------>\n");
356 }
357