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