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