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