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