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