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 
1037 	var i = cvcchain.length;
1038 	do {
1039 		i--;
1040 		var cvc = cvcchain[i];
1041 
1042 		var car = cvc.getCAR().getBytes();
1043 
1044 		var pukrefdo = new ASN1(0x83, car);
1045 		var pukref = pukrefdo.getBytes();
1046 
1047 		this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x81, 0xB6, pukref);
1048 	} while ((this.card.SW != 0x9000) && (i > 0));
1049 
1050 	for (; i < cvcchain.length; i++) {
1051 		var cvc = cvcchain[i];
1052 
1053 		var car = cvc.getCAR().getBytes();
1054 
1055 		var pukrefdo = new ASN1(0x83, car);
1056 		var pukref = pukrefdo.getBytes();
1057 
1058 		this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x81, 0xB6, pukref, [0x9000]);
1059 
1060 		// Extract value of 7F21
1061 		var tl = new TLVList(cvc.getBytes(), TLV.EMV);
1062 		var t = tl.index(0);
1063 		var v = t.getValue();
1064 
1065 		this.log("Certificate: ");
1066 		this.log(new ASN1(v));
1067 		this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x2A, 0x00, 0xBE, v, [0x9000]);
1068 	}
1069 
1070 	this.terminalCert = cvcchain[cvcchain.length - 1];
1071 }
1072 
1073 
1074 
1075 /**
1076  * Set the ID_PICC used for terminal authentication in EAC 1.11
1077  *
1078  * @param {ByteString} id
1079  * @param {Key} kmac the key Kmac
1080  */
1081 EAC20.prototype.setIDPICC = function(idPICC) {
1082 	this.idPICC = idPICC;
1083 }
1084 
1085 
1086 
1087 /**
1088  * Perform terminal authentication using a given terminal key
1089  *
1090  * @param {Key} termkey the terminal private key
1091  * @param {ByteString} auxdata auxiliary data (tag '67') to be included in terminal authentication
1092  * @param {Crypto} crypto optional alternative crypto provider (e.g. for key in SmartCard-HSM)
1093  */
1094 EAC20.prototype.performTerminalAuthentication = function(termkey, auxdata, crypto) {
1095 	var signatureInput = this.performTerminalAuthenticationSetup(auxdata);
1096 
1097 	if (crypto == undefined) {
1098 		var crypto = this.crypto;
1099 	}
1100 	var signature = crypto.sign(termkey, CVC.getSignatureMech(this.terminalCert.getPublicKeyOID()), signatureInput);
1101 
1102 	var keysize = termkey.getSize();
1103 	if (keysize < 0) {
1104 		keysize = termkey.getComponent(Key.ECC_P).length;
1105 	} else {
1106 		keysize >>= 3;
1107 	}
1108 
1109 	signature = CVC.unwrapSignature(signature, keysize);
1110 	this.log("Signature (Encoded):");
1111 	this.log(signature);
1112 
1113 	this.performTerminalAuthenticationFinal(signature);
1114 }
1115 
1116 
1117 
1118 /**
1119  * Prepare terminal authentication by setting the required security environment
1120  *
1121  * @param {ByteString} auxdata auxiliary data (tag '67') to be included in terminal authentication
1122  */
1123 EAC20.prototype.performTerminalAuthenticationSetup = function(auxdata) {
1124 
1125 	var idIFD = this.ca.getCompressedPublicKey();
1126 
1127 	var bb = new ByteBuffer();
1128 
1129 	if (typeof(this.cakeyId) != "undefined") {
1130 		bb.append(new ASN1(0x80, this.terminalCert.getPublicKeyOID()).getBytes());
1131 	}
1132 
1133 	bb.append(new ASN1(0x83, this.terminalCert.getCHR().getBytes()).getBytes());
1134 
1135 	if (typeof(this.cakeyId) != "undefined") {
1136 		if (auxdata) {
1137 			bb.append(auxdata);
1138 		}
1139 		bb.append(new ASN1(0x91, idIFD).getBytes());
1140 	}
1141 
1142 	var msedata = bb.toByteString();
1143 	this.log("Manage SE data:");
1144 	this.log(msedata);
1145 	this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x81, 0xA4, msedata, [0x9000]);
1146 
1147 	var challenge = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x84, 0x00, 0x00, 8, [0x9000]);
1148 
1149 	var bb = new ByteBuffer();
1150 	bb.append(this.idPICC);
1151 	bb.append(challenge);
1152 	bb.append(idIFD);
1153 	if (auxdata) {
1154 		bb.append(auxdata);
1155 	}
1156 	var signatureInput = bb.toByteString();
1157 	this.log("Signature Input:");
1158 	this.log(signatureInput);
1159 	return signatureInput;
1160 }
1161 
1162 
1163 
1164 /**
1165  * Complete terminal authentication by submitting the signature to the card
1166  *
1167  * @param {ByteString} signature the signature as concatenation of r and s
1168  */
1169 EAC20.prototype.performTerminalAuthenticationFinal = function(signature) {
1170 	this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x82, 0x00, 0x00, signature, [0x9000]);
1171 }
1172 
1173 
1174 
1175 /**
1176  * Perform chip authentication in version 1 and establish a secure channel
1177  *
1178  * @return true, if chip authentication was successfull
1179  * @type boolean
1180  */
1181 EAC20.prototype.performChipAuthenticationV1 = function(keyid) {
1182 	this.log("performChipAuthenticationV1() called");
1183 
1184 	if (typeof(keyid) == "undefined") {
1185 		keyid = 0;
1186 	}
1187 
1188 	var cainfo = this.CAInfos[keyid];
1189 	if (typeof(cainfo) == "undefined") {
1190 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyid + " for ChipAuthenticationInfo");
1191 	}
1192 
1193 	var capuk = this.CAPublicKeys[keyid];
1194 	if (typeof(capuk) == "undefined") {
1195 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyid + " for ChipAuthenticationPublicKeyInfo");
1196 	}
1197 
1198 	var domainParameter = capuk.domainParameter;
1199 
1200 	this.ca = new ChipAuthentication(this.crypto, cainfo.protocol, domainParameter);
1201 
1202 	this.ca.generateEphemeralCAKeyPair();
1203 
1204 	if (cainfo.protocol.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) {
1205 		var bb = new ByteBuffer();
1206 		bb.append(new ASN1(0x91, this.ca.getEphemeralPublicKey()).getBytes());
1207 
1208 		if (typeof(cainfo.keyId) != "undefined") {
1209 			bb.append(new ByteString("8401", HEX));
1210 			bb.append(cainfo.keyId);
1211 		}
1212 
1213 		var msedata = bb.toByteString();
1214 		this.log("Manage SE data:");
1215 		this.log(msedata);
1216 		this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA6, msedata, [0x9000]);
1217 
1218 		this.ca.performKeyAgreement(capuk.publicKey);
1219 
1220 		this.log("Create DES based secure channel");
1221 		var sm = new IsoSecureChannel(this.crypto);
1222 		sm.setEncKey(this.ca.kenc);
1223 		sm.setMacKey(this.ca.kmac);
1224 		sm.setSendSequenceCounter(new ByteString("0000000000000000", HEX));
1225 	} else {
1226 		var bb = new ByteBuffer();
1227 		bb.append(new ASN1(0x80, cainfo.protocol).getBytes());
1228 
1229 		if (typeof(cainfo.keyId) != "undefined") {
1230 			bb.append(new ByteString("8401", HEX));
1231 			bb.append(cainfo.keyId);
1232 		}
1233 
1234 		var msedata = bb.toByteString();
1235 		this.log("Manage SE data:");
1236 		this.log(msedata);
1237 		this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]);
1238 
1239 		var ephemeralPublicKeyIfd = this.ca.getEphemeralPublicKey();
1240 
1241 		var dado = new ASN1(0x7C, new ASN1(0x80, ephemeralPublicKeyIfd));
1242 
1243 		this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]);
1244 
1245 		this.ca.performKeyAgreement(capuk.publicKey);
1246 
1247 		this.log("Create AES based secure channel");
1248 		var sm = new IsoSecureChannel(this.crypto, IsoSecureChannel.SSC_SYNC_ENC_POLICY);
1249 		sm.setEncKey(this.ca.kenc);
1250 		sm.setMacKey(this.ca.kmac);
1251 		sm.setMACSendSequenceCounter(new ByteString("00000000000000000000000000000000", HEX));
1252 	}
1253 
1254 	this.card.setCredential(sm);
1255 	this.sm = sm;
1256 }
1257 
1258 
1259 
1260 /**
1261  * Prepare chip authentication by generating the ephemeral key pair
1262  *
1263  * @param {Number} keyId the key identifier to be used for chip authentication
1264  */
1265 EAC20.prototype.prepareChipAuthentication = function(keyId) {
1266 	this.log("prepareChipAuthentication() called");
1267 
1268 	this.cakeyId = keyId;
1269 
1270 	var cainfo = this.CAInfos[keyId];
1271 	if (typeof(cainfo) == "undefined") {
1272 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyId + " for ChipAuthenticationInfo");
1273 	}
1274 
1275 	var cadp = this.CADPs[keyId];
1276 	if (typeof(cadp) == "undefined") {
1277 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + keyId + " for ChipAuthenticationDomainParameterInfo");
1278 	}
1279 	this.cadp = cadp;
1280 
1281 	this.ca = new ChipAuthentication(this.crypto, cainfo.protocol, cadp.domainParameter);
1282 
1283 	this.ca.includeDPinAuthToken = this.includeDPinAuthToken;
1284 	this.ca.generateEphemeralCAKeyPair();
1285 }
1286 
1287 
1288 
1289 /**
1290  * Perform chip authentication in version 2 and establish a secure channel
1291  *
1292  * @return true, if chip authentication was successfull
1293  * @type boolean
1294  */
1295 EAC20.prototype.performChipAuthenticationV2 = function() {
1296 	this.log("performChipAuthenticationV2() called");
1297 
1298 	var cainfo = this.CAInfos[this.cakeyId];
1299 	if (typeof(cainfo) == "undefined") {
1300 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + this.cakeyId + " for ChipAuthenticationInfo");
1301 	}
1302 
1303 	if (this.ca.algo != cainfo.protocol) {
1304 		this.log("Special handling for ChipAuthenticationInfo in EF.CardSecurity overwriting ChipAuthenticationInfo in EF.CardAccess");
1305 		this.log("Protocol in EF.CardAccess: " + this.ca.algo);
1306 		this.log("Protocol is EF.CardSecurity: " + cainfo.protocol);
1307 		this.ca.algo = cainfo.protocol;
1308 	}
1309 
1310 	var capuk = this.CAPublicKeys[this.cakeyId];
1311 	if (typeof(capuk) == "undefined") {
1312 		throw new GPError("EAC20", GPError.INVALID_DATA, 0, "Unknown keyId " + this.cakeyId + " for ChipAuthenticationPublicKeyInfo");
1313 	}
1314 
1315 	var bb = new ByteBuffer();
1316 	bb.append(new ASN1(0x80, cainfo.protocol).getBytes());
1317 
1318 	if (typeof(cainfo.keyId) != "undefined") {
1319 		bb.append(new ByteString("8401", HEX));
1320 		bb.append(cainfo.keyId);
1321 	}
1322 
1323 	var msedata = bb.toByteString();
1324 	this.log("Manage SE data:");
1325 	this.log(msedata);
1326 	this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]);
1327 
1328 	var ephemeralPublicKeyIfd = this.ca.getEphemeralPublicKey();
1329 
1330 	var dado = new ASN1(0x7C, new ASN1(0x80, ephemeralPublicKeyIfd));
1331 
1332 	var dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]);
1333 
1334 	this.log(dadobin);
1335 
1336 	var dado = new ASN1(dadobin);
1337 	assert(dado.tag == 0x7C);
1338 	assert(dado.elements == 2);
1339 	var nonceDO = dado.get(0);
1340 	assert(nonceDO.tag == 0x81);
1341 	var nonce = nonceDO.value;
1342 
1343 	var authTokenDO = dado.get(1);
1344 	assert(authTokenDO.tag == 0x82);
1345 	var authToken = authTokenDO.value;
1346 
1347 	this.ca.performKeyAgreement(capuk.publicKey, nonce);
1348 
1349 	var result = this.ca.verifyAuthenticationToken(authToken);
1350 
1351 	if (result) {
1352 		this.log("Authentication token valid");
1353 
1354 		if (this.ca.algo.equals(ChipAuthentication.id_CA_ECDH_3DES_CBC_CBC)) {
1355 			this.log("Create DES based secure channel");
1356 			var sm = new IsoSecureChannel(this.crypto);
1357 			sm.setEncKey(this.ca.kenc);
1358 			sm.setMacKey(this.ca.kmac);
1359 			sm.setMACSendSequenceCounter(new ByteString("0000000000000000", HEX));
1360 		} else {
1361 			this.log("Create AES based secure channel");
1362 			var sm = new IsoSecureChannel(this.crypto, IsoSecureChannel.SSC_SYNC_ENC_POLICY);
1363 			sm.setEncKey(this.ca.kenc);
1364 			sm.setMacKey(this.ca.kmac);
1365 			sm.setMACSendSequenceCounter(new ByteString("00000000000000000000000000000000", HEX));
1366 		}
1367 		this.card.setCredential(sm);
1368 		this.sm = sm;
1369 	} else {
1370 		this.log("Authentication token invalid");
1371 	}
1372 
1373 	return result;
1374 }
1375 
1376 
1377 
1378 /**
1379  * Verify authenticated auxiliary data
1380  *
1381  * @param {ByteString} oid the object identifier for the auxiliary data provided during terminal authentication
1382  * @return true, if auxiliary data was verified
1383  * @type boolean
1384  */
1385 EAC20.prototype.verifyAuxiliaryData = function(oid) {
1386 	var o = new ASN1(ASN1.OBJECT_IDENTIFIER, oid);
1387 	this.card.sendSecMsgApdu(Card.ALL, 0x80, 0x20, 0x80, 0x00, o.getBytes(), [0x9000,0x6300]);
1388 	return this.card.SW == 0x9000;
1389 }
1390 
1391 
1392 
1393 /**
1394  * Perform chip authentication and establish a secure channel
1395  *
1396  * @param {Number} keyid the key identifier (only required for ChipAuthentication in version 1)
1397  * @return true, if chip authentication was successfull
1398  * @type boolean
1399  */
1400 EAC20.prototype.performChipAuthentication = function(keyid) {
1401 	if (typeof(this.cakeyId) != "undefined") {
1402 		return this.performChipAuthenticationV2();
1403 	} else {
1404 		return this.performChipAuthenticationV1(keyid);
1405 	}
1406 }
1407 
1408 
1409 
1410 /**
1411  * Perform restricted identification
1412  *
1413  * @param {Number} keyId restricted identification key identifier
1414  * @param {ByteString} sectorPublicKey the sector public key data
1415  * @param {Number} sectorPublicKeyIndex optional argument that allows to select a specific sector public key in the terminal certificate
1416  * @return the sector specific identifier
1417  * @type ByteString
1418  */
1419 EAC20.prototype.performRestrictedIdentification = function(keyId, sectorPublicKey, sectorPublicKeyIndex) {
1420 	var bb = new ByteBuffer();
1421 	bb.append(new ASN1(0x80, new ByteString("id-RI-ECDH-SHA-256", OID)).getBytes());
1422 
1423 	bb.append(new ByteString("8401", HEX));
1424 	bb.append(keyId);
1425 
1426 	var msedata = bb.toByteString();
1427 	this.log("Manage SE data:");
1428 	this.log(msedata);
1429 	this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO, 0x00, 0x22, 0x41, 0xA4, msedata, [0x9000]);
1430 
1431 	var tag = 0xA0;
1432 	if (sectorPublicKeyIndex) {
1433 		tag = 0xA0 + (sectorPublicKeyIndex << 1);
1434 	}
1435 
1436 	// ToDo change to sectorPublicKey.value
1437 	var dado = new ASN1(0x7C, new ASN1(tag, sectorPublicKey.bytes(5)));
1438 
1439 	this.log("GA Input: " + dado.getBytes());
1440 
1441 	var dadobin = this.card.sendSecMsgApdu(Card.CPRO|Card.CENC|Card.RPRO|Card.RENC, 0x00, 0x86, 0x00, 0x00, dado.getBytes(), 0, [0x9000]);
1442 
1443 	this.log(dadobin);
1444 
1445 	var dado = new ASN1(dadobin);
1446 	assert(dado.tag == 0x7C);
1447 	assert(dado.elements == 1);
1448 	var nonceDO = dado.get(0);
1449 	assert((nonceDO.tag == 0x81) || (nonceDO.tag == 0x83));
1450 	var sectorId = nonceDO.value;
1451 
1452 	this.log("Sector specific identifier: " + sectorId);
1453 	return sectorId;
1454 }
1455