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 Simulator for EAC 2.0 Protocol 25 */ 26 27 load("pace.js"); 28 29 30 /** 31 * Create an EAC 2.0 simulation object 32 * @class Class implementing a EAC simulator 33 * 34 * @constructor 35 */ 36 function EAC20Sim() { 37 this.reset(0); 38 39 this.pacedp = new Key(); 40 this.pacedp.setComponent(Key.ECC_CURVE_OID, new ByteString("1.3.36.3.3.2.8.1.1.7", OID)); 41 } 42 43 44 EAC20Sim.emptyData = new ByteString("", HEX); 45 46 47 /** 48 * Resets the card and all internal variables. 49 * 50 * @param {Number} procedure one of Card.RESET_COLD or Card.RESET_WARM - has no relevance 51 */ 52 EAC20Sim.prototype.reset = function(procedure) { 53 this.SW = 0x9000; 54 this.se = { VEXK: new SecurityEnvironment(), CDIK: new SecurityEnvironment(), SMRES: new SecurityEnvironment(), SMCOM: new SecurityEnvironment()}; 55 this.pace = null; 56 } 57 58 59 60 /** 61 * Manage security environment. 62 * 63 * <p>Only the SET variante is supported.</p> 64 * <p>Called internally with INS 22.</p> 65 */ 66 EAC20Sim.prototype.manageSE = function(p1, p2, data) { 67 if ((p1 & 0x0F) == 1) { // SET 68 var tlv = new ASN1(p2, data); 69 tlv = new ASN1(tlv.getBytes()); // Dirty trick to deserialize as TLV tree 70 71 if (p1 & 0x80) { // Verification, Encryption, External Authentication and Key Agreement 72 this.se.VEXK.add(tlv); 73 } 74 if (p1 & 0x40) { // Calculation, Decryption, Internal Authentication and Key Agreement 75 this.se.CDIK.add(tlv); 76 } 77 if (p1 & 0x20) { // Secure Messaging Response 78 this.se.SMRES.add(tlv); 79 } 80 if (p1 & 0x10) { // Secure Messaging Command 81 this.se.SMCOM.add(tlv); 82 } 83 print(tlv); 84 } else { 85 this.SW = 0x8A61; // Function not supported 86 } 87 } 88 89 90 91 /** 92 * Performs a GENERAL AUTHENTICATE command in various steps of the EAC protocol 93 * 94 * @param {Number} p1 the P1 parameter from the command APDU 95 * @param {Number} p2 the P2 parameter from the command APDU 96 * @param {ByteString} data the data part from the command APDU 97 * @param {Number} le the Le parameter from the command APDU 98 * @return the response data 99 * @type ByteString 100 */ 101 EAC20Sim.prototype.generalAuthenticate = function(p1, p2, data, le) { 102 var a = new ASN1(data); 103 104 if (a.tag != 0x7C) 105 throw new GPError("EACSIM", GPError.INVALID_DATA, 0, "Body must contain data element 0x7C"); 106 107 var response; 108 109 if (a.elements == 0) { // 1st General Authenticate 110 // ToDo use info from SE 111 this.pace = new PACE(PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_128, this.pacedp); 112 this.pace.setPassword(new ByteString("000001", ASCII)); 113 var encnonce = this.pace.getEncryptedNonce(); 114 response = new ASN1(0x80, encnonce); 115 } else { 116 if (!this.pace) 117 throw new GPError("EACSIM", GPError.INVALID_MECH, 0, "PACE must have been initialized"); 118 119 if (a.elements != 1) 120 throw new GPError("EACSIM", GPError.INVALID_DATA, 0, "Dynamic Authentication Data may only contain 1 element"); 121 122 a = a.get(0); 123 124 switch(a.tag) { 125 case 0x81: 126 if (!this.pace.hasNonce()) 127 throw new GPError("EACSIM", GPError.INVALID_DATA, 0, "Invalid sequence. First GA missing"); 128 129 if (this.pace.hasMapping()) 130 throw new GPError("EACSIM", GPError.INVALID_DATA, 0, "Invalid sequence. Steps was already performed"); 131 132 if (a.value.byteAt(0) != 0x04) 133 throw new GPError("EACSIM", GPError.INVALID_DATA, 0, "Public key does not start with '04'"); 134 135 var mappingData = this.pace.getMappingData(); 136 response = new ASN1(0x82, mappingData); 137 138 this.pace.performMapping(a.value); 139 break; 140 case 0x83: 141 if (!this.pace.hasMapping()) 142 throw new GPError("EACSIM", GPError.INVALID_DATA, 0, "Invalid sequence. Second GA missing"); 143 144 if (a.value.byteAt(0) != 0x04) 145 throw new GPError("EACSIM", GPError.INVALID_DATA, 0, "Public key does not start with '04'"); 146 147 var ephKey = this.pace.getEphemeralPublicKey(); 148 response = new ASN1(0x84, ephKey); 149 150 this.pace.performKeyAgreement(a.value); 151 break; 152 case 0x85: 153 if (!this.pace.verifyAuthenticationToken(a.value)) { 154 throw new GPError("EACSIM", GPError.INVALID_DATA, 0, "Verification of authentication token failed"); 155 } 156 157 var authToken = this.pace.calculateAuthenticationToken(); 158 159 response = new ASN1(0x86, authToken); 160 break; 161 default: 162 throw new GPError("EACSIM", GPError.INVALID_DATA, 0, "Unsupported Dynamic Authentication Data"); 163 } 164 } 165 166 var t = new ASN1(0x7C, response); 167 return t.getBytes(); 168 } 169 170 171 172 /** 173 * Send an APDU to the simulation. 174 * 175 * @param {Number} cla the class byte 176 * @param {Number} ins the instruction byte 177 * @param {Number} p1 the parameter 1 byte 178 * @param {Number} p2 the parameter 2 byte 179 * @param {Number} p3 absent or data or Le 180 * @param {Number} p4 absent or Le 181 * @return the data returned from the simulation or an empty ByteString 182 * @type ByteString 183 */ 184 185 EAC20Sim.prototype.sendApdu = function(cla, ins, p1, p2, p3, p4) { 186 187 var data = EAC20Sim.emptyData; 188 this.SW = 0x9000; 189 190 try { 191 switch(ins) { 192 case 0x22: 193 this.manageSE(p1, p2, p3); 194 break; 195 case 0x86: 196 data = this.generalAuthenticate(p1, p2, p3, p4); 197 break; 198 } 199 } 200 catch(e) { 201 print("Exception " + e); 202 if (e instanceof GPError) { 203 if (e.reason != 0) { 204 this.SW = e.reason; 205 } else { 206 this.SW = 0x6300; 207 } 208 } 209 210 } 211 212 return data; 213 } 214 215 216 217 /** 218 * Displays all internal informations. 219 */ 220 EAC20Sim.prototype.toString = function() { 221 var str = "SW1/SW2: " + this.SW + "\n"; 222 str += "SE for Verification, Encryption, External Authentication and Key Agreement\n"; 223 str += this.se.VEXK.toString(); 224 str += "SE for Calculation, Decryption, Internal Authentication and Key Agreement\n"; 225 str += this.se.CDIK.toString(); 226 str += "SE for Secure Messaging Response\n"; 227 str += this.se.SMRES.toString(); 228 str += "SE for Secure Messaging Command\n"; 229 str += this.se.SMCOM.toString(); 230 231 str += this.pace; 232 return str; 233 } 234 235 236 237 /** 238 * Creates a security environment container that collect cryptographic reference templates (CRT) 239 * 240 * @class Class implementing a security environment for cryptographic operations. 241 * @constructor 242 */ 243 function SecurityEnvironment() { 244 this.t = { AT:null, KAT: null, HT: null, CCT:null, DST:null, CT: null }; 245 } 246 247 248 249 /** 250 * Adds CRT elements to a named template. 251 * 252 * @param {String} tname the CRT name one of AT, KAT, HT, CCT, DST or CT 253 * @param {ASN1} tlv the tlv object containing the CRT elements 254 **/ 255 SecurityEnvironment.prototype.addElements = function(tname, tlv) { 256 var t = this.t[tname]; 257 if (t) { 258 for (var i = 0; i < tlv.elements; i++) { 259 var o = tlv.get(i); 260 SecurityEnvironment.decorateCRT(o); 261 t.add(o); 262 } 263 } else { 264 for (var i = 0; i < tlv.elements; i++) { 265 var o = tlv.get(i); 266 SecurityEnvironment.decorateCRT(o); 267 } 268 this.t[tname] = tlv; 269 } 270 } 271 272 273 274 /** 275 * Adds a CRT identified by it's tag 276 * 277 * @param {ASN1} tlv the tlv object 278 */ 279 SecurityEnvironment.prototype.add = function(tlv) { 280 switch(tlv.tag) { 281 case 0xA4: 282 tlv.setName("AT"); 283 break; 284 case 0xA6: 285 tlv.setName("KAT"); 286 break; 287 case 0xAA: 288 tlv.setName("HT"); 289 break; 290 case 0xB4: 291 tlv.setName("CCT"); 292 break; 293 case 0xB6: 294 tlv.setName("DST"); 295 break; 296 case 0xB8: 297 tlv.setName("CT"); 298 break; 299 default: 300 throw new GPError("SecurityEnvironment", GPError.INVALID_DATA, tlv.tag, "Invalid tag for CRT"); 301 } 302 this.addElements(tlv.name, tlv); 303 } 304 305 306 307 /** 308 * Return textual representation of security environment container 309 */ 310 SecurityEnvironment.prototype.toString = function() { 311 var str = ""; 312 313 if (this.t.AT) { 314 str += "Authentication Template (AT)\n" + this.t.AT; 315 } 316 if (this.t.KAT) { 317 str += "Key Agreement Template (KAT)\n" + this.t.KAT; 318 } 319 if (this.t.HT) { 320 str += "Hash Template (HT)\n" + this.t.HT; 321 } 322 if (this.t.CCT) { 323 str += "Cryptographic Checksum Template (CCT)\n" + this.t.CCT; 324 } 325 if (this.t.DST) { 326 str += "Digital Signature Template (DST)\n" + this.t.DST; 327 } 328 if (this.t.CT) { 329 str += "Confidentiality Template (CT)\n" + this.t.CT; 330 } 331 return str; 332 } 333 334 335 336 /** 337 * Decorates a tlv object from the CRT 338 */ 339 SecurityEnvironment.decorateCRT = function(asn1) { 340 switch(asn1.tag) { 341 case 0x80: 342 asn1.setName("cryptographicMechanism 80"); 343 break; 344 case 0x81: 345 asn1.setName("fileIdentifierOrPath 81"); 346 break; 347 case 0x82: 348 asn1.setName("dFName 82"); 349 break; 350 case 0x83: 351 asn1.setName("secretOrPublicKeyReference 83"); 352 break; 353 case 0x84: 354 asn1.setName("sessionOrPrivateKeyReference 84"); 355 break; 356 case 0x85: 357 asn1.setName("nullBlock 85"); 358 break; 359 case 0x86: 360 asn1.setName("chainingBlock 86"); 361 break; 362 case 0x87: 363 asn1.setName("initialBlock 87"); 364 break; 365 case 0x88: 366 asn1.setName("previousChallenge 88"); 367 break; 368 case 0x89: 369 asn1.setName("proprietaryDataElementIndex 89"); 370 break; 371 case 0x8A: 372 asn1.setName("proprietaryDataElementIndex 8A"); 373 break; 374 case 0x8B: 375 asn1.setName("proprietaryDataElementIndex 8B"); 376 break; 377 case 0x8C: 378 asn1.setName("proprietaryDataElementIndex 8C"); 379 break; 380 case 0x8D: 381 asn1.setName("proprietaryDataElementIndex 8D"); 382 break; 383 case 0x90: 384 asn1.setName("cardHashCode 90"); 385 break; 386 case 0x91: 387 asn1.setName("ephemeralPublicKey 91"); 388 break; 389 case 0x92: 390 asn1.setName("cardTimeStamp 92"); 391 break; 392 case 0x93: 393 asn1.setName("dsiCounter 93"); 394 break; 395 case 0x94: 396 asn1.setName("challengeOrDerivationParameter 94"); 397 break; 398 case 0x95: 399 asn1.setName("usageQualifier 95"); 400 break; 401 case 0x8E: 402 asn1.setName("cryptographicContentReference 8E"); 403 break; 404 case 0x67: 405 asn1.setName("auxiliaryAuthenticatedData 67"); 406 break; 407 case 0x67: 408 asn1.setName("auxiliaryAuthenticatedData 67"); 409 break; 410 case 0x7F4C: 411 asn1.setName("certificateHolderAuthorisationTemplate 7F4C"); 412 break; 413 } 414 } 415