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 Implementation of the chip authentication protocol for both card and terminal
 25  * as defined for EAC 2.0
 26  */
 27 
 28 
 29 
 30 /**
 31  * Create a ChipAuthenticationInfo object
 32  *
 33  * @class <p>This class encodes and decodes ChipAuthenticationInfo objects.</p>
 34  * <p>The class implements the following ASN.1 syntax:</p>
 35  * <pre>
 36  *	ChipAuthenticationInfo ::= SEQUENCE {
 37  *		protocol OBJECT IDENTIFIER(
 38  *			id-CA-DH-3DES-CBC-CBC |
 39  *			id-CA-DH-AES-CBC-CMAC-128 |
 40  *			id-CA-DH-AES-CBC-CMAC-192 |
 41  *			id-CA-DH-AES-CBC-CMAC-256 |
 42  *			id-CA-ECDH-3DES-CBC-CBC |
 43  *			id-CA-ECDH-AES-CBC-CMAC-128 |
 44  *			id-CA-ECDH-AES-CBC-CMAC-192 |
 45  *			id-CA-ECDH-AES-CBC-CMAC-256),
 46  *		version INTEGER, -- MUST be 1 or 2
 47  *		keyId INTEGER OPTIONAL
 48  * }
 49  * </pre>
 50  * @constructor
 51  * @param {ASN1} the optional tlv structure to initialize the object
 52  */
 53 function ChipAuthenticationInfo(tlv) {
 54 	if (tlv && (tlv instanceof ASN1)) {
 55 		assert(tlv.isconstructed);
 56 		assert(tlv.elements >= 2);
 57 
 58 		var i = 0;
 59 		var t = tlv.get(i++);
 60 		assert(t.tag == ASN1.OBJECT_IDENTIFIER);
 61 		this.protocol = t.value;
 62 
 63 		var t = tlv.get(i++);
 64 		assert(t.tag == ASN1.INTEGER);
 65 		this.version = t.value.toSigned();
 66 
 67 		if (i < tlv.elements) {
 68 			var t = tlv.get(i++);
 69 			assert(t.tag == ASN1.INTEGER);
 70 			this.keyId = t.value.toSigned();
 71 		}
 72 
 73 	}
 74 }
 75 
 76 exports.ChipAuthenticationInfo = ChipAuthenticationInfo;
 77 
 78 
 79 /**
 80  * Convert object to TLV structure
 81  *
 82  * @return the TLV structure
 83  * @type ASN1
 84  */
 85 ChipAuthenticationInfo.prototype.toTLV = function() {
 86 	var t = new ASN1(ASN1.SEQUENCE);
 87 
 88 	t.add(new ASN1(ASN1.OBJECT_IDENTIFIER, this.protocol));
 89 
 90 	var bb = new ByteBuffer();
 91 	bb.append(this.version);
 92 	t.add(new ASN1(ASN1.INTEGER, bb.toByteString()));
 93 
 94 	if (typeof(this.keyId) != "undefined") {
 95 		t.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(this.keyId)));
 96 	}
 97 	return t;
 98 }
 99 
100 
101 
102 ChipAuthenticationInfo.prototype.toString = function() {
103 	return "ChipAuthenticationInfo(protocol=" + this.protocol + ", version=" + this.version + ", keyId=" + this.keyId + ")";
104 }
105 
106 
107 
108 /**
109  * Create a ChipAuthenticationDomainParameterInfo object
110  *
111  * @class <p>This class encodes and decodes ChipAuthenticationDomainParameterInfo objects.</p>
112  * <p>The class implements the following ASN.1 syntax:</p>
113  * <pre>
114  *	ChipAuthenticationDomainParameterInfo ::= SEQUENCE {
115  *		protocol OBJECT IDENTIFIER(id-CA-DH | id-CA-ECDH),
116  *		domainParameter AlgorithmIdentifier,
117  *		keyId INTEGER OPTIONAL
118  * }
119  * </pre>
120  * @constructor
121  * @param {ASN1} the optional tlv structure to initialize the object
122  */
123 function ChipAuthenticationDomainParameterInfo(tlv) {
124 	if (tlv && (tlv instanceof ASN1)) {
125 		assert(tlv.isconstructed);
126 		assert(tlv.elements >= 2);
127 
128 		var i = 0;
129 		var t = tlv.get(i++);
130 		assert(t.tag == ASN1.OBJECT_IDENTIFIER);
131 		this.protocol = t.value;
132 
133 		var t = tlv.get(i++);
134 		assert(t.tag == ASN1.SEQUENCE);
135 
136 		if (t.elements > 0) {
137 			var oid = t.get(0);
138 			assert(oid.tag == ASN1.OBJECT_IDENTIFIER);
139 			if (oid.value.equals(new ByteString("standardizedDomainParameter", OID))) {
140 				this.standardizedDomainParameter = t.get(1).value.toUnsigned();
141 				var curveoid = ChipAuthentication.standardizedDomainParameter[this.standardizedDomainParameter];
142 				if (!curveoid) {
143 					throw new GPError("ChipAuthenticationPublicKeyInfo", GPError.INVALID_DATA, 0, "Standardized domain parameter " + this.standardizedDomainParameter + " is unknown");
144 				}
145 				this.domainParameter = new Key();
146 				this.domainParameter.setComponent(Key.ECC_CURVE_OID, new ByteString(curveoid, OID));
147 			} else {
148 				this.domainParameter = ECCUtils.decodeECParameters(t.get(1));
149 			}
150 		} else {
151 			this.domainParameter = new Key();
152 			this.domainParameter.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID));
153 		}
154 
155 		if (i < tlv.elements) {
156 			var t = tlv.get(i++);
157 			assert(t.tag == ASN1.INTEGER);
158 			this.keyId = t.value.toSigned();
159 		}
160 	}
161 }
162 
163 if (typeof(exports) != "undefined")
164 	exports.ChipAuthenticationDomainParameterInfo = ChipAuthenticationDomainParameterInfo;
165 
166 
167 /**
168  * Convert object to TLV structure
169  *
170  * @return the TLV structure
171  * @type ASN1
172  */
173 ChipAuthenticationDomainParameterInfo.prototype.toTLV = function() {
174 	var t = new ASN1(ASN1.SEQUENCE);
175 
176 	t.add(new ASN1(ASN1.OBJECT_IDENTIFIER, this.protocol));
177 
178 	var c = new ASN1(ASN1.SEQUENCE);
179 	if (this.standardizedDomainParameter) {
180 		c.add(new ASN1(ASN1.OBJECT_IDENTIFIER, new ByteString("standardizedDomainParameter", OID)));
181 		c.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(this.standardizedDomainParameter)));
182 	} else {
183 
184 	}
185 	t.add(c);
186 
187 	if (typeof(this.keyId) != "undefined") {
188 		t.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(this.keyId)));
189 	}
190 	return t;
191 }
192 
193 
194 
195 ChipAuthenticationDomainParameterInfo.prototype.toString = function() {
196 	return "ChipAuthenticationDomainParameterInfo(protocol=" + this.protocol + ", keyId=" + this.keyId + ")";
197 }
198 
199 
200 
201 /**
202  * Create a ChipAuthenticationPublicKeyInfo object
203  *
204  * @class <p>This class encodes and decodes ChipAuthenticationPublicKeyInfo objects.</p>
205  * <p>The class implements the following ASN.1 syntax:</p>
206  * <pre>
207  *	ChipAuthenticationPublicKeyInfo ::= SEQUENCE {
208  *		protocol OBJECT IDENTIFIER(id-PK-DH | id-PK-ECDH),
209  *		chipAuthenticationPublicKey SubjectPublicKeyInfo,
210  *		keyId INTEGER OPTIONAL
211  *	}
212  *
213  *	SubjectPublicKeyInfo ::= SEQUENCE {
214  *		algorithm  AlgorithmIdentifier,
215  *		subjectPublicKey BIT STRING
216  *	}
217  *
218  *	AlgorithmIdentifier ::= SEQUENCE {
219  *		algorithm  OBJECT IDENTIFIER,
220  *		parameters ANY DEFINED BY algorithm OPTIONAL
221  *	}
222  * </pre>
223  * @constructor
224  * @param {ASN1} the optional tlv structure to initialize the object
225  */
226 function ChipAuthenticationPublicKeyInfo(tlv) {
227 	if (tlv && (tlv instanceof ASN1)) {
228 		assert(tlv.isconstructed);
229 		assert(tlv.elements >= 2);
230 
231 		var i = 0;
232 		var t = tlv.get(i++);
233 		assert(t.tag == ASN1.OBJECT_IDENTIFIER);		// protocol
234 		this.protocol = t.value;
235 
236 		var t = tlv.get(i++);
237 		assert(t.tag == ASN1.SEQUENCE);					// Subject public key info
238 		assert(t.elements == 2);
239 
240 		var algo = t.get(0);
241 		assert(algo.tag == ASN1.SEQUENCE);				// Algorithm Identifier
242 		assert(algo.elements == 2);
243 
244 		var oid = algo.get(0);							// algorithm
245 		assert(oid.tag == ASN1.OBJECT_IDENTIFIER);
246 		this.algorithm = oid.value;
247 
248 		if (oid.value.equals(new ByteString("standardizedDomainParameter", OID))) {
249 			this.standardizedDomainParameter = algo.get(1).value.toUnsigned();
250 			var curveoid = ChipAuthentication.standardizedDomainParameter[this.standardizedDomainParameter];
251 			if (!curveoid) {
252 				throw new GPError("ChipAuthenticationPublicKeyInfo", GPError.INVALID_DATA, 0, "Standardized domain parameter " + this.standardizedDomainParameter + " is unknown");
253 			}
254 			this.domainParameter = new Key();
255 			this.domainParameter.setComponent(Key.ECC_CURVE_OID, new ByteString(curveoid, OID));
256 		} else {
257 			this.domainParameter = ECCUtils.decodeECParameters(algo.get(1));
258 		}
259 
260 		var puk = t.get(1);
261 		assert(puk.tag == ASN1.BIT_STRING);
262 		this.publicKey = puk.value.bytes(1);
263 
264 		if (i < tlv.elements) {
265 			var t = tlv.get(i++);
266 			assert(t.tag == ASN1.INTEGER);
267 			this.keyId = t.value.toSigned();
268 		}
269 	}
270 }
271 
272 if (typeof(exports) != "undefined")
273 	exports.ChipAuthenticationPublicKeyInfo = ChipAuthenticationPublicKeyInfo;
274 
275 
276 
277 /**
278  * Removes leading zeros and prepends a single '00' to ByteStrings which have the most significant bit set.
279  *
280  * This prevent interpretation of the integer representation if converted into
281  * a signed ASN1 INTEGER.
282  *
283  * @param {ByteString} value the value to convert
284  * @return the converted value
285  * @type ByteString
286  */
287 ChipAuthenticationPublicKeyInfo.convertUnsignedInteger = function(value) {
288 	assert(value.length > 0);
289 
290 	var i = 0;
291 	for (var i = 0; (i < value.length - 1) && (value.byteAt(i) == 0); i++);
292 
293 	if (value.byteAt(i) >= 0x80) {
294 		value = (new ByteString("00", HEX)).concat(value.bytes(i));
295 	} else {
296 		value = value.bytes(i);
297 	}
298 
299 	return value;
300 }
301 
302 
303 
304 /**
305  * Creates the EC Public Key as subjectPublicKeyInfo TLV structure object.
306  *
307  * <p>The structure is defined as:</p>
308  * <pre>
309  *	SubjectPublicKeyInfo  ::=  SEQUENCE  {
310  *		algorithm            AlgorithmIdentifier,
311  *		subjectPublicKey     BIT STRING  }
312  *
313  *	AlgorithmIdentifier  ::=  SEQUENCE  {
314  *		algorithm               OBJECT IDENTIFIER,
315  *		parameters              ANY DEFINED BY algorithm OPTIONAL  }
316  *
317  *	id-ecPublicKey OBJECT IDENTIFIER ::= {
318  *		iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
319  *
320  *	ECParameters ::= CHOICE {
321  *		namedCurve         OBJECT IDENTIFIER,
322  *		implicitCurve      NULL,
323  *		specifiedCurve     SpecifiedECDomain }
324  * </pre>
325  * @return the subjectPublicKey TLV structure
326  * @type ASN1
327  */
328 ChipAuthenticationPublicKeyInfo.createECSubjectPublicKeyInfo = function(publicKey, encodeECDomainParameter) {
329 	var t = new ASN1("subjectPublicKeyInfo", ASN1.SEQUENCE);
330 
331 	var algorithm = new ASN1("algorithm", ASN1.SEQUENCE,
332 			new ASN1("algorithm", ASN1.OBJECT_IDENTIFIER, new ByteString("1.2.840.10045.2.1", OID))
333 		);
334 
335 	if (encodeECDomainParameter) {
336 		if (publicKey.getComponent(Key.ECC_P)) {		// Make sure curve components are available if only curve oid is defined
337 			publicKey.setComponent(Key.ECC_CURVE_OID, publicKey.getComponent(Key.ECC_CURVE_OID));
338 		}
339 		groupCAPuk.setComponent(Key.ECC_CURVE_OID, groupCAPuk.getComponent(Key.ECC_CURVE_OID));
340 		var ecParameter =
341 			new ASN1("ecParameters", ASN1.SEQUENCE,
342 				new ASN1("version", ASN1.INTEGER, new ByteString("01", HEX)),
343 				new ASN1("fieldID", ASN1.SEQUENCE,
344 					new ASN1("fieldType", ASN1.OBJECT_IDENTIFIER, new ByteString("prime-field", OID)),
345 					new ASN1("prime", ASN1.INTEGER,
346 						ChipAuthenticationPublicKeyInfo.convertUnsignedInteger(publicKey.getComponent(Key.ECC_P)))
347 				),
348 				new ASN1("curve", ASN1.SEQUENCE,
349 					new ASN1("a", ASN1.OCTET_STRING,
350 						ChipAuthenticationPublicKeyInfo.convertUnsignedInteger(publicKey.getComponent(Key.ECC_A))),
351 					new ASN1("b", ASN1.OCTET_STRING,
352 						ChipAuthenticationPublicKeyInfo.convertUnsignedInteger(publicKey.getComponent(Key.ECC_B)))
353 				),
354 				new ASN1("base", ASN1.OCTET_STRING,
355 						(new ByteString("04", HEX)).concat(publicKey.getComponent(Key.ECC_GX)).concat(publicKey.getComponent(Key.ECC_GY))),
356 				new ASN1("order", ASN1.INTEGER,
357 					ChipAuthenticationPublicKeyInfo.convertUnsignedInteger(publicKey.getComponent(Key.ECC_N)))
358 			);
359 
360 		var cofactor = publicKey.getComponent(Key.ECC_H);
361 		var i = 0;
362 		for (; (i < cofactor.length) && (cofactor.byteAt(i) == 0); i++);
363 		if (i < cofactor.length) {
364 			ecParameter.add(new ASN1("cofactor", ASN1.INTEGER, cofactor.bytes(i)));
365 		}
366 		algorithm.add(ecParameter);
367 	} else {
368 		algorithm.add(new ASN1("parameters", ASN1.OBJECT_IDENTIFIER, publicKey.getComponent(Key.ECC_CURVE_OID)));
369 	}
370 
371 	t.add(algorithm);
372 
373 	// Prefix a 00 to form correct bitstring
374 	// Prefix a 04 to indicate uncompressed format
375 	var keybin = new ByteString("0004", HEX);
376 	keybin = keybin.concat(publicKey.getComponent(Key.ECC_QX));
377 	keybin = keybin.concat(publicKey.getComponent(Key.ECC_QY));
378 	t.add(new ASN1("subjectPublicKey", ASN1.BIT_STRING, keybin));
379 
380 	return t;
381 }
382 
383 
384 
385 /**
386  * Convert object to TLV structure
387  *
388  * @return the TLV structure
389  * @type ASN1
390  */
391 ChipAuthenticationPublicKeyInfo.prototype.toTLV = function() {
392 	var t = new ASN1("chipAuthenticationPublicKeyInfo", ASN1.SEQUENCE);
393 
394 	t.add(new ASN1("protocol", ASN1.OBJECT_IDENTIFIER, this.protocol));
395 
396 	if (this.algorithm.equals(new ByteString("id-ecPublicKey", OID))) {
397 		var spki = ChipAuthenticationPublicKeyInfo.createECSubjectPublicKeyInfo(this.publicKey, true);
398 	} else {
399 		var algoid = new ASN1("algorithm", ASN1.SEQUENCE, new ASN1("algorithm", ASN1.OBJECT_IDENTIFIER, this.algorithm));
400 		if (this.algorithm.equals(new ByteString("standardizedDomainParameter", OID))) {
401 			algoid.add(new ASN1("standardizedDomainParameter", ASN1.INTEGER, ByteString.valueOf(this.standardizedDomainParameter)));
402 		}
403 
404 		var spki = new ASN1("subjectPublicKey", ASN1.SEQUENCE);
405 		spki.add(algoid);
406 		var puk = (new ByteString("0004", HEX)).concat(this.publicKey.getComponent(Key.ECC_QX)).concat(this.publicKey.getComponent(Key.ECC_QY));
407 		spki.add(new ASN1("publicKey", ASN1.BIT_STRING, puk));
408 
409 	}
410 
411 	t.add(spki);
412 
413 	if (typeof(this.keyId) != "undefined") {
414 		t.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(this.keyId)));
415 	}
416 	return t;
417 }
418 
419 
420 
421 ChipAuthenticationPublicKeyInfo.prototype.toString = function() {
422 	return "ChipAuthenticationPublicKeyInfo(protocol=" + this.protocol + ", algorithm=" + this.algorithm + ", publicKey=" + this.publicKey + ", keyId=" + this.keyId + ")";
423 }
424 
425 
426 
427 /**
428  * Create a ChipAuthentication protocol object
429  *
430  * @class This class implements the ChipAuthentication protocol
431  *
432  * @constructor
433  *
434  * @param {Crypto} crypto the crypto provider
435  * @param {ByteString} algo the algorithm OID
436  * @param {Key} domparam the key object holding ECC domain parameter
437  */
438 function ChipAuthentication(crypto, algo, domparam) {
439 	this.crypto = crypto;
440 	this.algo = algo;
441 	this.domparam = domparam;
442 	this.includeDPinAuthToken = false;
443 	this.noPadding = false;
444 
445 //	print(ECCUtils.ECParametersToString(domparam));
446 }
447 
448 if (typeof(exports) != "undefined")
449 	exports.ChipAuthentication = ChipAuthentication;
450 
451 
452 ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC = (new ByteString("id-CA-ECDH-3DES-CBC-CBC", OID));
453 ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_128 = (new ByteString("id-CA-ECDH-AES-CBC-CMAC-128", OID));
454 ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_192 = (new ByteString("id-CA-ECDH-AES-CBC-CMAC-192", OID));
455 ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_256 = (new ByteString("id-CA-ECDH-AES-CBC-CMAC-256", OID));
456 
457 ChipAuthentication.standardizedDomainParameter = [];
458 ChipAuthentication.standardizedDomainParameter[8] = "secp192r1";
459 ChipAuthentication.standardizedDomainParameter[9] = "brainpoolP192r1";
460 ChipAuthentication.standardizedDomainParameter[10] = "secp224r1";
461 ChipAuthentication.standardizedDomainParameter[11] = "brainpoolP224r1";
462 ChipAuthentication.standardizedDomainParameter[12] = "secp256r1";
463 ChipAuthentication.standardizedDomainParameter[13] = "brainpoolP256r1";
464 ChipAuthentication.standardizedDomainParameter[14] = "brainpoolP320r1";
465 ChipAuthentication.standardizedDomainParameter[15] = "secp384r1";
466 ChipAuthentication.standardizedDomainParameter[16] = "brainpoolP384r1";
467 ChipAuthentication.standardizedDomainParameter[17] = "brainpoolP512r1";
468 ChipAuthentication.standardizedDomainParameter[18] = "secp521r1";
469 
470 
471 /**
472  * Derive key from input parameter, counter and optional nonce
473  *
474  * @param {ByteString} input the first part of the hash input
475  * @param {Number} counter the counter value
476  * @param {nonce} the optional nonce inserted between the input and the counter
477  * @return the key object
478  * @type Key
479  */
480 ChipAuthentication.prototype.deriveKey = function(input, counter, nonce) {
481 	if (typeof(nonce) != "undefined") {
482 		input = input.concat(nonce);
483 	}
484 
485 	var bb = new ByteBuffer("000000", HEX);
486 	bb.append(counter);
487 
488 	input = input.concat(bb.toByteString());
489 
490 	var key = new Key();
491 
492 	if (this.algo.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) {
493 		var digest = this.crypto.digest(Crypto.SHA_1, input);
494 		key.setComponent(Key.DES, digest.left(16));
495 	} else if (this.algo.equals(ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_128)) {
496 		var digest = this.crypto.digest(Crypto.SHA_1, input);
497 		key.setComponent(Key.AES, digest.left(16));
498 	} else if (this.algo.equals(ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_192)) {
499 		var digest = this.crypto.digest(Crypto.SHA_256, input);
500 		key.setComponent(Key.AES, digest.left(24));
501 	} else if (this.algo.equals(ChipAuthentication.id_CA_ECDH_AES_CBC_CMAC_256)) {
502 		var digest = this.crypto.digest(Crypto.SHA_256, input);
503 		key.setComponent(Key.AES, digest);
504 	} else {
505 		throw new GPError("ChipAuthentication", GPError.INVALID_MECH, 0, "Algorithm not supported");
506 	}
507 
508 	return key;
509 }
510 
511 
512 
513 /**
514  * Generate ephemeral key pair
515  */
516 ChipAuthentication.prototype.generateEphemeralCAKeyPair = function() {
517 	this.prkCA = new Key(this.domparam);
518 	this.prkCA.setType(Key.PRIVATE);
519 
520 	this.pukCA = new Key(this.domparam);
521 	this.pukCA.setType(Key.PUBLIC);
522 
523 	this.crypto.generateKeyPair(Crypto.EC, this.pukCA, this.prkCA);
524 }
525 
526 
527 
528 /**
529  * Set chip authentication keys
530  *
531  * @param {Key} prk the private key
532  * @param {Key} puk the public key
533  */
534 ChipAuthentication.prototype.setKeyPair = function(prk, puk) {
535 	this.prkCA = prk;
536 	this.pukCA = puk;
537 }
538 
539 
540 
541 /**
542  * Returns the x coordinate of the public key
543  *
544  * @return the encoded public key
545  * @type ByteString
546  */
547 ChipAuthentication.prototype.getCompressedPublicKey = function() {
548 	return (this.pukCA.getComponent(Key.ECC_QX));
549 }
550 
551 
552 
553 /**
554  * Returns the ephemeral public key
555  *
556  * @return the encoded public key
557  * @type ByteString
558  */
559 ChipAuthentication.prototype.getEphemeralPublicKey = function() {
560 	var ecpk = new ByteString("04", HEX);
561 	ecpk = ecpk.concat(this.pukCA.getComponent(Key.ECC_QX));
562 	ecpk = ecpk.concat(this.pukCA.getComponent(Key.ECC_QY));
563 	return ecpk;
564 }
565 
566 
567 
568 /**
569  * Decodes the ephemeral public key
570  *
571  * @return the decoded public key
572  * @type ByteString
573  */
574 ChipAuthentication.prototype.decodeEphemeralPublicKey = function(encodedKey) {
575 	if (encodedKey.byteAt(0) != 0x04) {
576 		throw new GPError("ChipAuthentication", GPError.INVALID_DATA, 0, "Terminal ephemeral public key does not start with '04'");
577 	}
578 
579 	var key = new Key(this.domparam);
580 	var l = key.getSize() >> 3;
581 
582 	if (encodedKey.length != 1 + (l << 1)) {
583 		throw new GPError("ChipAuthentication", GPError.INVALID_DATA, 0, "Length of terminal ephemeral public key does not match curve");
584 	}
585 
586 	key.setType(Key.PUBLIC);
587 	key.setComponent(Key.ECC_QX, encodedKey.bytes(1, l));
588 	key.setComponent(Key.ECC_QY, encodedKey.bytes(1 + l, l));
589 	return key;
590 }
591 
592 
593 
594 /**
595  * Performs the mapping operation with mapping data from the other side
596  *
597  * @param {ByteString} publicKey the public key in encoded format
598  */
599 ChipAuthentication.prototype.performKeyAgreement = function(publicKey, nonce) {
600 	if (publicKey.byteAt(0) != 0x04)
601 		throw new GPError("ChipAuthentication", GPError.INVALID_DATA, 0, "Public key must start with '04'");
602 
603 	if ((nonce != undefined) && !(nonce instanceof ByteString))
604 		throw new GPError("ChipAuthentication", GPError.INVALID_TYPE, 0, "nonce must be of type ByteString");
605 
606 	var l = (publicKey.length - 1) >> 1;
607 	if (l != this.prkCA.getComponent(Key.ECC_P).length) {
608 		throw new GPError("ChipAuthentication", GPError.INVALID_DATA, 0, "Public key size does not match private key size");
609 	}
610 
611 	this.otherPuK = new Key(this.domparam);
612 	this.otherPuK.setComponent(Key.ECC_QX, publicKey.bytes(    1, l));
613 	this.otherPuK.setComponent(Key.ECC_QY, publicKey.bytes(l + 1, l));
614 
615 	var k = this.crypto.decrypt(this.prkCA, Crypto.ECDH, publicKey.bytes(1));
616 //	GPSystem.trace("Shared Secret K:");
617 //	GPSystem.trace(k);
618 	this.kenc = this.deriveKey(k, 1, nonce);
619 	this.kmac = this.deriveKey(k, 2, nonce);
620 }
621 
622 
623 
624 /**
625  * Calculate and verify the authentication token over the public key received from
626  * the other side
627  *
628  * @param {ByteString} the MAC over the authentication data
629  * @return true if the MAC is valid
630  * @type Boolean
631  */
632 ChipAuthentication.prototype.verifyAuthenticationToken = function(authToken) {
633 	var t = ChipAuthentication.encodePublicKey(this.algo.toString(OID), this.pukCA, this.includeDPinAuthToken);
634 //	GPSystem.trace("Authentication Token for verification:");
635 //	GPSystem.trace(t);
636 
637 	if (this.algo.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) {
638 		if (this.noPadding) {
639 			var inp = t.getBytes();
640 		} else {
641 			var inp = t.getBytes().pad(Crypto.ISO9797_METHOD_2);
642 		}
643 		var at = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, inp);
644 		return at.equals(authToken);
645 	} else {
646 		var at = this.crypto.sign(this.kmac, Crypto.AES_CMAC, t.getBytes());
647 		return at.left(8).equals(authToken);
648 	}
649 }
650 
651 
652 
653 /**
654  * Calculate the authentication token over the public key received from
655  * the other side
656  *
657  * @param {ByteString} the MAC over the authentication data
658  * @return true if the MAC is valid
659  * @type Boolean
660  */
661 ChipAuthentication.prototype.calculateAuthenticationToken = function() {
662 	var t = ChipAuthentication.encodePublicKey(this.algo.toString(OID), this.otherPuK, this.includeDPinAuthToken);
663 //	GPSystem.trace("Authentication Token for signing:");
664 //	GPSystem.trace(t);
665 
666 	if (this.algo.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) {
667 		if (this.noPadding) {
668 			var inp = t.getBytes();
669 		} else {
670 			var inp = t.getBytes().pad(Crypto.ISO9797_METHOD_2);
671 		}
672 		var at = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, inp);
673 	} else {
674 		var at = this.crypto.sign(this.kmac, Crypto.AES_CMAC, t.getBytes()).left(8);
675 	}
676 	return at;
677 }
678 
679 
680 
681 /**
682  * Strips leading zeros of a ByteString
683  *
684  * @param {ByteString} value the ByteString value
685  * @return the stripped ByteString object, may be an empty ByteString
686  * @type ByteString
687  */
688 ChipAuthentication.stripLeadingZeros = function(value) {
689 	var i = 0;
690 	for (; (i < value.length) && (value.byteAt(i) == 0); i++);
691 
692 	return value.right(value.length - i);
693 }
694 
695 
696 
697 /**
698  * Encode an ECC public key in the format defined by the EAC 2.0 specification
699  *
700  * @param {String} oid the object identifier to encode
701  * @param {Key} key the EC public key
702  * @param {Boolean} withDP true to encode domain parameter as well
703  */
704 ChipAuthentication.encodePublicKey = function(oid, key, withDP) {
705 
706 	var t = new ASN1("ecPublicKey", 0x7F49);
707 	t.add(new ASN1("objectIdentifier", ASN1.OBJECT_IDENTIFIER, new ByteString(oid, OID)));
708 	if (withDP) {
709 		t.add(new ASN1("primeModulus", 0x81, key.getComponent(Key.ECC_P)));
710 		t.add(new ASN1("firstCoefficient", 0x82, key.getComponent(Key.ECC_A)));
711 		t.add(new ASN1("secondCoefficient", 0x83, key.getComponent(Key.ECC_B)));
712 
713 		var point = new ByteString("04", HEX);
714 		point = point.concat(key.getComponent(Key.ECC_GX));
715 		point = point.concat(key.getComponent(Key.ECC_GY));
716 		t.add(new ASN1("basePoint", 0x84, point));
717 
718 		t.add(new ASN1("orderOfTheBasePoint", 0x85, key.getComponent(Key.ECC_N)));
719 	}
720 	var point = new ByteString("04", HEX);
721 	point = point.concat(key.getComponent(Key.ECC_QX));
722 	point = point.concat(key.getComponent(Key.ECC_QY));
723 	t.add(new ASN1("publicPoint", 0x86, point));
724 
725 	if (withDP) {
726 		var cofactor = key.getComponent(Key.ECC_H);
727 		cofactor = ChipAuthentication.stripLeadingZeros(cofactor);
728 
729 		t.add(new ASN1("cofactor", 0x87, cofactor));
730 	}
731 
732 	return t;
733 }
734