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 PACE/SAC protocol for both card and terminal
 25  */
 26 
 27 
 28 
 29 /**
 30  * Create a PACEInfo object
 31  *
 32  * @class <p>This class encodes and decodes PACEInfo objects.</p>
 33  * <p>The class implements the following ASN.1 syntax:</p>
 34  * <pre>
 35  * PACEInfo ::= SEQUENCE {
 36  * 		protocol	OBJECT IDENTIFIER,
 37  * 		version INTEGER, -- MUST be 1 or 2
 38  * 		parameterId INTEGER OPTIONAL
 39  * }
 40  * </pre>
 41  * @constructor
 42  * @param {ASN1} the optional tlv structure to initialize the object
 43  */
 44 function PACEInfo(tlv) {
 45 	if (tlv && (tlv instanceof ASN1)) {
 46 		assert(tlv.isconstructed);
 47 		assert(tlv.elements >= 2);
 48 
 49 		var i = 0;
 50 		var t = tlv.get(i++);
 51 		assert(t.tag == ASN1.OBJECT_IDENTIFIER);
 52 		this.protocol = t.value;
 53 
 54 		var t = tlv.get(i++);
 55 		assert(t.tag == ASN1.INTEGER);
 56 		this.version = t.value.toSigned();
 57 
 58 		if (i < tlv.elements) {
 59 			var t = tlv.get(i++);
 60 			assert(t.tag == ASN1.INTEGER);
 61 			this.parameterId = t.value.toSigned();
 62 		}
 63 	}
 64 }
 65 
 66 exports.PACEInfo = PACEInfo;
 67 
 68 
 69 
 70 /**
 71  * Convert object to TLV structure
 72  *
 73  * @return the TLV structure
 74  * @type ASN1
 75  */
 76 PACEInfo.prototype.toTLV = function() {
 77 	var t = new ASN1(ASN1.SEQUENCE);
 78 
 79 	t.add(new ASN1(ASN1.OBJECT_IDENTIFIER, this.protocol));
 80 
 81 	var bb = new ByteBuffer();
 82 	bb.append(this.version);
 83 	t.add(new ASN1(ASN1.INTEGER, bb.toByteString()));
 84 
 85 	if (typeof(this.parameterId) != "undefined") {
 86 		var bb = new ByteBuffer();
 87 		bb.append(this.parameterId);
 88 		t.add(new ASN1(ASN1.INTEGER, bb.toByteString()));
 89 	}
 90 	return t;
 91 }
 92 
 93 
 94 
 95 PACEInfo.prototype.toString = function() {
 96 	return "PACEInfo(protocol=" + this.protocol + ", version=" + this.version + ", parameterId=" + this.parameterId + ")";
 97 }
 98 
 99 
100 
101 /**
102  * Create a PACEDomainParameterInfo object
103  *
104  * @class <p>This class encodes and decodes PACEDomainParameterInfo objects.</p>
105  * <p>The class implements the following ASN.1 syntax:</p>
106  * <pre>
107  *   PACEDomainParameterInfo ::= SEQUENCE {
108  *   	protocol OBJECT IDENTIFIER(id-PACE-DH | id-PACE-ECDH),
109  *   	domainParameter AlgorithmIdentifier,
110  *   	parameterId INTEGER OPTIONAL
111  *   }
112  * </pre>
113  * @constructor
114  * @param {ASN1} the optional tlv structure to initialize the object
115  */
116 function PACEDomainParameterInfo(tlv) {
117 	if (tlv && (tlv instanceof ASN1)) {
118 		assert(tlv.isconstructed);
119 		assert(tlv.elements >= 2);
120 
121 		var i = 0;
122 		var t = tlv.get(i++);
123 		assert(t.tag == ASN1.OBJECT_IDENTIFIER);
124 		this.protocol = t.value;
125 
126 		var t = tlv.get(i++);
127 		assert(t.tag == ASN1.SEQUENCE);
128 
129 		if (t.elements > 0) {
130 			this.domainParameter = ECCUtils.decodeECParameters(t.get(1));
131 		} else {
132 			this.domainParameter = new Key();
133 			this.domainParameter.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID));
134 		}
135 
136 		if (i < tlv.elements) {
137 			var t = tlv.get(i++);
138 			assert(t.tag == ASN1.INTEGER);
139 			this.parameterId = t.value.toSigned();
140 		}
141 
142 	}
143 }
144 
145 exports.PACEDomainParameterInfo = PACEDomainParameterInfo;
146 
147 
148 
149 /**
150  * Convert object to TLV structure
151  *
152  * @return the TLV structure
153  * @type ASN1
154  */
155 PACEDomainParameterInfo.prototype.toTLV = function() {
156 	var t = new ASN1(ASN1.SEQUENCE);
157 
158 	t.add(new ASN1(ASN1.OBJECT_IDENTIFIER, this.protocol));
159 
160 	var c = new ASN1(ASN1.SEQUENCE);
161 	if (this.standardizedDomainParameter) {
162 		c.add(new ASN1(ASN1.OBJECT_IDENTIFIER, new ByteString("standardizedDomainParameter", OID)));
163 		c.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(this.standardizedDomainParameter)));
164 	} else {
165 
166 	}
167 	t.add(c);
168 
169 	if (typeof(this.parameterId) != "undefined") {
170 		var bb = new ByteBuffer();
171 		bb.append(this.parameterId);
172 		t.add(new ASN1(ASN1.INTEGER, bb.toByteString()));
173 	}
174 	return t;
175 }
176 
177 
178 
179 PACEDomainParameterInfo.getStandardizedDomainParameter = function(id) {
180 	var key = new Key();
181 	key.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID));
182 	return key;
183 }
184 
185 
186 
187 PACEDomainParameterInfo.prototype.toString = function() {
188 	return "PACEDomainParameterInfo(protocol=" + this.protocol + ", parameterId=" + this.parameterId + ")";
189 }
190 
191 
192 
193 /**
194  * Create a PACE protocol object
195  *
196  * @class This class implements the PACE protocol
197  *
198  * @constructor
199  *
200  * @param {Crypto} crypto the crypto provider
201  * @param {ByteString} algo the algorithm OID
202  * @param {Key} domainparam the key object holding ECC domain parameter
203  * @param {Number} version protocol version (1 or 2)
204  */
205 function PACE(crypto, algo, domparam, version) {
206 	this.crypto = crypto;
207 	this.algo = algo.toString(OID);
208 	this.domparam = domparam;
209 
210 	if (typeof(version) != "undefined") {
211 		this.version = version;
212 	} else {
213 		this.version = 1;
214 	}
215 
216 	if (this.algo == PACE.id_PACE_ECDH_GM_3DES_CBC_CBC) {
217 		this.symalgo = Key.DES;
218 	} else {
219 		this.symalgo = Key.AES;
220 	}
221 
222 	this.sym = Crypto.AES;
223 }
224 
225 exports.PACE = PACE;
226 
227 
228 
229 /**
230  * Return algorithm type
231  *
232  * @type Number
233  * @returns Either Key.DES or Key.AES
234  */
235 PACE.prototype.getSymmetricAlgorithm = function() {
236 	return this.symalgo;
237 }
238 
239 
240 
241 /**
242  * Derive key from input parameter, counter and optional nonce
243  *
244  * @param {ByteString} input the first part of the hash input
245  * @param {Number} counter the counter value
246  * @param {nonce} the optional nonce inserted between the input and the counter
247  * @return the key object
248  * @type Key
249  */
250 PACE.prototype.deriveKey = function(input, counter, nonce) {
251 	if (typeof(nonce) != "undefined") {
252 		input = input.concat(nonce);
253 	}
254 
255 	var bb = new ByteBuffer("000000", HEX);
256 	bb.append(counter);
257 
258 	input = input.concat(bb.toByteString());
259 
260 	var key = new Key();
261 
262 	if (this.algo == PACE.id_PACE_ECDH_GM_3DES_CBC_CBC) {
263 		var digest = this.crypto.digest(Crypto.SHA_1, input);
264 		key.setComponent(Key.DES, digest.left(16));
265 	} else if (this.algo == PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_128) {
266 		var digest = this.crypto.digest(Crypto.SHA_1, input);
267 		key.setComponent(Key.AES, digest.left(16));
268 	} else if (this.algo == PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_192) {
269 		var digest = this.crypto.digest(Crypto.SHA_256, input);
270 		key.setComponent(Key.AES, digest.left(24));
271 	} else if (this.algo == PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_256) {
272 		var digest = this.crypto.digest(Crypto.SHA_256, input);
273 		key.setComponent(Key.AES, digest);
274 	} else {
275 		throw new GPError("pace", GPError.INVALID_MECH, 0x6A80, "Algorithm not supported");
276 	}
277 	return key;
278 }
279 
280 
281 
282 /**
283  * Set the password and derive the PACE key.
284  * @param {ByteString} pwd the PACE password (Hash Value for MRZ and ASCII string for others)
285  * @return the PACE key.
286  */
287 PACE.prototype.setPassword = function(pwd) {
288 	this.pacekey = this.deriveKey(pwd, 3);
289 }
290 
291 
292 
293 /**
294  * Set the PACE key.
295  * @param {ByteString} key the PACE key
296  * @return the PACE key.
297  */
298 PACE.prototype.setPACEKey = function(key) {
299 	this.pacekey = key;
300 }
301 
302 
303 
304 /**
305  * Generate nonce and encrypt using PACE key.
306  * @return the encrypted nonce
307  * @type ByteString
308  */
309 PACE.prototype.getEncryptedNonce = function() {
310 	this.nonce = this.crypto.generateRandom(16);
311 	if (this.symalgo == Key.DES) {
312 		var encnonce = this.crypto.encrypt(this.pacekey, Crypto.DES_CBC, this.nonce);
313 	} else {
314 		var encnonce = this.crypto.encrypt(this.pacekey, Crypto.AES_ECB, this.nonce);
315 	}
316 	return encnonce;
317 }
318 
319 
320 
321 /**
322  * Decrypt and store nonce using PACE key.
323  *
324  * @param {ByteString} nonce the encrypted nonce
325  */
326 PACE.prototype.decryptNonce = function(encnonce) {
327 	if (this.symalgo == Key.DES) {
328 		this.nonce = this.crypto.decrypt(this.pacekey, Crypto.DES_CBC, encnonce);
329 	} else {
330 		this.nonce = this.crypto.decrypt(this.pacekey, Crypto.AES_ECB, encnonce);
331 	}
332 }
333 
334 
335 
336 /**
337  * Returns true, if the nonce is known.
338  * @return true if the nonce is known
339  * @type Boolean
340  */
341 PACE.prototype.hasNonce = function() {
342 	return (typeof(this.nonce) != "undefined");
343 }
344 
345 
346 
347 /**
348  * Generate ephemeral ECC key pair.
349  *
350  * @param domainParameter the domain parameter for the key pair
351  * @return the ephemeral public key
352  * @type Key
353  */
354 PACE.prototype.generateEphemeralKeyPair = function(domainParameter) {
355 	this.prk = new Key(domainParameter);
356 	this.prk.setType(Key.PRIVATE);
357 
358 	this.puk = new Key(domainParameter);
359 	this.puk.setType(Key.PUBLIC);
360 
361 	this.crypto.generateKeyPair(Crypto.EC, this.puk, this.prk);
362 
363 	return this.puk;
364 }
365 
366 
367 
368 /**
369  * Generates and returns the mapping data for this instance
370  * @return the mapping data
371  * @type ByteString
372  */
373 PACE.prototype.getMappingData = function() {
374 	if (typeof(this.prk) == "undefined") {
375 		this.generateEphemeralKeyPair(this.domparam);
376 	}
377 
378 	var ecpk = new ByteString("04", HEX);
379 	ecpk = ecpk.concat(this.puk.getComponent(Key.ECC_QX));
380 	ecpk = ecpk.concat(this.puk.getComponent(Key.ECC_QY));
381 	return ecpk;
382 }
383 
384 
385 
386 /**
387  * Performs the mapping operation with mapping data from the other side
388  *
389  */
390 PACE.prototype.performMapping = function(mappingData) {
391 	if (mappingData.byteAt(0) != 0x04)
392 		throw new GPError("PACE", GPError.INVALID_DATA, 0x6A80, "Public key must start with '04'");
393 
394 	if (typeof(this.nonce) == "undefined")
395 		throw new GPError("PACE", GPError.INVALID_MECH, 0x6985, "Nonce is not yet defined");
396 
397 	var l = (mappingData.length - 1) >> 1;
398 	if (l != this.prk.getComponent(Key.ECC_P).length) {
399 		throw new GPError("PACE", GPError.INVALID_DATA, 0, "Public key size does not match private key size");
400 	}
401 
402 	var h = this.crypto.decrypt(this.prk, Crypto.ECDHP, mappingData.bytes(1));
403 
404 	var l = h.length >> 1;
405 	var H = new Key(this.domparam);
406 	H.setComponent(Key.ECC_QX, h.bytes(0, l));
407 	H.setComponent(Key.ECC_QY, h.bytes(l, l));
408 
409 	var G = new Key(this.domparam);
410 	// Copy generator point into public key point
411 	G.setComponent(Key.ECC_QX, G.getComponent(Key.ECC_GX));
412 	G.setComponent(Key.ECC_QY, G.getComponent(Key.ECC_GY));
413 
414 	// Calculate G' = s * G + P, where P is initially stored in H and
415 	// G' is finally stored in H.
416 	this.crypto.deriveKey(G, Crypto.EC_MULTIPLY_ADD, this.nonce, H);
417 
418 	// Create new domain parameter with G'
419 	this.ephDomParam = new Key(this.domparam);
420 	this.ephDomParam.setComponent(Key.ECC_GX, H.getComponent(Key.ECC_QX));
421 	this.ephDomParam.setComponent(Key.ECC_GY, H.getComponent(Key.ECC_QY));
422 }
423 
424 
425 
426 /**
427  * Returns the ephemeral public key based on the new domain parameter
428  *
429  * @return the encoded public key
430  * @type ByteString
431  */
432 PACE.prototype.getEphemeralPublicKey = function() {
433 	this.generateEphemeralKeyPair(this.ephDomParam);
434 	var ecpk = new ByteString("04", HEX);
435 	ecpk = ecpk.concat(this.puk.getComponent(Key.ECC_QX));
436 	ecpk = ecpk.concat(this.puk.getComponent(Key.ECC_QY));
437 	return ecpk;
438 }
439 
440 
441 
442 /**
443  * Performs the mapping operation with mapping data from the other side
444  *
445  * @param {ByteString} publicKey the public key in encoded format
446  */
447 PACE.prototype.performKeyAgreement = function(publicKey) {
448 	if (publicKey.byteAt(0) != 0x04)
449 		throw new GPError("PACE", GPError.INVALID_DATA, 0x6A80, "Public key must start with '04'");
450 
451 	if (typeof(this.nonce) == "undefined")
452 		throw new GPError("PACE", GPError.INVALID_MECH, 0x6985, "Nonce is not yet defined");
453 
454 	var l = (publicKey.length - 1) >> 1;
455 	if (l != this.prk.getComponent(Key.ECC_P).length) {
456 		throw new GPError("PACE", GPError.INVALID_DATA, 0, "Public key size does not match private key size");
457 	}
458 
459 	this.otherPuK = new Key(this.ephDomParam);
460 	this.otherPuK.setComponent(Key.ECC_QX, publicKey.bytes(    1, l));
461 	this.otherPuK.setComponent(Key.ECC_QY, publicKey.bytes(l + 1, l));
462 
463 	var k = this.crypto.decrypt(this.prk, Crypto.ECDH, publicKey.bytes(1));
464 	GPSystem.trace("Shared Secret K:");
465 	GPSystem.trace(k);
466 	this.kenc = this.deriveKey(k, 1);
467 	this.kmac = this.deriveKey(k, 2);
468 
469 }
470 
471 
472 
473 /**
474  * Strips leading zeros of a ByteString
475  *
476  * @param {ByteString} value the ByteString value
477  * @return the stripped ByteString object, may be an empty ByteString
478  * @type ByteString
479  */
480 PACE.stripLeadingZeros = function(value) {
481 	var i = 0;
482 	for (; (i < value.length) && (value.byteAt(i) == 0); i++);
483 
484 	return value.right(value.length - i);
485 }
486 
487 
488 
489 /**
490  * Encode an ECC public key in the format defined by the EAC 2.0 specification
491  *
492  * @param {String} oid the object identifier to encode
493  * @param {Key} key the EC public key
494  * @param {Boolean} withDP true to encode domain parameter as well
495  * @type ASN1
496  * @returns the ASN1 encoded public key object
497  */
498 PACE.encodePublicKey = function(oid, key, withDP) {
499 
500 	var t = new ASN1("ecPublicKey", 0x7F49);
501 	t.add(new ASN1("objectIdentifier", ASN1.OBJECT_IDENTIFIER, new ByteString(oid, OID)));
502 	if (withDP) {
503 		t.add(new ASN1("primeModulus", 0x81, key.getComponent(Key.ECC_P)));
504 		t.add(new ASN1("firstCoefficient", 0x82, key.getComponent(Key.ECC_A)));
505 		t.add(new ASN1("secondCoefficient", 0x83, key.getComponent(Key.ECC_B)));
506 
507 		var point = new ByteString("04", HEX);
508 		point = point.concat(key.getComponent(Key.ECC_GX));
509 		point = point.concat(key.getComponent(Key.ECC_GY));
510 		t.add(new ASN1("basePoint", 0x84, point));
511 
512 		t.add(new ASN1("orderOfTheBasePoint", 0x85, key.getComponent(Key.ECC_N)));
513 	}
514 	var point = new ByteString("04", HEX);
515 	point = point.concat(key.getComponent(Key.ECC_QX));
516 	point = point.concat(key.getComponent(Key.ECC_QY));
517 	t.add(new ASN1("publicPoint", 0x86, point));
518 
519 	if (withDP) {
520 		var cofactor = key.getComponent(Key.ECC_H);
521 		cofactor = PACE.stripLeadingZeros(cofactor);
522 
523 		t.add(new ASN1("cofactor", 0x87, cofactor));
524 	}
525 
526 	return t;
527 }
528 
529 
530 
531 /**
532  * Calculate the authentication token over the public key received from
533  * the other side
534  *
535  * @return the MAC over the authentication data
536  * @type ByteString
537  */
538 PACE.prototype.calculateAuthenticationToken = function() {
539 	var t = PACE.encodePublicKey(this.algo, this.otherPuK, (this.version == 1));
540 	GPSystem.trace("Authentication Token:");
541 	GPSystem.trace(t);
542 
543 	if (this.symalgo == Key.DES) {
544 		var inp = t.getBytes().pad(Crypto.ISO9797_METHOD_2);
545 		var at = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, inp);
546 	} else {
547 		var at = this.crypto.sign(this.kmac, Crypto.AES_CMAC, t.getBytes()).left(8);
548 	}
549 
550 	return at;
551 }
552 
553 
554 
555 /**
556  * Calculate and verify the authentication token over the public key received from
557  * the other side
558  *
559  * @param {ByteString} the MAC over the authentication data
560  * @return true if the MAC is valid
561  * @type Boolean
562  */
563 PACE.prototype.verifyAuthenticationToken = function(authToken) {
564 	var t = PACE.encodePublicKey(this.algo, this.puk, (this.version == 1));
565 	GPSystem.trace("Authentication Token:");
566 	GPSystem.trace(t);
567 
568 	if (this.symalgo == Key.DES) {
569 		var inp = t.getBytes().pad(Crypto.ISO9797_METHOD_2);
570 		var at = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, inp);
571 	} else {
572 		var at = this.crypto.sign(this.kmac, Crypto.AES_CMAC, t.getBytes()).left(8);
573 	}
574 
575 	return at.equals(authToken);
576 }
577 
578 
579 
580 /**
581  * Returns true, if the mapping has been performed.
582  * @return true if the mapping has been performed
583  * @type Boolean
584  */
585 PACE.prototype.hasMapping = function() {
586 	return (typeof(this.ephDomParam) != "undefined");
587 }
588 
589 
590 
591 /**
592  * Describe key
593  * @param {Key} the key
594  * @return the string describing the key
595  * @type String
596  */
597 PACE.keyToString = function(key) {
598 	var str = "";
599 	var kval = key.getComponent(Key.AES);
600 	if (kval) {
601 		str += "(AES) " + kval + "\n";
602 	}
603 	var kval = key.getComponent(Key.DES);
604 	if (kval) {
605 		str += "(DES) " + kval + "\n";
606 	}
607 	return str;
608 }
609 
610 
611 
612 /**
613  * Returns a human readable presentation of the current pace state.
614  * return {String} the object information
615  */
616 PACE.prototype.toString = function() {
617 	var str = "Algorithm " + this.algo + "\n";
618 
619 	if (typeof(this.pacekey) != "undefined") {
620 		str += "PACE Key " + PACE.keyToString(this.pacekey);
621 	}
622 
623 	if (typeof(this.nonce) != "undefined") {
624 		str += "Nonce " + this.nonce + "\n";
625 	}
626 
627 	if (typeof(this.ephDomParam) != "undefined") {
628 		str += "Point G' " + this.ephDomParam.getComponent(Key.ECC_GX) + " " + this.ephDomParam.getComponent(Key.ECC_GY) + "\n";
629 	}
630 
631 	if (typeof(this.kenc) != "undefined") {
632 		str += "Kenc " + PACE.keyToString(this.kenc);
633 	}
634 
635 	if (typeof(this.kmac) != "undefined") {
636 		str += "Kmac" + PACE.keyToString(this.kmac);
637 	}
638 
639 	return str;
640 }
641 
642 
643 PACE.bsi_de = "0.4.0.127.0.7";
644 PACE.id_PACE = PACE.bsi_de + ".2.2.4";
645 PACE.id_PACE_ECDH_GM = PACE.id_PACE + ".2";
646 PACE.id_PACE_ECDH_GM_3DES_CBC_CBC     = PACE.id_PACE_ECDH_GM + ".1";
647 PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_128 = PACE.id_PACE_ECDH_GM + ".2";
648 PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_192 = PACE.id_PACE_ECDH_GM + ".3";
649 PACE.id_PACE_ECDH_GM_AES_CBC_CMAC_256 = PACE.id_PACE_ECDH_GM + ".4";
650 
651 PACE.id_roles = PACE.bsi_de + ".3.1.2";
652 PACE.id_IS = PACE.id_roles + ".1";
653 
654