      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