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 Support for card verifiable certificates and certificate requests according to EAC 1.1/2.0
 25  */
 26 
 27 
 28 // Imports
 29 var PublicKeyReference = require('scsh/eac/PublicKeyReference').PublicKeyReference;
 30 
 31 
 32 
 33 /**
 34  * Create a CVC object from a DER encoded ByteString.
 35  *
 36  * @class Class implementing a decoder for card verifiable certificates or requests according to
 37  *        Extended Access Control (EAC) as defined in BSI TR-03110 1.11 and 2.02.
 38  * @constructor
 39  * @param {ByteString} param the DER encoded certificate
 40  * @return
 41  */
 42 function CVC() {
 43 	if (arguments.length > 0) {
 44 		var arg = arguments[0];
 45 		if (arg instanceof ASN1) {
 46 			this.asn = arg;
 47 			this.bin = this.asn.getBytes();
 48 		} else if (arg instanceof ByteString) {
 49 			this.bin = arg;
 50 			this.asn = new ASN1(this.bin);
 51 		} else {
 52 			throw new GPError("CVC", GPError.INVALID_DATA, 0, "Argument must be of type ByteString or ASN1");
 53 		}
 54 		if (this.asn.tag == CVC.TAG_AT) {
 55 			this.body = this.asn.get(0).get(0);
 56 		} else if (this.asn.tag == CVC.TAG_CVC) {
 57 			this.body = this.asn.get(0);
 58 		} else {
 59 			throw new GPError("CVC", GPError.INVALID_DATA, 0, "Argument is neither a CVC or CVC request");
 60 		}
 61 	}
 62 }
 63 
 64 exports.CVC = CVC;
 65 
 66 CVC.crypto = new Crypto();
 67 
 68 
 69 /** Authentication Template */
 70 CVC.TAG_AT = 0x67;
 71 /** CV Certificate */
 72 CVC.TAG_CVC = 0x7F21;
 73 /** Certificate Body */
 74 CVC.TAG_BODY = 0x7F4E;
 75 /** Certificate Profile Identifier */
 76 CVC.TAG_CPI = 0x5F29;
 77 /** Certification Authority Reference */
 78 CVC.TAG_CAR = 0x42;
 79 /** Public Key */
 80 CVC.TAG_PUK = 0x7F49;
 81 /** Prime Modulus */
 82 CVC.TAG_ECC_P = 0x81;
 83 /** First coefficient a */
 84 CVC.TAG_ECC_A = 0x82;
 85 /** Second coefficient b */
 86 CVC.TAG_ECC_B = 0x83;
 87 /** Base Point G */
 88 CVC.TAG_ECC_G = 0x84;
 89 /** Order of the base point */
 90 CVC.TAG_ECC_N = 0x85;
 91 /** Public Point y */
 92 CVC.TAG_ECC_Q = 0x86;
 93 /** Cofactor f */
 94 CVC.TAG_ECC_H = 0x87;
 95 /** Certificate Holder Reference */
 96 CVC.TAG_CHR = 0x5F20;
 97 /** Certificate Holder Authorisation Template */
 98 CVC.TAG_CHAT = 0x7F4C;
 99 /** Relative Authorization */
100 CVC.TAG_AUT = 0x53;
101 /** Certificate Extension */
102 CVC.TAG_EXTN = 0x65;
103 /** Certificate Effective Date */
104 CVC.TAG_CED = 0x5F25;
105 /** Certificate Expiration Date */
106 CVC.TAG_CXD = 0x5F24;
107 /** Signature */
108 CVC.TAG_SIG = 0x5F37;
109 
110 
111 /** Table of tag names */
112 CVC.OBJECTNAMES = []
113 CVC.OBJECTNAMES[CVC.TAG_AT] = "Authentication Template";
114 CVC.OBJECTNAMES[CVC.TAG_CVC] = "CV Certificate";
115 CVC.OBJECTNAMES[CVC.TAG_BODY] = "Certificate Body";
116 CVC.OBJECTNAMES[CVC.TAG_CPI] = "Certificate Profile Indicator";
117 CVC.OBJECTNAMES[CVC.TAG_CAR] = "Certification Authority Reference";
118 CVC.OBJECTNAMES[CVC.TAG_PUK] = "Public Key";
119 CVC.OBJECTNAMES[CVC.TAG_ECC_P] = "Prime/Modulus";
120 CVC.OBJECTNAMES[CVC.TAG_ECC_A] = "First coefficient a/Exponent";
121 CVC.OBJECTNAMES[CVC.TAG_ECC_B] = "Second coefficient b";
122 CVC.OBJECTNAMES[CVC.TAG_ECC_G] = "Base Point G";
123 CVC.OBJECTNAMES[CVC.TAG_ECC_N] = "Order of the base point";
124 CVC.OBJECTNAMES[CVC.TAG_ECC_Q] = "Public Point y";
125 CVC.OBJECTNAMES[CVC.TAG_ECC_H] = "Cofactor f";
126 CVC.OBJECTNAMES[CVC.TAG_CHR] = "Certificate Holder Reference";
127 CVC.OBJECTNAMES[CVC.TAG_CHAT] = "Certificate Holder Authentication Template";
128 CVC.OBJECTNAMES[CVC.TAG_AUT] = "Relative Authorization";
129 CVC.OBJECTNAMES[CVC.TAG_EXTN] = "Extension";
130 CVC.OBJECTNAMES[CVC.TAG_CED] = "Certificate Effective Date";
131 CVC.OBJECTNAMES[CVC.TAG_CXD] = "Certificate Expiration Date";
132 CVC.OBJECTNAMES[CVC.TAG_SIG] = "Signature";
133 
134 CVC.PUK_SYNTAX = [
135 	{ tag: 0x06, minlen: 2, maxlen: 20 },
136 	{ tag: CVC.TAG_ECC_P, minlen: 20, maxlen: 512, optional: true },
137 	{ tag: CVC.TAG_ECC_A, minlen: 1, maxlen: 512, optional: true },
138 	{ tag: CVC.TAG_ECC_B, minlen: 20, maxlen: 66, optional: true },
139 	{ tag: CVC.TAG_ECC_G, minlen: 41, maxlen: 133, optional: true },
140 	{ tag: CVC.TAG_ECC_N, minlen: 20, maxlen: 66, optional: true },
141 	{ tag: CVC.TAG_ECC_Q, minlen: 41, maxlen: 133, optional: true },
142 	{ tag: CVC.TAG_ECC_H, minlen: 1, maxlen: 1, optional: true }
143 ];
144 
145 CVC.REQ_BODY_SYNTAX = [
146 	{ tag: CVC.TAG_CPI, minlen: 1, maxlen: 1 },
147 	{ tag: CVC.TAG_CAR, minlen: 8, maxlen: 16, optional: true },
148 	{ tag: CVC.TAG_PUK, content: CVC.PUK_SYNTAX },
149 	{ tag: CVC.TAG_CHR, minlen: 8, maxlen: 16 },
150 	{ tag: CVC.TAG_EXTN, optional: true }
151 ];
152 
153 CVC.REQ_SYNTAX = [
154 	{ tag: CVC.TAG_BODY, content: CVC.REQ_BODY_SYNTAX },
155 	{ tag: CVC.TAG_SIG, minlen: 40, maxlen: 550 }
156 ];
157 
158 CVC.ATREQ_SYNTAX = [
159 	{ tag: CVC.TAG_CVC, content: CVC.REQ_SYNTAX },
160 	{ tag: CVC.TAG_CAR, minlen: 8, maxlen: 16 },
161 	{ tag: CVC.TAG_SIG, minlen: 40, maxlen: 550 }
162 ];
163 
164 CVC.CHAT_SYNTAX = [
165 	{ tag: 0x06, minlen: 2, maxlen: 20 },
166 	{ tag: CVC.TAG_AUT, minlen: 1, maxlen: 5 }
167 ];
168 
169 CVC.CERT_BODY_SYNTAX = [
170 	{ tag: CVC.TAG_CPI, minlen: 1, maxlen: 1 },
171 	{ tag: CVC.TAG_CAR, minlen: 8, maxlen: 16 },
172 	{ tag: CVC.TAG_PUK, content: CVC.PUK_SYNTAX },
173 	{ tag: CVC.TAG_CHR, minlen: 8, maxlen: 16 },
174 	{ tag: CVC.TAG_CHAT, content: CVC.CHAT_SYNTAX },
175 	{ tag: CVC.TAG_CED, minlen: 6, maxlen: 6 },
176 	{ tag: CVC.TAG_CXD, minlen: 6, maxlen: 6 },
177 	{ tag: CVC.TAG_EXTN, optional: true }
178 ];
179 
180 CVC.CERT_SYNTAX = [
181 	{ tag: CVC.TAG_BODY, content: CVC.CERT_BODY_SYNTAX },
182 	{ tag: CVC.TAG_SIG, minlen: 24, maxlen: 550 }
183 ];
184 
185 
186 
187 /** Table of rights description for id-IS */
188 CVC.ISRIGHTS = [
189 	"Read access to ePassport application: DG 3 (Fingerprint)",
190 	"Read access to ePassport application: DG 4 (Iris)",
191 	"RFU (Bit 3)",
192 	"RFU (Bit 4)",
193 	"RFU (Bit 5)",
194 	"Read access to eID application"
195 ];
196 CVC.idIS = new ByteString("id-IS", OID);
197 
198 
199 /** Table of rights description for id-AT */
200 CVC.ATRIGHTS = [
201 	"Age Verification",
202 	"Community ID Verification",
203 	"Restricted Identification",
204 	"Privileged Terminal",
205 	"CAN allowed",
206 	"PIN Management",
207 	"Install Certificate",
208 	"Install Qualified Certificate",
209 
210 	"Read Access DG 1 (Document Type)",
211 	"Read Access DG 2 (Issuing State)",
212 	"Read Access DG 3 (Date of Expiration)",
213 	"Read Access DG 4 (Given Name)",
214 	"Read Access DG 5 (Surname)",
215 	"Read Access DG 6 (Pseudonym)",
216 	"Read Access DG 7 (Academic Grade)",
217 	"Read Access DG 8 (Date of Birth)",
218 
219 	"Read Access DG 9 (Place of Birth)",
220 	"Read Access DG 10",
221 	"Read Access DG 11",
222 	"Read Access DG 12",
223 	"Read Access DG 13",
224 	"Read Access DG 14",
225 	"Read Access DG 15",
226 	"Read Access DG 16",
227 
228 	"Read Access DG 17 (Place of Residence)",
229 	"Read Access DG 18 (Community ID)",
230 	"Read Access DG 19 (Conditions I-eAT)",
231 	"Read Access DG 20 (Conditions II-eAT)",
232 	"Read Access DG 21",
233 	"RFU (Bit 29)",
234 	"RFU (Bit 30)",
235 	"RFU (Bit 31)",
236 
237 	"RFU (Bit 32)",
238 	"Write Access DG 21",
239 	"Write Access DG 20 (Conditions II-eAT)",
240 	"Write Access DG 19 (Conditions I-eAT)",
241 	"Write Access DG 18 (Community ID)",
242 	"Write Access DG 17 (Place of Residence)"
243 ];
244 CVC.idAT = new ByteString("id-AT", OID);
245 
246 
247 
248 /** Table of rights description for id-ST */
249 CVC.STRIGHTS = [
250 	"Generate electronic signature",
251 	"Generate qualified electronic signature",
252 	"RFU (Bit 2)",
253 	"RFU (Bit 3)",
254 	"RFU (Bit 4)",
255 	"RFU (Bit 5)"
256 ];
257 CVC.idST = new ByteString("id-ST", OID);
258 
259 CVC.idSC_HSM = new ByteString("2B0601040181C31F030101", HEX);
260 
261 
262 
263 /** TA constants */
264 CVC.id_TA_ECDSA = new ByteString("id-TA-ECDSA", OID);
265 CVC.id_TA_ECDSA_SHA_1 = new ByteString("id-TA-ECDSA-SHA-1", OID);
266 CVC.id_TA_ECDSA_SHA_224 = new ByteString("id-TA-ECDSA-SHA-224", OID);
267 CVC.id_TA_ECDSA_SHA_256 = new ByteString("id-TA-ECDSA-SHA-256", OID);
268 CVC.id_TA_ECDSA_SHA_384 = new ByteString("id-TA-ECDSA-SHA-384", OID);
269 CVC.id_TA_ECDSA_SHA_512 = new ByteString("id-TA-ECDSA-SHA-512", OID);
270 CVC.id_TA_RSA_v1_5_SHA_1 = new ByteString("id-TA-RSA-v1-5-SHA-1", OID);
271 CVC.id_TA_RSA_v1_5_SHA_256 = new ByteString("id-TA-RSA-v1-5-SHA-256", OID);
272 CVC.id_TA_RSA_v1_5_SHA_512 = new ByteString("id-TA-RSA-v1-5-SHA-512", OID);
273 CVC.id_TA_RSA_PSS_SHA_1 = new ByteString("id-TA-RSA-PSS-SHA-1", OID);
274 CVC.id_TA_RSA_PSS_SHA_256 = new ByteString("id-TA-RSA-PSS-SHA-256", OID);
275 CVC.id_TA_RSA_PSS_SHA_512 = new ByteString("id-TA-RSA-PSS-SHA-512", OID);
276 
277 CVC.id_ECPUBLICKEY = new ByteString("id-ecPublicKey", OID);
278 CVC.id_ANSIX9 = new ByteString("iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) prime(1)", OID);
279 CVC.id_CERTICOM = new ByteString("iso(1) identified-organization(3) certicom(132) curve(0)", OID);
280 CVC.id_BRAINPOOL = new ByteString("ellipticCurve", OID);
281 
282 
283 
284 /**
285  * Return signature mechanism for object identifier
286  *
287  * @param {ByteString} oid the object identifer from the public key object
288  * @returns the signature mechanism as Crypto. constant or -1 if not defined
289  * @type Number
290  */
291 CVC.getSignatureMech = function(oid, keysize) {
292 	if (oid.equals(CVC.id_TA_ECDSA_SHA_1))
293 		return Crypto.ECDSA_SHA1;
294 	if (oid.equals(CVC.id_TA_ECDSA_SHA_224))
295 		return Crypto.ECDSA_SHA224;
296 	if (oid.equals(CVC.id_TA_ECDSA_SHA_256))
297 		return Crypto.ECDSA_SHA256;
298 	if (oid.equals(CVC.id_TA_ECDSA_SHA_384))
299 		return Crypto.ECDSA_SHA384;
300 	if (oid.equals(CVC.id_TA_ECDSA_SHA_512))
301 		return Crypto.ECDSA_SHA512;
302 	if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_1))
303 		return Crypto.RSA_SHA1;
304 	if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_256))
305 		return Crypto.RSA_SHA256;
306 	if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_512))
307 		return Crypto.RSA_SHA512;
308 	if (oid.equals(CVC.id_TA_RSA_PSS_SHA_1))
309 		return Crypto.RSA_PSS_SHA1;
310 	if (oid.equals(CVC.id_TA_RSA_PSS_SHA_256))
311 		return Crypto.RSA_PSS_SHA256;
312 	if (oid.equals(CVC.id_TA_RSA_PSS_SHA_512))
313 		return Crypto.RSA_PSS_SHA512;
314 	if (CVC.isCurveOID(oid)) {
315 		assert(typeof(keysize) == "number", "Argument keysize must be number");
316 		if (keysize >= 512) {
317 			return Crypto.ECDSA_SHA512;
318 		} else if (keysize >= 384) {
319 			return Crypto.ECDSA_SHA384;
320 		} else if (keysize >= 256) {
321 			return Crypto.ECDSA_SHA256;
322 		} else if (keysize >= 224) {
323 			return Crypto.ECDSA_SHA224;
324 		}
325 	}
326 
327 	return -1;
328 }
329 
330 
331 
332 /**
333  * Return hash mechanism for object identifier
334  *
335  * @param {ByteString} oid the object identifer from the public key object
336  * @returns the hash mechanism as Crypto. constant or -1 if not defined
337  * @type Number
338  */
339 CVC.getHashMech = function(oid) {
340 	if (oid.equals(CVC.id_TA_ECDSA_SHA_1))
341 		return Crypto.SHA_1;
342 	if (oid.equals(CVC.id_TA_ECDSA_SHA_224))
343 		return Crypto.SHA_224;
344 	if (oid.equals(CVC.id_TA_ECDSA_SHA_256))
345 		return Crypto.SHA_256;
346 	if (oid.equals(CVC.id_TA_ECDSA_SHA_384))
347 		return Crypto.SHA_384;
348 	if (oid.equals(CVC.id_TA_ECDSA_SHA_512))
349 		return Crypto.SHA_512;
350 	if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_1))
351 		return Crypto.SHA_1;
352 	if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_256))
353 		return Crypto.SHA_256;
354 	if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_512))
355 		return Crypto.SHA_512;
356 	if (oid.equals(CVC.id_TA_RSA_PSS_SHA_1))
357 		return Crypto.SHA_1;
358 	if (oid.equals(CVC.id_TA_RSA_PSS_SHA_256))
359 		return Crypto.SHA_256;
360 	if (oid.equals(CVC.id_TA_RSA_PSS_SHA_512))
361 		return Crypto.SHA_512;
362 	return -1;
363 }
364 
365 
366 
367 /**
368  * Return true of the object identifier denotes a curve
369  *
370  * @type boolean
371  * @return true, if ECDSA based OID
372  */
373 CVC.isCurveOID = function(oid) {
374 	if (oid.startsWith(CVC.id_BRAINPOOL) == CVC.id_BRAINPOOL.length) {
375 		return true;
376 	}
377 	if (oid.startsWith(CVC.id_ANSIX9) == CVC.id_ANSIX9.length) {
378 		return true;
379 	}
380 	if (oid.startsWith(CVC.id_CERTICOM) == CVC.id_CERTICOM.length) {
381 		return true;
382 	}
383 	return false;
384 }
385 
386 
387 
388 /**
389  * Return true of the object identifier starts with id-TA-ECDSA
390  *
391  * @type boolean
392  * @return true, if ECDSA based OID
393  */
394 CVC.isECDSA = function(oid) {
395 	if (oid.startsWith(CVC.id_TA_ECDSA) == CVC.id_TA_ECDSA.length) {
396 		return true;
397 	}
398 	if (oid.equals(CVC.id_ECPUBLICKEY)) {
399 		return true;
400 	}
401 	if (CVC.isCurveOID(oid)) {
402 		return true;
403 	}
404 	return false;
405 }
406 
407 
408 
409 /**
410  * Wrap an ECDSA signature in the format r || s into a TLV encoding as defined by RFC 3279
411  *
412  * @param signature ByteString containing the concatenation of r and s as unsigned integer values
413  * @returns ASN.1 SEQUENCE objects containing two signed integer r and s
414  */
415 CVC.wrapSignature = function(signature) {
416 	var len = signature.length / 2;
417 
418 	// r and s are unsigned big integer. We might need to pad a zero for ASN.1 INTEGER which is signed
419 	var r = signature.bytes(0, len);
420 	while ((r.length > 1) && (r.byteAt(0) == 0)) {
421 		r = r.bytes(1);
422 	}
423 	if (r.byteAt(0) >= 0x80) {
424 		r = ByteString.valueOf(0, 1).concat(r);
425 	}
426 
427 	var s = signature.bytes(len, len);
428 	while ((s.length > 1) && (s.byteAt(0) == 0)) {
429 		s = s.bytes(1);
430 	}
431 	if (s.byteAt(0) >= 0x80) {
432 		s = ByteString.valueOf(0, 1).concat(s);
433 	}
434 
435 	var t = new ASN1(ASN1.SEQUENCE);
436 	t.add(new ASN1(ASN1.INTEGER, r));
437 	t.add(new ASN1(ASN1.INTEGER, s));
438 
439 	return t.getBytes();
440 }
441 
442 
443 
444 /**
445  * Integer to octet string conversion
446  */
447 CVC.I2O = function(value, length) {
448 	if (value.length > length) {
449 		value = value.right(length);
450 	}
451 	while (value.length < length) {
452 		value = CVC.PAD.left((length - value.length - 1 & 15) + 1).concat(value);
453 	}
454 	return value;
455 }
456 CVC.PAD = new ByteString("00000000000000000000000000000000", HEX);
457 
458 
459 
460 /**
461  * Unwrap a ECDSA signature from the TLV encoding according to RFC3279 into the concatenation
462  * of the unsigned integer r and s
463  *
464  * @param signature TLV encoded signature
465  * @returns concatenation of r and s
466  */
467 CVC.unwrapSignature = function(signature, keylen) {
468 	var t = new ASN1(signature);
469 	if (typeof(keylen) != "undefined") {
470 		var r = CVC.I2O(t.get(0).value, keylen);
471 		var s = CVC.I2O(t.get(1).value, keylen);
472 	} else {
473 		var r = t.get(0).value;
474 		if (r.byteAt(0) == 00)
475 			r = r.bytes(1);
476 
477 		var s = t.get(1).value;
478 		if (s.byteAt(0) == 00)
479 			s = s.bytes(1);
480 	}
481 
482 	return r.concat(s);
483 }
484 
485 
486 
487 /**
488  * Rewrap an ECDSA signature that contains redundant leading zeros in integer.
489  *
490  * @param signature the signature
491  * @type ByteString
492  * @param the fixed signature
493  */
494 CVC.rewrapSignature = function(signature) {
495 	var a = new ASN1(signature);
496 	var r = a.get(0).value;
497 	while ((r.byteAt(0) == 0) && (r.byteAt(1) < 0x80)) {
498 		r = r.bytes(1);
499 	}
500 
501 	var s = a.get(1).value;
502 	while ((s.byteAt(0) == 0) && (s.byteAt(1) < 0x80)) {
503 		s = s.bytes(1);
504 	}
505 	return (new ASN1(0x30, new ASN1(0x02, r), new ASN1(0x02, s))).getBytes();
506 }
507 
508 
509 
510 /**
511  * Check a ASN sequence against a structure description
512  *
513  * @private
514  * @param {Object} s the structure description
515  * @param {ASN1} asn the asn sequence
516  * @type String
517  * @return null if valid or the validation error message
518  */
519 CVC.checkStructure = function(s, asn) {
520 	var j = 0;
521 	for (var i = 0; i < s.length; i++) {
522 		var d = s[i];
523 
524 //		print("Checking tag " + d.tag.toString(HEX));
525 
526 		if (j < asn.elements) {
527 			var a = asn.get(j);
528 
529 			if (a.tag == d.tag) {
530 				if ((typeof(d.minlen) != "undefined") && (a.value.length < d.minlen)) {
531 					return "Length (" + a.value.length + ") of " + CVC.OBJECTNAMES[d.tag] + "(" + d.tag.toString(HEX) + ") less than " + d.minlen;
532 				}
533 				if ((typeof(d.maxlen) != "undefined") && (a.value.length > d.maxlen)) {
534 					return "Length (" + a.value.length + ") of " + CVC.OBJECTNAMES[d.tag] + "(" + d.tag.toString(HEX) + ") more than " + d.maxlen;
535 				}
536 				if ((typeof(d.content) != "undefined")) {
537 					var r = CVC.checkStructure(d.content, a);
538 					if (r) {
539 						return r;
540 					}
541 				}
542 				j++;
543 			} else {
544 				if (!d.optional) {
545 					return "Expecting " + CVC.OBJECTNAMES[d.tag] + "(" + d.tag.toString(HEX) + ") but found tag " + a.tag.toString(HEX);
546 				}
547 			}
548 		} else {
549 			if (!d.optional) {
550 				return "Expecting " + CVC.OBJECTNAMES[d.tag] + "(" + d.tag.toString(HEX) + ") but sequence is exhaused";
551 			}
552 		}
553 	}
554 	return null;
555 }
556 
557 
558 
559 /**
560  * Validate ASN1 semantic for card verifiable certificate or certificate request
561  *
562  * @type String
563  * @return null if no error, or error message
564  */
565 CVC.prototype.validate = function() {
566 	var r = null;
567 	if (this.asn.tag == CVC.TAG_AT) {
568 		r = CVC.checkStructure(CVC.ATREQ_SYNTAX, this.asn);
569 	} else {
570 		if (this.body.find(CVC.TAG_CED)) {
571 			r = CVC.checkStructure(CVC.CERT_SYNTAX, this.asn);
572 		} else {
573 			r = CVC.checkStructure(CVC.REQ_SYNTAX, this.asn);
574 		}
575 	}
576 	return r;
577 }
578 
579 
580 
581 /**
582  * Return true of the certificate contains domain parameter
583  *
584  * @type boolean
585  * @return true, if certificate contains domain parameter
586  */
587 CVC.prototype.containsDomainParameter = function() {
588 	var pdo = this.body.find(CVC.TAG_PUK);
589 	if (pdo == null) {
590 		return false;
591 	}
592 
593 	var d = pdo.find(0x84);		// Generator
594 	return (d != null);
595 }
596 
597 
598 
599 /**
600  * Returns the certificate profile indicator (CPI)
601  *
602  * @return the CPI or null
603  * @type Number
604  */
605 CVC.prototype.getCPI = function() {
606 	var cpido = this.body.find(CVC.TAG_CPI);
607 
608 	if (!cpido) {
609 		return null;
610 	}
611 
612 	return cpido.value.toUnsigned();
613 }
614 
615 
616 
617 /**
618  * Returns the certification authority reference (CAR).
619  *
620  * @return the CAR or null
621  * @type PublicKeyReference
622  */
623 CVC.prototype.getCAR = function() {
624 	var cardo = this.body.find(CVC.TAG_CAR);
625 
626 	if (!cardo) {
627 		return null;
628 	}
629 
630 	return new PublicKeyReference(cardo.value);
631 }
632 
633 
634 
635 /**
636  * Returns the certificate holder reference (CHR).
637  *
638  * @return the CHR
639  * @type PublicKeyReference
640  */
641 CVC.prototype.getCHR = function() {
642 	var chrdo = this.body.find(CVC.TAG_CHR);
643 
644 	if (!chrdo) {
645 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a CHR");
646 	}
647 
648 	return new PublicKeyReference(chrdo.value);
649 }
650 
651 
652 
653 /**
654  * Returns the certificate effective date (CED).
655  *
656  * @return the CED or null
657  * @type Date
658  */
659 CVC.prototype.getCED = function() {
660 	var ceddo = this.body.find(CVC.TAG_CED);
661 
662 	if (!ceddo) {
663 		return null
664 	}
665 
666 	var b = ceddo.value;
667 
668 	var d = new Date();
669 	d.setFullYear(b.byteAt(0) * 10 + b.byteAt(1) + 2000,
670 				  b.byteAt(2) * 10 + b.byteAt(3) - 1,
671 				  b.byteAt(4) * 10 + b.byteAt(5));
672 	d.setHours(12, 0, 0, 0);
673 	return d;
674 }
675 
676 
677 
678 /**
679  * Returns the certificate expiration date (CXD).
680  *
681  * @return the CXD or null
682  * @type Date
683  */
684 CVC.prototype.getCXD = function() {
685 	var cxddo = this.body.find(CVC.TAG_CXD);
686 
687 	if (!cxddo) {
688 		return null
689 	}
690 
691 	var b = cxddo.value;
692 
693 	var d = new Date();
694 	d.setFullYear(b.byteAt(0) * 10 + b.byteAt(1) + 2000,
695 				  b.byteAt(2) * 10 + b.byteAt(3) - 1,
696 				  b.byteAt(4) * 10 + b.byteAt(5));
697 	d.setHours(12, 0, 0, 0);
698 	return d;
699 }
700 
701 
702 
703 /**
704  * Returns the outer certification authority reference (CAR).
705  *
706  * @return the outer CAR or null
707  * @type PublicKeyReference
708  */
709 CVC.prototype.getOuterCAR = function() {
710 	if (!this.isAuthenticatedRequest()) {
711 		return null;
712 	}
713 	var cardo = this.asn.get(1);
714 
715 	if (!cardo) {
716 		return null
717 	}
718 
719 	return new PublicKeyReference(cardo.value);
720 }
721 
722 
723 
724 /**
725  * Returns the extension identified by the object identifier.
726  *
727  * @return the extension including the OID or null if not defined
728  * @type ASN1
729  */
730 CVC.prototype.getExtension = function(extoid) {
731 	var extdo = this.body.find(CVC.TAG_EXTN);
732 
733 	if (!extdo) {
734 		return null;
735 	}
736 
737 //	print(extdo);
738 
739 	for (var i = 0; i < extdo.elements; i++) {
740 		var ext = extdo.get(i);
741 		var oid = ext.get(0);
742 		assert(oid.tag == ASN1.OBJECT_IDENTIFIER);
743 		if (oid.value.equals(extoid)) {
744 			return ext;
745 		}
746 	}
747 	return null;
748 }
749 
750 
751 
752 /**
753  * Returns the Certificate Holder Authorization Template.
754  *
755  * @return the chat or null if not defined
756  * @type ASN1
757  */
758 CVC.prototype.getCHAT = function() {
759 	var chat = this.body.find(CVC.TAG_CHAT);
760 
761 	return chat;
762 }
763 
764 
765 
766 /**
767  * Returns the public key object identifier
768  *
769  * @returns the object identifier assigned to the public key
770  * @type ByteString
771  */
772 CVC.prototype.getPublicKeyOID = function() {
773 	var pdo = this.body.find(CVC.TAG_PUK);
774 	if (pdo == null) {
775 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
776 	}
777 
778 	var d = pdo.find(ASN1.OBJECT_IDENTIFIER);
779 	if (d == null) {
780 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Public key does not contain an object identifier");
781 	}
782 	return d.value;
783 }
784 
785 
786 
787 /**
788  * Decode a public key from the TR-03110 format
789  *
790  * @param {ASN1} pdo the public key data object
791  * @param {Key} key the key object to fill
792  */
793 CVC.decodeECPublicKey = function(pdo, key) {
794 
795 	var d = pdo.find(0x86);		// Public point
796 	if (d == null) {
797 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key value");
798 	}
799 
800 	var b = d.value.bytes(1);
801 	key.setComponent(Key.ECC_QX, b.left(b.length >> 1));
802 	key.setComponent(Key.ECC_QY, b.right(b.length >> 1));
803 
804 	var d = pdo.find(0x81);		// Prime modulus
805 	if (d != null) {
806 		key.setComponent(Key.ECC_P, d.value);
807 	}
808 
809 	var d = pdo.find(0x82);		// First coefficient a
810 	if (d != null) {
811 		key.setComponent(Key.ECC_A, d.value);
812 	}
813 
814 	var d = pdo.find(0x83);		// First coefficient b
815 	if (d != null) {
816 		key.setComponent(Key.ECC_B, d.value);
817 	}
818 
819 	var d = pdo.find(0x84);		// Base Point G
820 	if (d != null) {
821 		var b = d.value.bytes(1);
822 		key.setComponent(Key.ECC_GX, b.left(b.length >> 1));
823 		key.setComponent(Key.ECC_GY, b.right(b.length >> 1));
824 	}
825 
826 	var d = pdo.find(0x85);		// Order of the base point
827 	if (d != null) {
828 		key.setComponent(Key.ECC_N, d.value);
829 	}
830 
831 	var d = pdo.find(0x87);		// Cofactor f
832 	if (d != null) {
833 		key.setComponent(Key.ECC_H, d.value);
834 	}
835 }
836 
837 
838 
839 /**
840  * Returns the EC public key contained in the certificate.
841  *
842  * @param {Key} domParam optional domain parameter if they are not contained in certificate
843  * @return the public key object
844  * @type Key
845  */
846 CVC.prototype.getECPublicKey = function(domParam) {
847 	var oid = this.getPublicKeyOID();
848 	if (CVC.isCurveOID(oid)) {
849 		var key = new Key();
850 		key.setComponent(Key.ECC_CURVE_OID, oid);
851 	} else if (typeof(domParam) != "undefined") {
852 		var key = new Key(domParam);
853 	} else {
854 		if (typeof(this.domParam) != "undefined") {
855 			var key = new Key(this.domParam);
856 		} else {
857 			var key = new Key();
858 		}
859 	}
860 
861 	key.setType(Key.PUBLIC);
862 
863 	var pdo = this.body.find(CVC.TAG_PUK);
864 	if (pdo == null) {
865 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
866 	}
867 
868 	CVC.decodeECPublicKey(pdo, key);
869 
870 	return key;
871 }
872 
873 
874 
875 /**
876  * Returns the RSA public key contained in the certificate.
877  *
878  * @return the public key object
879  * @type Key
880  */
881 CVC.prototype.getRSAPublicKey = function() {
882 	var key = new Key();
883 
884 	key.setType(Key.PUBLIC);
885 
886 	var pdo = this.body.find(CVC.TAG_PUK);
887 	if (pdo == null) {
888 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
889 	}
890 
891 	var d = pdo.find(0x81);		// modulus
892 	if (d != null) {
893 		key.setComponent(Key.MODULUS, d.value);
894 	}
895 
896 	var d = pdo.find(0x82);		// public exponent
897 	if (d != null) {
898 		key.setComponent(Key.EXPONENT, d.value);
899 	}
900 
901 	return key;
902 }
903 
904 
905 
906 /**
907  * Returns the public key contained in the certificate.
908  *
909  * @param {Key} domParam optional domain parameter if they are not contained in certificate
910  * @return the public key object
911  * @type Key
912  */
913 CVC.prototype.getPublicKey = function(domParam) {
914 	var pkoid = this.getPublicKeyOID();
915 
916 	if (CVC.isECDSA(pkoid)) {
917 		return this.getECPublicKey(domParam);
918 	}
919 	return this.getRSAPublicKey();
920 }
921 
922 
923 
924 /**
925  * Determine the SubjectKeyIdentifier as defined in X.509
926  *
927  * @return the key identifier
928  * @type ByteString
929  */
930 CVC.prototype.determineKeyIdentifier = function() {
931 	var pdo = this.body.find(CVC.TAG_PUK);
932 	if (pdo == null) {
933 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
934 	}
935 
936 	var pkoid = this.getPublicKeyOID();
937 
938 	var keybin;
939 	if (CVC.isECDSA(pkoid)) {
940 		var d = pdo.find(0x86);		// Public point
941 		keybin = d.value;
942 	} else {
943 		var modulus = pdo.find(0x81).value;		// modulus
944 		if (modulus.byteAt(0) >= 0x80) {
945 			modulus = ByteString.valueOf(0).concat(modulus);
946 		}
947 		var exponent = pdo.find(0x82).value;		// public exponent
948 		var rsapub = new ASN1("RSAPublicKey", ASN1.SEQUENCE,
949 				      new ASN1("modulus", ASN1.INTEGER, modulus),
950 				      new ASN1("publicKeyExponent", ASN1.INTEGER, exponent));
951 
952 		keybin = rsapub.getBytes();
953 	}
954 	return CVC.crypto.digest(Crypto.SHA_1, keybin);
955 }
956 
957 
958 
959 /**
960  * Determine if this is an authenticated request
961  *
962  * @returns true, if authenticated request
963  * @type Boolean
964  */
965 CVC.prototype.isAuthenticatedRequest = function() {
966 	return (this.asn.tag == CVC.TAG_AT);
967 }
968 
969 
970 
971 /**
972  * Determine if this is a certificate request
973  *
974  * @returns true, if certificate request
975  * @type Boolean
976  */
977 CVC.prototype.isCertificateRequest = function() {
978 	if (this.isAuthenticatedRequest()) {
979 		return true;
980 	}
981 
982 	var ced = this.getCED();
983 	return ced == null;
984 }
985 
986 
987 
988 /**
989  * Determine if this is a countersigned authenticated request
990  *
991  * @returns true, if countersigned authenticated request
992  * @type Boolean
993  */
994 CVC.prototype.isCountersignedRequest = function() {
995 	if (!this.isAuthenticatedRequest()) {
996 		return false;
997 	}
998 	return (this.getCHR().getHolder() != this.getOuterCAR().getHolder());
999 }
1000 
1001 
1002 
1003 /**
1004  * Determine if this certificate is expired
1005  *
1006  * @returns true, if certificate is expired
1007  * @type Boolean
1008  */
1009 CVC.prototype.isExpired = function() {
1010 	var now = new Date();
1011 	now.setHours(12, 0, 0, 0);
1012 	return (now.valueOf() > this.getCXD().valueOf());
1013 }
1014 
1015 
1016 
1017 /**
1018  * Verify certificate signature with public key
1019  *
1020  * @param {Crypto} crypto the crypto instance to use for verification
1021  * @param {Key} puk the public key
1022  * @param {ByteString} oid the signature algorithm
1023  * @returns true if the signature is valid
1024  * @type Boolean
1025  */
1026 CVC.prototype.verifyWith = function(crypto, puk, oid) {
1027 	if (this.asn.tag == CVC.TAG_AT) {
1028 		var signature = this.asn.get(0).get(1);
1029 	} else {
1030 		var signature = this.asn.get(1);
1031 	}
1032 
1033 	if (typeof(oid) == "undefined") {
1034 		var oid = this.getPublicKeyOID();
1035 	}
1036 	var mech = CVC.getSignatureMech(oid, puk.getSize());
1037 
1038 	if (CVC.isECDSA(oid)) {
1039 		var signatureValue = CVC.wrapSignature(signature.value);
1040 		this.domParam = puk;
1041 	} else {
1042 		var signatureValue = signature.value;
1043 	}
1044 
1045 	return crypto.verify(puk, mech, this.body.getBytes(), signatureValue);
1046 }
1047 
1048 
1049 
1050 /**
1051  * Verify certificate signature with public key from card verifiable certificate
1052  *
1053  * @param {CVC} cvc the card verifiable certificate used to obtain the public key
1054  * @returns true if the signature is valid
1055  * @type Boolean
1056  */
1057 CVC.prototype.verifyWithCVC = function(crypto, cvc) {
1058 	return this.verifyWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID());
1059 }
1060 
1061 
1062 
1063 /**
1064  * Verify outer signature of an authenticated request with public key
1065  *
1066  * @param {Key} puk the public key
1067  * @param {ByteString} oid the signature algorithm
1068  * @returns true if the signature is valid
1069  * @type Boolean
1070  */
1071 CVC.prototype.verifyATWith = function(crypto, puk, oid) {
1072 	if (!this.isAuthenticatedRequest()) {
1073 		throw new GPError("CVC", GPError.INVALID_DATA, 0, "Not an authenticated request");
1074 	}
1075 
1076 	var signature = this.asn.get(2);
1077 	var signatureInput = this.asn.get(0).getBytes().concat(this.asn.get(1).getBytes());
1078 
1079 	if (typeof(oid) == "undefined") {
1080 		var oid = this.getPublicKeyOID();
1081 	}
1082 	var mech = CVC.getSignatureMech(oid);
1083 
1084 	if (CVC.isECDSA(oid)) {
1085 		var signatureValue = CVC.wrapSignature(signature.value);
1086 	} else {
1087 		var signatureValue = signature.value;
1088 	}
1089 	return crypto.verify(puk, mech, signatureInput, signatureValue);
1090 }
1091 
1092 
1093 
1094 /**
1095  * Verify outer signature of an authenticated request with public key from card verifiable certificate
1096  *
1097  * @param {CVC} cvc the card verifiable certificate used to obtain the public key
1098  * @returns true if the signature is valid
1099  * @type Boolean
1100  */
1101 CVC.prototype.verifyATWithCVC = function(crypto, cvc) {
1102 	return this.verifyATWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID());
1103 }
1104 
1105 
1106 
1107 /**
1108  * Returns the encoded certificate
1109  *
1110  * @return the DER encoded certificate
1111  * @type ByteString
1112  */
1113 CVC.prototype.getBytes = function() {
1114 	return this.bin;
1115 }
1116 
1117 
1118 
1119 /**
1120  * Returns the certificate as ASN1 structure
1121  *
1122  * @return the certificate as ASN1 structure
1123  * @type ASN1
1124  */
1125 CVC.prototype.getASN1 = function() {
1126 	return this.asn;
1127 }
1128 
1129 
1130 
1131 /**
1132  * Function to recursively walk the ASN.1 tree
1133  */
1134 CVC.decorateTree = function(node) {
1135 	var name = CVC.OBJECTNAMES[node.tag];
1136 
1137 	if (name) {
1138 		node.setName(name);
1139 	}
1140 
1141 	if (node.isconstructed) {
1142 		for (var i = 0; i < node.elements; i++) {
1143 			CVC.decorateTree(node.get(i));
1144 		}
1145 	}
1146 }
1147 
1148 
1149 
1150 /**
1151  * Decorate the ASN.1 object with the correct name
1152  */
1153 CVC.prototype.decorate = function() {
1154 	CVC.decorateTree(this.asn);
1155 	var cxddo = this.body.find(CVC.TAG_CXD);
1156 	if (cxddo == null) {
1157 		if (this.asn.tag == CVC.TAG_AT) {
1158 			this.asn.setName("Authenticated CVC Request");
1159 		} else {
1160 			this.asn.setName("CVC Request");
1161 		}
1162 	}
1163 }
1164 
1165 
1166 
1167 /**
1168  * Return list of rights granted by the certificate
1169  *
1170  * @returns the list of rights
1171  * @type String[]
1172  */
1173 CVC.prototype.getRightsAsList = function() {
1174 	var list = [];
1175 
1176 	var rtab;
1177 	var chat = this.getCHAT();
1178 	if (chat == null) {
1179 		return list;
1180 	}
1181 
1182 	var oid = chat.get(0).value;
1183 
1184 	if (oid.equals(CVC.idIS)) {
1185 		rtab = CVC.ISRIGHTS;
1186 	} else if (oid.equals(CVC.idAT)) {
1187 		rtab = CVC.ATRIGHTS;
1188 	} else if (oid.equals(CVC.idST)) {
1189 		rtab = CVC.STRIGHTS;
1190 	} else {
1191 		return null;
1192 	}
1193 
1194 	var mask = chat.get(1).value;
1195 	var c = 0;
1196 	for (var i = mask.length - 1; i >= 0; i--) {
1197 		var akku = mask.byteAt(i);
1198 		for (var j = 0; j < (i == 0 ? 6 : 8); j++) {
1199 			if (akku & 1) {
1200 				list.push(rtab[c]);
1201 			}
1202 			c++;
1203 			akku >>= 1;
1204 		}
1205 	}
1206 	return list;
1207 }
1208 
1209 
1210 
1211 /**
1212  * Return position of certificate in PKI hierachie
1213  *
1214  * @returns Position in PKI hierachie: 0 unknown, 1-CVCA, 2-DVCA, 3-Terminal
1215  * @type Number
1216  */
1217 CVC.prototype.getLevel = function() {
1218 	var chat = this.getCHAT();
1219 	if (!chat) {
1220 		return 0;
1221 	}
1222 
1223 	var l = chat.get(1).value.byteAt(0) & 0xC0;
1224 	if (l == 0xC0) {
1225 		return 1;
1226 	}
1227 	return l == 0x00 ? 3 : 2;
1228 }
1229 
1230 
1231 
1232 /**
1233  * Return a string describing the certificate type
1234  *
1235  * @returns a describing string
1236  * @type String
1237  */
1238 CVC.prototype.getType = function() {
1239 	var ced = this.getCED();
1240 	var chat = this.getCHAT();
1241 
1242 	// Decode certificate / request type
1243 	var str = "CVC ";
1244 	if (ced == null) {
1245 		if (this.asn.tag == CVC.TAG_AT) {
1246 			str = "AT-CVREQ ";
1247 		} else {
1248 			str = "CVREQ ";
1249 		}
1250 	}
1251 
1252 	// Decode CA type
1253 	if (chat != null) {
1254 		var oid = chat.get(0).value;
1255 
1256 		if (oid.equals(CVC.idSC_HSM)) {
1257 			str += "id-SC-HSM ";
1258 			switch(chat.get(1).value.byteAt(0) & 0xC0) {
1259 				case 0xC0: str += "SRCA "; break;
1260 				case 0x80: str += "DICA "; break;
1261 				case 0x40: str += "MICA "; break;
1262 				case 0x00: str += "Device "; break;
1263 			}
1264 		} else {
1265 			var trustedDV = "";
1266 			var untrustedDV = "";
1267 
1268 			if (oid.equals(CVC.idIS)) {
1269 				str += "id-IS ";
1270 				trustedDV = "(official domestic) ";
1271 				untrustedDV = "(official foreign) ";
1272 			} else if (oid.equals(CVC.idAT)) {
1273 				str += "id-AT ";
1274 				trustedDV = "(official domestic) ";
1275 				untrustedDV = "(non-official / foreign) ";
1276 			} else if (oid.equals(CVC.idST)) {
1277 				str += "id-ST ";
1278 				trustedDV = "(accreditation body) ";
1279 				untrustedDV = "(certification service provider) ";
1280 			} else {
1281 				str += oid.toString(OID) + " ";
1282 			}
1283 
1284 			switch(chat.get(1).value.byteAt(0) & 0xC0) {
1285 				case 0xC0: str += "CVCA "; break;
1286 				case 0x80: str += "DV " + trustedDV; break;
1287 				case 0x40: str += "DV " + untrustedDV; break;
1288 				case 0x00: str += "Terminal "; break;
1289 			}
1290 		}
1291 	}
1292 
1293 	return str;
1294 }
1295 
1296 
1297 
1298 /**
1299  * Return a textual description of the certificate
1300  *
1301  * @returns a string containing information about the certificate
1302  * @type String
1303  */
1304 CVC.prototype.toString = function() {
1305 	var car = this.getCAR();
1306 	var ced = this.getCED();
1307 
1308 	var str = this.getType();
1309 
1310 	if (car) {
1311 		str += "CAR=" + car.toString() + " ";
1312 	}
1313 
1314 	str += "CHR=" + this.getCHR().toString() + " ";
1315 
1316 	if (ced) {
1317 		str += "CED=" + ced.toLocaleDateString() + " ";
1318 	}
1319 
1320 	var cxd = this.getCXD();
1321 	if (cxd) {
1322 		str += "CXD=" + cxd.toLocaleDateString() + " ";
1323 	}
1324 
1325 	if (this.isAuthenticatedRequest()) {
1326 		str += "oCAR=" + this.getOuterCAR().toString() + " ";
1327 	}
1328 
1329 	return str;
1330 }
1331 
1332 
1333 
1334 /**
1335  * Return object suitable for JSON encoding
1336  *
1337  * @returns a JSON encodable object
1338  * @type Object
1339  */
1340 CVC.prototype.toJSON = function() {
1341 	return { clazz: "CVC", value: this.bin };
1342 }
1343 
1344 
1345 
1346 /**
1347  * Recreate object from JSON encoding
1348  *
1349  * @returns the CVC object
1350  * @type Object
1351  */
1352 CVC.fromJSON = function(obj) {
1353 	assert(obj.clazz.equals("CVC"), "JSON object must contain clazz: CVC");
1354 	return new CVC(obj.value);
1355 }
1356