      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