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 Extended Access Control protocol in version 2.0
 25  */
 26 
 27 
 28 load("tools/eccutils.js");
 29 
 30 load("pace.js");
 31 load("chipauthentication.js");
 32 load("restrictedidentification.js");
 33 load("cvcertstore.js");
 34 
 35 
 36 
 37 /**
 38  * Create a protocol object for EAC
 39  *
 40  * @class Class implementing support for Extended Access Control V2
 41  * @constructor
 42  * @param {Crypto} crypto the crypto provider
 43  * @param {Card} card the card object
 44  */
 45 function EAC20(crypto, card) {
 46 	this.crypto = crypto;
 47 	this.card = card;
 48 	this.sm = null;
 49 	this.includeDPinAuthToken = false;		// Standard for PACE version >= 2
 50 
 51 	this.PACEInfos = new Array();
 52 	this.PACEDPs = new Array();
 53 
 54 	this.CAInfos = new Array();
 55 	this.CADPs = new Array();
 56 	this.CAPublicKeys = new Array();
 57 
 58 	this.RIInfos = new Array();
 59 	this.maxRData = 0;
 60 	this.maxCData = 239;		// Used for update binary
 61 	this.useFID = false;		// Use FIDs rather than SFIs
 62 	this.verbose = true;
 63 	this.selectADFwithoutSM = false;	// Send SELECT ADF without SM (for applets)
 64 }
 65 
 66 
 67 /** PACE PWD is the hashed MRZ */
 68 EAC20.ID_MRZ = 1;
 69 /** PACE PWD is the CAN */
 70 EAC20.ID_CAN = 2;
 71 /** PACE PWD is the PIN */
 72 EAC20.ID_PIN = 3;
 73 /** PACE PWD is the PUK */
 74 EAC20.ID_PUK = 4;
 75 
 76 EAC20.AID_LDS = new ByteString("A0000002471001", HEX);
 77 EAC20.AID_eID = new ByteString("E80704007F00070302", HEX);
 78 EAC20.AID_eSign = new ByteString("A000000167455349474E", HEX);
 79 
 80 EAC20.SFI_CVCA = 0x1C;
 81 EAC20.SFI_ChipSecurity = 0x1B;
 82 EAC20.SFI_CardAccess = 0x1C;
 83 EAC20.SFI_CardSecurity = 0x1D;
 84 EAC20.SFI_COM = 0x1E;
 85 
 86 
 87 EAC20.prototype.log = function(str) {
 88 	if (this.verbose) {
 89 		GPSystem.trace(str);
 90 	}
 91 }
 92 
 93 
 94 
 95 /**
 96  * Process a list of security infos from EF.CardInfo, EF.CardSecurity or EF.ChipSecurity
 97  * 
 98  * @param {ASN1} si the security info ASN Sequence
 99  * @param {boolean} fromCardSecurity true if security infos are taken from EF.CardSecurity, EF.ChipSecurity or EF.DG14
100  */
101 EAC20.prototype.processSecurityInfos = function(si, fromCardSecurity) {
102 	this.log("SecurityInfos:");
103 	this.log(si);
104 	
105 	var id_PACE = new ByteString("id-PACE", OID);
106 	var id_PACE_DH_GM = new ByteString("id-PACE-DH-GM", OID);
107 	var id_PACE_ECDH_GM = new ByteString("id-PACE-ECDH-GM", OID);
108 	var id_PACE_DH_IM = new ByteString("id-PACE-DH-IM", OID);
109 	var id_PACE_ECDH_IM = new ByteString("id-PACE-ECDH-IM", OID);
110 	var id_PK_ECDH = new ByteString("id-PK-ECDH", OID);
111 	var id_CA = new ByteString("id-CA", OID);
112 	var id_CA_DH = new ByteString("id-CA-DH", OID);
113 	var id_CA_ECDH = new ByteString("id-CA-ECDH", OID);
114 	var id_TA = new ByteString("id-TA", OID);
115 	var id_PT = new ByteString("id-PT", OID);
116 
117 	for (var i = 0; i < si.elements; i++) {
118 		var o = si.get(i);
119 		assert((o.elements >= 1) && (o.elements <= 3));
120 
121 		var oid = o.get(0);
122 		assert(oid.tag == ASN1.OBJECT_IDENTIFIER);
123 		
124 		if (oid.value.startsWith(id_TA) == id_TA.length) {
125 			this.log("TA : " + o);
126 		} else if (oid.value.startsWith(id_PACE) == id_PACE.length) {
127 			if (oid.value.equals(id_PACE_DH_GM) ||
128 				oid.value.equals(id_PACE_ECDH_GM) ||
129 				oid.value.equals(id_PACE_DH_IM) ||
130 				oid.value.equals(id_PACE_ECDH_GM)) {
131 				this.log("PaceDomainParameterInfo : " + o);
132 				
133 				var pdpi = new PACEDomainParameterInfo(o);
134 				this.log(pdpi);
135 				
136 				var id = pdpi.parameterId;
137 				
138 				if (typeof(id) == "undefined") {
139 					id = 0;
140 				}
141 				
142 				if (!fromCardSecurity && (typeof(this.PACEDPs[id]) != "undefined")) {
143 					throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate parameterId " + id + " for PACEDomainParameter");
144 				}
145 				
146 				this.PACEDPs[id] = pdpi;
147 			} else {
148 				this.log("PaceInfo : " + o);
149 
150 				var pi = new PACEInfo(o);
151 				this.log(pi);
152 				
153 				var id = pi.parameterId;
154 				
155 				if (typeof(id) == "undefined") {
156 					id = 0;
157 				}
158 				
159 				if (pi.version == 1) {
160 					if (!fromCardSecurity && (typeof(this.PACEInfos[id]) != "undefined")) {
161 						throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate parameterId " + id + " for PACEInfo");
162 					}
163 				} else {
164 					id = 0;
165 				}
166 				this.PACEInfos[id] = pi;
167 			}
168 		} else if (oid.value.equals(id_PK_ECDH)) {
169 			this.log("ChipAuthenticationPublicKeyInfo : " + o);
170 
171 			var capki = new ChipAuthenticationPublicKeyInfo(o);
172 			this.log(capki);
173 
174 			var id = capki.keyId;
175 
176 			if (typeof(id) == "undefined") {
177 				this.log("Using default key id 0");
178 				id = 0;
179 			}
180 
181 			if (!fromCardSecurity && (typeof(this.CAPublicKeys[id]) != "undefined")) {
182 				throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate keyId " + id + " for ChipAuthenticationPublicKeyInfo");
183 			}
184 
185 			this.CAPublicKeys[id] = capki;
186 		} else if (oid.value.startsWith(id_CA) == id_CA.length) {
187 			if (oid.value.equals(id_CA_DH) ||
188 				oid.value.equals(id_CA_ECDH)) {
189 				this.log("ChipAuthenticationDomainParameterInfo : " + o);
190 
191 				var cadpi = new ChipAuthenticationDomainParameterInfo(o);
192 				this.log(cadpi);
193 
194 				var id = cadpi.keyId;
195 
196 				if (typeof(id) == "undefined") {
197 					this.log("Using default key id 0");
198 					id = 0;
199 				}
200 
201 				if (!fromCardSecurity && (typeof(this.CADPs[id]) != "undefined")) {
202 					throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate keyId " + id + " for ChipAuthenticationDomainParameter");
203 				}
204 
205 				this.CADPs[id] = cadpi;
206 			} else {
207 				this.log("ChipAuthenticationInfo : " + o);
208 
209 				var cai = new ChipAuthenticationInfo(o);
210 				this.log(cai);
211 
212 				var id = cai.keyId;
213 
214 				if (typeof(id) == "undefined") {
215 					this.log("Using default key id 0");
216 					id = 0;
217 				}
218 
219 				if (!fromCardSecurity && (typeof(this.CAInfos[id]) != "undefined")) {
220 					throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate keyId " + id + " for ChipAuthenticationInfo");
221 				}
222 
223 				this.CAInfos[id] = cai;
224 			}
225 		} else if (oid.value.startsWith(RestrictedIdentification.id_RI) == RestrictedIdentification.id_RI.length) {
226 			if (oid.value.equals(RestrictedIdentification.id_RI_DH) ||
227 				oid.value.equals(RestrictedIdentification.id_RI_ECDH)) {
228 				this.log("RestrictedIdentificationDomainParameterInfo : " + o);
229 
230 				var ridpi = new RestrictedIdentificationDomainParameterInfo(o);
231 				this.log(ridpi);
232 
233 				if (!fromCardSecurity && (typeof(this.RIDP) != "undefined")) {
234 					throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate RestrictedIdentificationDomainParameter");
235 				this.RIDP = ridpi;
236 				}
237 			} else {
238 				this.log("RestrictedIdentificationInfo : " + o);
239 
240 				var rii = new RestrictedIdentificationInfo(o);
241 				this.log(rii);
242 
243 				var id = rii.keyId;
244 				
245 				if (typeof(id) == "undefined") {
246 					this.log("Using default key id 0");
247 					id = 0;
248 				}
249 
250 				if (!fromCardSecurity && (typeof(this.RIInfos[id]) != "undefined")) {
251 					throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Duplicate keyId " + id + " for RestrictedIdentificationInfo");
252 				}
253 
254 				this.RIInfos[id] = rii;
255 			}
256 		} else if (oid.value.equals(id_PT)) {
257 			this.log("PrivilegedTerminalInfo : " + o);
258 			this.processSecurityInfos(o.get(1), fromCardSecurity);
259 		}
260 	}
261 }
262 
263 
264 
265 /**
266  * Select EF using FID and read elementary file
267  *
268  * @param {ByteString} fid 2 byte file identifier
269  * @type ByteString
270  * @return the content of the EF
271  */
272 EAC20.prototype.readEFwithFID = function(fid) {
273 	assert(fid.length == 2, "Length of fid must be 2 bytes");
274 	this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xA4, 0x02, 0x0C, fid, [0x9000]);
275 
276 	var bb = new ByteBuffer();
277 	var offset = 0;
278 	do	{
279 		var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xB0, offset >> 8, offset & 0xFF, this.maxRData);
280 		bb.append(rsp);
281 		offset += rsp.length;
282 	} while ((this.card.SW == 0x9000) && (rsp.length > 0));
283 	
284 	return bb.toByteString();
285 }
286 
287 
288 
289 /**
290  * Select EF using FID and update content
291  *
292  * @param {ByteString} fid 2 byte file identifier
293  * @param {ByteString} data data to be written
294  */
295 EAC20.prototype.updateEFwithFID = function(fid, data) {
296 	assert(fid.length == 2, "Length of fid must be 2 bytes");
297 	this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xA4, 0x02, 0x0C, fid, [0x9000]);
298 
299 	var offset = 0;
300 	while (offset < data.length) {
301 		var len = data.length - offset;
302 		len = this.maxCData < len ? this.maxCData : len;
303 		this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xD6, offset >> 8, offset & 0xFF, data.bytes(offset, len), [0x9000]);
304 		offset += len;
305 	}
306 }
307 
308 
309 
310 /**
311  * Select and read EF using SFI
312  *
313  * @param {Number} short file identifier
314  * @type ByteString
315  * @return the content of the EF
316  */
317 EAC20.prototype.readEFwithSFI = function(sfi) {
318 	assert(typeof(sfi) == "number", "Parameter must be a number");
319 
320 	if (this.useFID) {
321 		var fid = ByteString.valueOf(0x0100 + sfi, 2);
322 		return this.readEFwithFID(fid);
323 	}
324 	
325 	var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xB0, 0x80 | sfi, 0x00, this.maxRData, [0x9000]);
326 
327 	var bb = new ByteBuffer(rsp);
328 	var offset = bb.length;
329 	do	{
330 		var rsp = this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xB0, offset >> 8, offset & 0xFF, this.maxRData);
331 		bb.append(rsp);
332 		offset += rsp.length;
333 	} while ((this.card.SW == 0x9000) && (rsp.length > 0));
334 	
335 	return bb.toByteString();
336 }
337 
338 
339 
340 /**
341  * Select EF using SFI and update content
342  *
343  * @param {Number} short file identifier
344  * @param {ByteString} data data to be written
345  */
346 EAC20.prototype.updateEFwithSFI = function(sfi, data) {
347 	assert(typeof(sfi) == "number", "Parameter must be a number");
348 
349 	if (this.useFID) {
350 		var fid = ByteString.valueOf(0x0100 + sfi, 2);
351 		return this.updateEFwithFID(fid, data);
352 	}
353 
354 	var offset = 0;
355 	var p1 = 0x80 | sfi;
356 	while (offset < data.length) {
357 		var len = data.length - offset;
358 		len = this.maxCData < len ? this.maxCData : len;
359 		this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xD6, p1, offset & 0xFF, data.bytes(offset, len), [0x9000]);
360 		offset += len;
361 		p1 = offset >> 8;
362 	}
363 }
364 
365 
366 
367 /**
368  * Select application DF
369  *
370  * @param {ByteString} aid the application identifier
371  */
372 EAC20.prototype.selectADF = function(aid) {
373 	if (this.selectADFwithoutSM) {
374 		this.card.sendApdu(0x00, 0xA4, 0x04, 0x0C, aid, [0x9000]);
375 	} else {
376 		this.card.sendSecMsgApdu(Card.ALL, 0x00, 0xA4, 0x04, 0x0C, aid, [0x9000]);
377 	}
378 }
379 
380 
381 
382 /**
383  * Select ePass LDS Application
384  */
385 EAC20.prototype.selectLDS = function() {
386 	this.selectADF(EAC20.AID_LDS);
387 }
388 
389 
390 
391 /**
392  * Select eID Application
393  */
394 EAC20.prototype.select_eID = function() {
395 	this.selectADF(EAC20.AID_eID);
396 }
397 
398 
399 
400 /**
401  * Select eSign Application
402  */
403 EAC20.prototype.select_eSign = function() {
404 	this.selectADF(EAC20.AID_eSign);
405 }
406 
407 
408 
409 /**
410  * Read EF.DG14 and process security infos
411  *
412  */
413 EAC20.prototype.readDG14 = function() {
414 	var cibin = this.readEFwithSFI(14);
415 	var citlv = new ASN1(cibin);
416 	this.log(citlv);
417 	
418 	this.processSecurityInfos(citlv.get(0), true);
419 }
420 
421 
422 
423 /**
424  * Read EF.CVCA and process contained CARs
425  *
426  */
427 EAC20.prototype.readCVCA = function() {
428 	var cvcabin = this.readEFwithSFI(EAC20.SFI_CVCA);
429 	assert(cvcabin.byteAt(0) == 0x42);
430 
431 	var cvca = new ASN1(cvcabin);
432 	this.lastCAR = new PublicKeyReference(cvca.value);
433 
434 	if (cvcabin.byteAt(cvca.size) == 0x42) {
435 		var cvca = new ASN1(cvcabin.bytes(cvca.size));
436 		this.previousCAR = new PublicKeyReference(cvca.value);
437 	}
438 }
439 
440 
441 
442 /**
443  * Read EF.CardAccess and process security infos
444  *
445  */
446 EAC20.prototype.readCardAccess = function() {
447 	var cibin = this.readEFwithSFI(EAC20.SFI_CardAccess);
448 	var citlv = new ASN1(cibin);
449 	this.log(citlv);
450 	
451 	this.processSecurityInfos(citlv, false);
452 }
453 
454 // Deprecated
455 EAC20.prototype.readCardInfo = EAC20.prototype.readCardAccess;
456 
457 
458 
459 /**
460  * Read EF.CardSecurity and process security infos
461  */
462 EAC20.prototype.readCardSecurity = function() {
463 	var csbin = this.readEFwithSFI(EAC20.SFI_CardSecurity);
464 	var cstlv = new ASN1(csbin);
465 	this.log("EF.CardSecurity:");
466 	this.log(cstlv);
467 
468 	var cms = new CMSSignedData(csbin);
469 
470 	var certs = cms.getSignedDataCertificates();
471 
472 	this.log("EF.CardSecurity Certificates:");
473 	for (var i = 0; i < certs.length; i++) {
474 		this.log(certs[i]);
475 	}
476 
477 	this.log("DocSigner Signature is " + (cms.isSignerInfoSignatureValid(0) ? "valid" : "not valid"));
478 
479 	var data = cms.getSignedContent();
480 
481 	this.log(data);
482 
483 	var cstlv = new ASN1(data);
484 
485 	this.log(cstlv);
486 	
487 	this.processSecurityInfos(cstlv, true);
488 }
489 
490 
491 
492 /**
493  * Read EF.ChipSecurity and process security infos
494  */
495 EAC20.prototype.readChipSecurity = function() {
496 	var csbin = this.readEFwithSFI(EAC20.SFI_ChipSecurity);
497 	var cstlv = new ASN1(csbin);
498 	this.log("EF.ChipSecurity:");
499 	this.log(cstlv);
500 	
501 	var cms = new CMSSignedData(csbin);
502 
503 	var certs = cms.getSignedDataCertificates();
504 
505 	this.log("EF.ChipSecurity Certificates:");
506 	for (var i = 0; i < certs.length; i++) {
507 		this.log(certs[i]);
508 	}
509 
510 	this.log("DocSigner Signature is " + (cms.isSignerInfoSignatureValid(0) ? "valid" : "not valid"));
511 
512 	var data = cms.getSignedContent();
513 
514 	this.log(data);
515 
516 	var cstlv = new ASN1(data);
517 
518 	this.log(cstlv);
519 
520 	this.processSecurityInfos(cstlv, true);
521 }
522 
523 
524 
525 /**
526  * Return the list of PACEInfo objects
527  *
528  * @return the list of PACEInfo objects read from the card, indexed by the parameterId
529  * @type PACEInfo[]
530  */
531 EAC20.prototype.getPACEInfos = function() {
532 	return this.PACEInfos;
533 }
534 
535 
536 
537 /**
538  * Return the list of PACEDomainParameterInfo objects
539  *
540  * @return the list of PACEDomainParameterInfo objects read from the card, indexed by the parameterId
541  * @type PACEDomainParameterInfo[]
542  */
543 EAC20.prototype.getPACEDomainParameterInfos = function() {
544 	return this.PACEDPs;
545 }
546 
547 
548 
549 /**
550  * Return the list of ChipAuthenticationInfo objects
551  *
552  * @return the list of ChipAuthenticationInfo objects read from the card, indexed by the keyId
553  * @type ChipAuthenticationInfo[]
554  */
555 EAC20.prototype.getCAInfos = function() {
556 	return this.CAInfos;
557 }
558 
559 
560 
561 /**
562  * Return the list of ChipAuthenticationDomainParameterInfo objects
563  *
564  * @return the list of ChipAuthenticationDomainParameterInfo objects read from the card, indexed by the keyId
565  * @type ChipAuthenticationDomainParameterInfo[]
566  */
567 EAC20.prototype.getCADomainParameterInfos = function() {
568 	return this.CADPs;
569 }
570 
571 
572 
573 /**
574  * Return the key id of the chip authentication key
575  *
576  * @return the key id
577  * @type 
578  */
579 EAC20.prototype.getCAKeyId = function(privileged) {
580 	for (var i in this.CAInfos) {		// Locate first entry in list
581 		if (this.CAInfos[i].keyId) {
582 			if (privileged) {
583 				privileged = false;		// Select second key if privileged key requested
584 			} else {
585 				return this.CAInfos[i].keyId;
586 			}
587 		}
588 	}
589 	return 0;
590 }
591 
592 
593 
594 /**
595  * Return the key id of the restricted identification key
596  *
597  * @param {boolean} authOnly return the RI key available after authentication only (to calculate the pseudonym)
598  * @return the key id
599  * @type 
600  */
601 EAC20.prototype.getRIKeyId = function(authOnly) {
602 	for each (var rii in this.RIInfos) {
603 		if (!authOnly == !rii.authorizedOnly) {
604 			return rii.keyId;
605 		}
606 	}
607 	return 0;
608 }
609 
610 
611 
612 /**
613  * Decode document number from 2 or 3 line MRZ
614  *
615  * <p>This method supports a document number in a three line MRZ longer than 10 digits.</p>
616  *
617  * @param {String} mrz the concatenation of the MRZ lines
618  * @type String
619  * @return the document number
620  */
621 EAC20.decodeDocumentNumber = function(mrz) {
622 	if (mrz.length == 88) {			// Two line MRZ
623 		var docno = mrz.substr(44, 10);
624 	} else {						// Three line MRZ
625 		if (mrz.charAt(14) == "<") {
626 			var sep = mrz.indexOf("<", 15);
627 			var docno = mrz.substr(5,9).concat(mrz.substring(15,sep)); 
628 		} else {
629 			var docno = mrz.substr(5, 10);
630 		}
631 	}
632 	return docno;
633 }
634 
635 
636 /**
637  * Calculate the hash over document number, date of birth and date of expiration from 2 or 3 line MRZ
638  *
639  * <pre>
640  * 2 line MRZ of Silver Data Set
641  *   P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<
642  *   L898902C<3UTO6908061F9406236ZE184226B<<<<<14
643  *   '-DocNo--'   '-DoB-' '-DoE-'
644  *
645  * 3 line MRZ of Silver Data Set
646  *   I<UTOL898902C<3<<<<<<<<<<<<<<<
647  *        '-DocNo--'
648  *   6908061F9406236UTO<<<<<<<<<<<1
649  *   '-DoB-' '-DoE-'
650  *   ERIKSON<<ANNA<MARIA<<<<<<<<<<<
651  * </pre>
652  *
653  * @param {String} mrz 2 line or 3 line machine readable zone
654  * @type ByteString
655  * @return the SHA-1 hash over the concatenation of document number, date of birth and date of expiration
656  */
657 EAC20.prototype.hashMRZ = function(mrz) {
658 	var hash_input = new ByteString(EAC20.decodeDocumentNumber(mrz), ASCII);
659 	// Convert to byte string
660 	var strbin = new ByteString(mrz, ASCII);
661 
662 	if (strbin.length == 88) {			// 2 line MRZ
663 		// Extract Date of Birth and Date of Expiration
664 		hash_input = hash_input.concat(strbin.bytes(57, 7));
665 		hash_input = hash_input.concat(strbin.bytes(65, 7));
666 	} else if (strbin.length == 90) {		// 3 line MRZ
667 		// Extract Date of Birth and Date of Expiration
668 		hash_input = hash_input.concat(strbin.bytes(30, 7));
669 		hash_input = hash_input.concat(strbin.bytes(38, 7));
670 	} else {
671 		throw new GPError("EAC20", GPError.INVALID_DATA, strbin.length, "MRZ must be either 88 or 90 character long");
672 	}
673 
674 	this.log("Hash Input : " + hash_input.toString(ASCII));
675 	var mrz_hash = this.crypto.digest(Crypto.SHA_1, hash_input);
676 	this.log("MRZ Hash : " + mrz_hash);
677 	return mrz_hash;
678 }
679 
680 
681 
682 /**
683  * Calculate the Basic Access Control (BAC) key from the MRZ
684  *
685  * @param {String} mrz 2 line or 3 line machine readable zone
686  * @param {Number} keyno Number of key to calculate (1 for Kenc and 2 for Kmac)
687  * @type Key
688  * @returns the key object
689  */
690 EAC20.prototype.calculateBACKey = function(mrz, keyno) {
691 	var mrz_hash = this.hashMRZ(mrz);
692 
693 	// Extract first 16 byte and append 00000001 or 00000002
694 	var bb = new ByteBuffer(mrz_hash.bytes(0, 16));
695 	bb.append(new ByteString("000000", HEX));
696 	bb.append(keyno);
697 
698 	// Hash again to calculate key value
699 	var keyval = this.crypto.digest(Crypto.SHA_1, bb.toByteString());
700 	keyval = keyval.bytes(0, 16);
701 	this.log("Value of Key : " + keyval);
702 	var key = new Key();
703 	key.setComponent(Key.DES, keyval);
704 
705 	return key;
706 }
707 
708 
709 
710 /**
711  * Perform BAC using the provided Kenc and Kmac values.
712  *
713  * @param {Key} kenc the key Kenc
714  * @param {Key} kmac the key Kmac
715  */
716 EAC20.prototype.performBACWithMRZ = function(mrz) {
717 	this.setIDPICC(new ByteString(EAC20.decodeDocumentNumber(mrz), ASCII));
718 
719 	var kenc = this.calculateBACKey(mrz, 1);
720 	var kmac = this.calculateBACKey(mrz, 2);
721 
722 	this.performBAC(kenc, kmac);
723 }
724 
725 
726 
727 /**
728  * Perform BAC using the provided Kenc and Kmac values.
729  *
730  * @param {Key} kenc the key Kenc
731  * @param {Key} kmac the key Kmac
732  */
733 EAC20.prototype.performBAC = function(kenc, kmac) {
734 
735 	// GET CHALLENGE
736 	var rndicc = this.card.sendApdu(0x00, 0x84, 0x00, 0x00, 0x08, [0x9000]);
737 
738 	var rndifd = this.crypto.generateRandom(8);
739 	var kifd = this.crypto.generateRandom(16);
740 
741 	var plain = rndifd.concat(rndicc).concat(kifd);
742 
743 	var cryptogram = this.crypto.encrypt(kenc, Crypto.DES_CBC, plain, new ByteString("0000000000000000", HEX));
744 
745 	var mac = this.crypto.sign(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2));
746 
747 	// EXTERNAL AUTHENTICATE
748 	var autresp = this.card.sendApdu(0x00, 0x82, 0x00, 0x00, cryptogram.concat(mac), 0);
749 	
750 	if (this.card.SW != 0x9000) {
751 		this.log("Mutual authenticate failed with " + this.card.SW.toString(16) + " \"" + this.card.SWMSG + "\". MRZ correct ?");
752 		throw new GPError("EAC20", GPError.CRYPTO_FAILED, 0, "Card did not accept MAC in BAC establishment");
753 	}
754 	
755 	cryptogram = autresp.bytes(0, 32);
756 	mac = autresp.bytes(32, 8);
757 
758 	if (!this.crypto.verify(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2), mac)) {
759 		throw new GPError("EAC20", GPError.CRYPTO_FAILED, 0, "Card MAC did not verify correctly");
760 	}
761 
762 	plain = this.crypto.decrypt(kenc, Crypto.DES_CBC, cryptogram, new ByteString("0000000000000000", HEX));
763 
764 	if (!plain.bytes(0, 8).equals(rndicc)) {
765 		throw new GPError("EAC20", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.ICC");
766 	}
767 
768 	if (!plain.bytes(8, 8).equals(rndifd)) {
769 		throw new GPError("EAC20", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.IFD");
770 	}
771 
772 	var kicc = plain.bytes(16, 16);
773 	keyinp = kicc.xor(kifd);
774 
775 	var hashin = keyinp.concat(new ByteString("00000001", HEX));
776 	var kencval = this.crypto.digest(Crypto.SHA_1, hashin);
777 	kencval = kencval.bytes(0, 16);
778 	var kenc = new Key();
779 	kenc.setComponent(Key.DES, kencval);
780 
781 	var hashin = keyinp.concat(new ByteString("00000002", HEX));
782 	var kmacval = this.crypto.digest(Crypto.SHA_1, hashin);
783 	kmacval = kmacval.bytes(0, 16);
784 	var kmac = new Key();
785 	kmac.setComponent(Key.DES, kmacval);
786 
787 	var ssc = rndicc.bytes(4, 4).concat(rndifd.bytes(4, 4));
788 
789 	this.sm = new IsoSecureChannel(this.crypto);
790 	this.sm.setEncKey(kenc);
791 	this.sm.setMacKey(kmac);
792 	this.sm.setSendSequenceCounter(ssc);
793 
794 	this.card.setCredential(this.sm);
795 }
796 
797 
798 
799 /**
800  * Perform PACE using the indicated parameter set, the identified password, the password value and
801  * an optional cardholder authentication template.
802  *
803  * <p>This method supports PACE version 1 and 2. For version 2, parameterId with a value between 0 and 31 denotes
804  * a standardized domain parameter as defined in TR-03110 2.04 or later.</p>
805  *
806  * @param {Number} parameterId the identifier for the PACEInfo and PACEDomainParameterInfo from EF.CardInfo. Use 0 for
807  *                             the default.
808  * @param {Number} pwdid one of EAC20.ID_MRZ, EAC20.ID_CAN, EAC20.ID_PIN, EAC20.ID_PUK
809  * @param {ByteString} pwd the PACE password
810  * @param {ASN1} chat the CHAT data object with tag 7F4C or null
811  */
812 EAC20.prototype.performPACE = function(parameterId, pwdid, pwd, chat) {
813 
814 	var paceinfo = this.PACEInfos[parameterId];
815 	if (typeof(paceinfo) == "undefined") {
816 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown parameterId " + parameterId + " for PACEInfo");
817 	}
818 	
819 	var domainParameter;
820 	
821 	// Used for Chip Authentication
822 	this.includeDPinAuthToken = !(paceinfo.version >= 2);
823 	
824 	if ((paceinfo.version == 1) || ((paceinfo.version == 2) && (parameterId > 31))) {
825 		var pacedp = this.PACEDPs[parameterId];
826 		if (typeof(pacedp) == "undefined") {
827 			throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown parameterId " + parameterId + " for PACEDomainParameterInfo");
828 		}
829 		domainParameter = pacedp.domainParameter;
830 	} else {
831 		domainParameter = PACEDomainParameterInfo.getStandardizedDomainParameter(parameterId);
832 	}
833 	
834 	if (!(pwd instanceof ByteString)) {
835 		throw new GPError("EAC20", GPError.INVALID_TYPE, 0, "Argument pwd must be of type ByteString");
836 	}
837 	
838 	if ((chat != null) && !(chat instanceof ASN1)) {
839 		throw new GPError("EAC20", GPError.INVALID_TYPE, 0, "Argument chat must be of type ASN1");
840 	}
841 
842 	
843 	var pace = new PACE(this.crypto, paceinfo.protocol, domainParameter, paceinfo.version);
844 	pace.setPassword(pwd);
845 
846 	// Manage SE
847 	var crt = new ByteBuffer();
848 	crt.append((new ASN1(0x80, paceinfo.protocol)).getBytes());
849 	crt.append(new ByteString("8301", HEX));
850 	crt.append(pwdid);
851 	if (chat != null) {
852 		crt.append(chat.getBytes());
853 	}
854 
855 	this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0xC1, 0xA4, crt.toByteString(), [0x9000, 0x63C2, 0x63C1 ]);
856 
857 	// General Authenticate
858 	var dado = new ASN1(0x7C);
859 
860 	dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x10, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]);
861 
862 	var dado = new ASN1(dadobin);
863 	assert(dado.tag == 0x7C);
864 	assert(dado.elements == 1);
865 	var encryptedNonceDO = dado.get(0);
866 	assert(encryptedNonceDO.tag == 0x80);
867 	var encryptedNonce = encryptedNonceDO.value;
868 
869 	this.log("Encrypted nonce: " + encryptedNonce);
870 
871 	pace.decryptNonce(encryptedNonce);
872 
873 	var mappingData = pace.getMappingData();
874 
875 	var dado = new ASN1(0x7C, new ASN1(0x81, mappingData));
876 
877 	dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x10, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]);
878 
879 	var dado = new ASN1(dadobin);
880 	assert(dado.tag == 0x7C);
881 	assert(dado.elements == 1);
882 	var mappingDataDO = dado.get(0);
883 	assert(mappingDataDO.tag == 0x82);
884 
885 	pace.performMapping(mappingDataDO.value);
886 
887 	var ephemeralPublicKeyIfd = pace.getEphemeralPublicKey();
888 
889 	var dado = new ASN1(0x7C, new ASN1(0x83, ephemeralPublicKeyIfd));
890 
891 	dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x10, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]);
892 
893 	var dado = new ASN1(dadobin);
894 	assert(dado.tag == 0x7C);
895 	assert(dado.elements == 1);
896 	var ephemeralPublicKeyICC = dado.get(0);
897 	assert(ephemeralPublicKeyICC.tag == 0x84);
898 
899 	this.idPICC = ephemeralPublicKeyICC.value.bytes(1, (ephemeralPublicKeyICC.value.length - 1) >> 1);
900 	this.log("ID_PICC : " + this.idPICC);
901 	
902 	pace.performKeyAgreement(ephemeralPublicKeyICC.value);
903 
904 
905 	var authToken = pace.calculateAuthenticationToken();
906 
907 	var dado = new ASN1(0x7C, new ASN1(0x85, authToken));
908 
909 	dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000, 0x63C2, 0x63C1, 0x63C0, 0x6283 ]);
910 
911 	if ((this.card.SW & 0xFFF0) == 0x63C0) {
912 		throw new GPError("EAC20", GPError.DEVICE_ERROR, this.card.SW, "Authentication failed: " + (this.card.SW & 0xF) + " tries left");
913 	}
914 
915 	if (this.card.SW == 0x6300) {
916 		throw new GPError("EAC20", GPError.DEVICE_ERROR, this.card.SW, "Authentication failed");
917 	}
918 
919 	var dado = new ASN1(dadobin);
920 	this.log(dado);
921 	assert(dado.tag == 0x7C);
922 	assert(dado.elements >= 1);
923 	assert(dado.elements <= 3);
924 	var authTokenDO = dado.get(0);
925 	assert(authTokenDO.tag == 0x86);
926 
927 	if (dado.elements > 1) {
928 		var cardo = dado.get(1);
929 		assert(cardo.tag == 0x87);
930 		this.lastCAR = new PublicKeyReference(cardo.value);
931 	}
932 	
933 	if (dado.elements > 2) {
934 		var cardo = dado.get(2);
935 		assert(cardo.tag == 0x88);
936 		this.previousCAR = new PublicKeyReference(cardo.value);
937 	}
938 
939 	var sm = null;
940 	
941 	if (pace.verifyAuthenticationToken(authTokenDO.value)) {
942 		this.log("Authentication token valid");
943 
944 		var symalgo = pace.getSymmetricAlgorithm();
945 		
946 		if (symalgo == Key.AES) {
947 			sm = new IsoSecureChannel(this.crypto, IsoSecureChannel.SSC_SYNC_ENC_POLICY);
948 			sm.setEncKey(pace.kenc);
949 			sm.setMacKey(pace.kmac);
950 			sm.setMACSendSequenceCounter(new ByteString("00000000000000000000000000000000", HEX));
951 		} else {
952 			sm = new IsoSecureChannel(this.crypto);
953 			sm.setEncKey(pace.kenc);
954 			sm.setMacKey(pace.kmac);
955 			sm.setMACSendSequenceCounter(new ByteString("0000000000000000", HEX));
956 		}
957 		this.card.setCredential(sm);
958 	}
959 	this.sm = sm;
960 }
961 
962 
963 
964 /**
965  * Return the trust anchor's CAR as indicated by the card in the PACE protocol
966  *
967  * @param {boolean} previous, true to return the previous CAR, if any
968  * @return the CertificationAuthorityReference (CAR)
969  * @type PublicKeyReference
970  */
971 EAC20.prototype.getTrustAnchorCAR = function(previous) {
972 	if (previous) {
973 		return this.previousCAR;
974 	} else {
975 		return this.lastCAR;
976 	}
977 }
978 
979 
980 
981 /**
982  * Submit a list of certificates to the card for verification
983  *
984  * @param {CVC[]} cvcchain the list of certificates, starting with link certificates, DVCA certificate and terminal certificate.
985  */
986 EAC20.prototype.verifyCertificateChain = function(cvcchain) {
987 	for (var i = 0; i < cvcchain.length; i++) {
988 		var cvc = cvcchain[i];
989 		
990 		var car = cvc.getCAR().getBytes();
991 		
992 		var pukrefdo = new ASN1(0x83, car);
993 		var pukref = pukrefdo.getBytes();
994 		
995 		this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x81, 0xB6, pukref, [0x9000]);
996 		
997 		// Extract value of 7F21
998 		var tl = new TLVList(cvc.getBytes(), TLV.EMV);
999 		var t = tl.index(0);
1000 		var v = t.getValue();
1001 		
1002 		this.log("Certificate: ");
1003 		this.log(new ASN1(v));
1004 		this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x2A, 0x00, 0xBE, v, [0x9000]);
1005 	}
1006 	
1007 	this.terminalCert = cvcchain[cvcchain.length - 1];
1008 }
1009 
1010 
1011 
1012 /**
1013  * Set the ID_PICC used for terminal authentication in EAC 1.11
1014  *
1015  * @param {ByteString} id
1016  * @param {Key} kmac the key Kmac
1017  */
1018 EAC20.prototype.setIDPICC = function(idPICC) {
1019 	this.idPICC = idPICC;
1020 }
1021 
1022 
1023 
1024 /**
1025  * Perform terminal authentication using a given terminal key
1026  *
1027  * @param {Key} termkey the terminal private key
1028  * @param {ByteString} auxdata auxiliary data (tag '67') to be included in terminal authentication
1029  * @param {Crypto} crypto optional alternative crypto provider (e.g. for key in SmartCard-HSM)
1030  */
1031 EAC20.prototype.performTerminalAuthentication = function(termkey, auxdata, crypto) {
1032 	var signatureInput = this.performTerminalAuthenticationSetup(auxdata);
1033 	
1034 	if (crypto == undefined) {
1035 		var crypto = this.crypto;
1036 	}
1037 	var signature = crypto.sign(termkey, CVC.getSignatureMech(this.terminalCert.getPublicKeyOID()), signatureInput);
1038 
1039 	var keysize = termkey.getSize();
1040 	if (keysize < 0) {
1041 		keysize = termkey.getComponent(Key.ECC_P).length;
1042 	} else {
1043 		keysize >>= 3;
1044 	}
1045 
1046 	signature = ECCUtils.unwrapSignature(signature, keysize);
1047 	this.log("Signature (Encoded):");
1048 	this.log(signature);
1049 
1050 	this.performTerminalAuthenticationFinal(signature);
1051 }
1052 
1053 
1054 
1055 /**
1056  * Prepare terminal authentication by setting the required security environment
1057  *
1058  * @param {ByteString} auxdata auxiliary data (tag '67') to be included in terminal authentication
1059  */
1060 EAC20.prototype.performTerminalAuthenticationSetup = function(auxdata) {
1061 
1062 	var idIFD = this.ca.getCompressedPublicKey();
1063 
1064 	var bb = new ByteBuffer();
1065 
1066 	if (typeof(this.cakeyId) != "undefined") {
1067 		bb.append(new ASN1(0x80, this.terminalCert.getPublicKeyOID()).getBytes());
1068 	}
1069 	
1070 	bb.append(new ASN1(0x83, this.terminalCert.getCHR().getBytes()).getBytes());
1071 	
1072 	if (typeof(this.cakeyId) != "undefined") {
1073 		if (auxdata) {
1074 			bb.append(auxdata);
1075 		}
1076 		bb.append(new ASN1(0x91, idIFD).getBytes());
1077 	}
1078 
1079 	var msedata = bb.toByteString();
1080 	this.log("Manage SE data:");
1081 	this.log(msedata);
1082 	this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x81, 0xA4, msedata, [0x9000]);
1083 
1084 	var challenge = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x84, 0x00, 0x00, 8, [0x9000]);
1085 	
1086 	var bb = new ByteBuffer();
1087 	bb.append(this.idPICC);
1088 	bb.append(challenge);
1089 	bb.append(idIFD);
1090 	if (auxdata) {
1091 		bb.append(auxdata);
1092 	}
1093 	var signatureInput = bb.toByteString();
1094 	this.log("Signature Input:");
1095 	this.log(signatureInput);
1096 	return signatureInput;
1097 }
1098 
1099 
1100 
1101 /**
1102  * Complete terminal authentication by submitting the signature to the card
1103  *
1104  * @param {ByteString} signature the signature as concatenation of r and s
1105  */
1106 EAC20.prototype.performTerminalAuthenticationFinal = function(signature) {
1107 	this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x82, 0x00, 0x00, signature, [0x9000]);
1108 }
1109 
1110 
1111 
1112 /**
1113  * Perform chip authentication in version 1 and establish a secure channel
1114  *
1115  * @return true, if chip authentication was successfull
1116  * @type boolean
1117  */
1118 EAC20.prototype.performChipAuthenticationV1 = function(keyid) {
1119 	this.log("performChipAuthenticationV1() called");
1120 
1121 	if (typeof(keyid) == "undefined") {
1122 		keyid = 0;
1123 	}
1124 
1125 	var cainfo = this.CAInfos[keyid];
1126 	if (typeof(cainfo) == "undefined") {
1127 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyid + " for ChipAuthenticationInfo");
1128 	}
1129 
1130 	var capuk = this.CAPublicKeys[keyid];
1131 	if (typeof(capuk) == "undefined") {
1132 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyid + " for ChipAuthenticationPublicKeyInfo");
1133 	}
1134 
1135 	var domainParameter = capuk.domainParameter;
1136 
1137 	this.ca = new ChipAuthentication(this.crypto, cainfo.protocol, domainParameter);
1138 
1139 	this.ca.generateEphemeralCAKeyPair();
1140 
1141 	var bb = new ByteBuffer();
1142 	bb.append(new ASN1(0x91, this.ca.getEphemeralPublicKey()).getBytes());
1143 
1144 	if (typeof(cainfo.keyId) != "undefined") {
1145 		bb.append(new ByteString("8401", HEX));
1146 		bb.append(cainfo.keyId);
1147 	}
1148 
1149 	var msedata = bb.toByteString();
1150 	this.log("Manage SE data:");
1151 	this.log(msedata);
1152 	this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA6, msedata, [0x9000]);
1153 
1154 	this.ca.performKeyAgreement(capuk.publicKey);
1155 
1156 	this.log("Create DES based secure channel");
1157 	var sm = new IsoSecureChannel(this.crypto);
1158 	sm.setEncKey(this.ca.kenc);
1159 	sm.setMacKey(this.ca.kmac);
1160 	sm.setSendSequenceCounter(new ByteString("0000000000000000", HEX));
1161 
1162 	this.card.setCredential(sm);
1163 	this.sm = sm;
1164 }
1165 
1166 
1167 
1168 /**
1169  * Prepare chip authentication by generating the ephemeral key pair
1170  *
1171  * @param {Number} keyId the key identifier to be used for chip authentication
1172  */
1173 EAC20.prototype.prepareChipAuthentication = function(keyId) {
1174 	this.log("prepareChipAuthentication() called");
1175 
1176 	this.cakeyId = keyId;
1177 
1178 	var cainfo = this.CAInfos[keyId];
1179 	if (typeof(cainfo) == "undefined") {
1180 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyId + " for ChipAuthenticationInfo");
1181 	}
1182 
1183 	var cadp = this.CADPs[keyId];
1184 	if (typeof(cadp) == "undefined") {
1185 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyId + " for ChipAuthenticationDomainParameterInfo");
1186 	}
1187 	this.cadp = cadp;
1188 
1189 	this.ca = new ChipAuthentication(this.crypto, cainfo.protocol, cadp.domainParameter);
1190 
1191 	this.ca.includeDPinAuthToken = this.includeDPinAuthToken;
1192 	this.ca.generateEphemeralCAKeyPair();
1193 }
1194 
1195 
1196 
1197 /**
1198  * Perform chip authentication in version 2 and establish a secure channel
1199  *
1200  * @return true, if chip authentication was successfull
1201  * @type boolean
1202  */
1203 EAC20.prototype.performChipAuthenticationV2 = function() {
1204 	this.log("performChipAuthenticationV2() called");
1205 
1206 	var cainfo = this.CAInfos[this.cakeyId];
1207 	if (typeof(cainfo) == "undefined") {
1208 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + this.cakeyId + " for ChipAuthenticationInfo");
1209 	}
1210 
1211 	if (this.ca.algo != cainfo.protocol) {
1212 		this.log("Special handling for ChipAuthenticationInfo in EF.CardSecurity overwriting ChipAuthenticationInfo in EF.CardAccess");
1213 		this.log("Protocol in EF.CardAccess: " + this.ca.algo);
1214 		this.log("Protocol is EF.CardSecurity: " + cainfo.protocol);
1215 		this.ca.algo = cainfo.protocol;
1216 	}
1217 	
1218 	var capuk = this.CAPublicKeys[this.cakeyId];
1219 	if (typeof(capuk) == "undefined") {
1220 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + this.cakeyId + " for ChipAuthenticationPublicKeyInfo");
1221 	}
1222 
1223 	var bb = new ByteBuffer();
1224 	bb.append(new ASN1(0x80, cainfo.protocol).getBytes());
1225 	
1226 	if (typeof(cainfo.keyId) != "undefined") {
1227 		bb.append(new ByteString("8401", HEX));
1228 		bb.append(cainfo.keyId);
1229 	}
1230 	
1231 	var msedata = bb.toByteString();
1232 	this.log("Manage SE data:");
1233 	this.log(msedata);
1234 	this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]);
1235 	
1236 	var ephemeralPublicKeyIfd = this.ca.getEphemeralPublicKey();
1237 
1238 	var dado = new ASN1(0x7C, new ASN1(0x80, ephemeralPublicKeyIfd));
1239 
1240 	var dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]);
1241 	
1242 	this.log(dadobin);
1243 
1244 	var dado = new ASN1(dadobin);
1245 	assert(dado.tag == 0x7C);
1246 	assert(dado.elements == 2);
1247 	var nonceDO = dado.get(0);
1248 	assert(nonceDO.tag == 0x81);
1249 	var nonce = nonceDO.value;
1250 
1251 	var authTokenDO = dado.get(1);
1252 	assert(authTokenDO.tag == 0x82);
1253 	var authToken = authTokenDO.value;
1254 
1255 	this.ca.performKeyAgreement(capuk.publicKey, nonce);
1256 
1257 	var result = this.ca.verifyAuthenticationToken(authToken);
1258 
1259 	if (result) {
1260 		this.log("Authentication token valid");
1261 
1262 		if (this.ca.algo.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) {
1263 			this.log("Create DES based secure channel");
1264 			var sm = new IsoSecureChannel(this.crypto);
1265 			sm.setEncKey(this.ca.kenc);
1266 			sm.setMacKey(this.ca.kmac);
1267 			sm.setMACSendSequenceCounter(new ByteString("0000000000000000", HEX));
1268 		} else {
1269 			this.log("Create AES based secure channel");
1270 			var sm = new IsoSecureChannel(this.crypto, IsoSecureChannel.SSC_SYNC_ENC_POLICY);
1271 			sm.setEncKey(this.ca.kenc);
1272 			sm.setMacKey(this.ca.kmac);
1273 			sm.setMACSendSequenceCounter(new ByteString("00000000000000000000000000000000", HEX));
1274 		}
1275 		this.card.setCredential(sm);
1276 		this.sm = sm;
1277 	} else {
1278 		this.log("Authentication token invalid");
1279 	}
1280 	
1281 	return result;
1282 }
1283 
1284 
1285 
1286 /**
1287  * Verify authenticated auxiliary data
1288  *
1289  * @param {ByteString} oid the object identifier for the auxiliary data provided during terminal authentication
1290  * @return true, if auxiliary data was verified
1291  * @type boolean
1292  */
1293 EAC20.prototype.verifyAuxiliaryData = function(oid) {
1294 	var o = new ASN1(ASN1.OBJECT_IDENTIFIER, oid);
1295 	this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x20, 0x80, 0x00, o.getBytes(), [0x9000,0x6300]);
1296 	return this.card.SW == 0x9000;
1297 }
1298 
1299 
1300 
1301 /**
1302  * Perform chip authentication and establish a secure channel
1303  *
1304  * @param {Number} keyid the key identifier (only required for ChipAuthentication in version 1)
1305  * @return true, if chip authentication was successfull
1306  * @type boolean
1307  */
1308 EAC20.prototype.performChipAuthentication = function(keyid) {
1309 	if (typeof(this.cakeyId) != "undefined") {
1310 		return this.performChipAuthenticationV2();
1311 	} else {
1312 		return this.performChipAuthenticationV1(keyid);
1313 	}
1314 }
1315 
1316 
1317 
1318 /**
1319  * Perform restricted identification
1320  *
1321  * @param {Number} keyId restricted identification key identifier
1322  * @param {ByteString} sectorPublicKey the sector public key data
1323  * @param {Number} sectorPublicKeyIndex optional argument that allows to select a specific sector public key in the terminal certificate
1324  * @return the sector specific identifier
1325  * @type ByteString
1326  */
1327 EAC20.prototype.performRestrictedIdentification = function(keyId, sectorPublicKey, sectorPublicKeyIndex) {
1328 	var bb = new ByteBuffer();
1329 	bb.append(new ASN1(0x80, new ByteString("id-RI-ECDH-SHA-256", OID)).getBytes());
1330 	
1331 	bb.append(new ByteString("8401", HEX));
1332 	bb.append(keyId);
1333 	
1334 	var msedata = bb.toByteString();
1335 	this.log("Manage SE data:");
1336 	this.log(msedata);
1337 	this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]);
1338 	
1339 	var tag = 0xA0;
1340 	if (sectorPublicKeyIndex) {
1341 		tag = 0xA0 + (sectorPublicKeyIndex << 1);
1342 	}
1343 	
1344 	// ToDo change to sectorPublicKey.value
1345 	var dado = new ASN1(0x7C, new ASN1(tag, sectorPublicKey.bytes(5)));
1346 
1347 	this.log("GA Input: " + dado.getBytes());
1348 	
1349 	var dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]);
1350 	
1351 	this.log(dadobin);
1352 
1353 	var dado = new ASN1(dadobin);
1354 	assert(dado.tag == 0x7C);
1355 	assert(dado.elements == 1);
1356 	var nonceDO = dado.get(0);
1357 	assert((nonceDO.tag == 0x81) || (nonceDO.tag == 0x83));
1358 	var sectorId = nonceDO.value;
1359 
1360 	this.log("Sector specific identifier: " + sectorId);
1361 	return sectorId;
1362 }
1363