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