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) {
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 	return -1;
315 }
316 
317 
318 
319 /**
320  * Return hash mechanism for object identifier
321  *
322  * @param {ByteString} oid the object identifer from the public key object
323  * @returns the hash mechanism as Crypto. constant or -1 if not defined
324  * @type Number
325  */
326 CVC.getHashMech = function(oid) {
327 	if (oid.equals(CVC.id_TA_ECDSA_SHA_1))
328 		return Crypto.SHA_1;
329 	if (oid.equals(CVC.id_TA_ECDSA_SHA_224))
330 		return Crypto.SHA_224;
331 	if (oid.equals(CVC.id_TA_ECDSA_SHA_256))
332 		return Crypto.SHA_256;
333 	if (oid.equals(CVC.id_TA_ECDSA_SHA_384))
334 		return Crypto.SHA_384;
335 	if (oid.equals(CVC.id_TA_ECDSA_SHA_512))
336 		return Crypto.SHA_512;
337 	if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_1))
338 		return Crypto.SHA_1;
339 	if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_256))
340 		return Crypto.SHA_256;
341 	if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_512))
342 		return Crypto.SHA_512;
343 	if (oid.equals(CVC.id_TA_RSA_PSS_SHA_1))
344 		return Crypto.SHA_1;
345 	if (oid.equals(CVC.id_TA_RSA_PSS_SHA_256))
346 		return Crypto.SHA_256;
347 	if (oid.equals(CVC.id_TA_RSA_PSS_SHA_512))
348 		return Crypto.SHA_512;
349 	return -1;
350 }
351 
352 
353 
354 /**
355  * Return true of the object identifier starts with id-TA-ECDSA
356  *
357  * @type boolean
358  * @return true, if ECDSA based OID
359  */
360 CVC.isECDSA = function(oid) {
361 	if (oid.startsWith(CVC.id_TA_ECDSA) == CVC.id_TA_ECDSA.length) {
362 		return true;
363 	}
364 	if (oid.equals(CVC.id_ECPUBLICKEY)) {
365 		return true;
366 	}
367 	if (oid.startsWith(CVC.id_BRAINPOOL) == CVC.id_BRAINPOOL.length) {
368 		return true;
369 	}
370 	if (oid.startsWith(CVC.id_ANSIX9) == CVC.id_ANSIX9.length) {
371 		return true;
372 	}
373 	if (oid.startsWith(CVC.id_CERTICOM) == CVC.id_CERTICOM.length) {
374 		return true;
375 	}
376 	return false;
377 }
378 
379 
380 
381 /**
382  * Wrap an ECDSA signature in the format r || s into a TLV encoding as defined by RFC 3279
383  *
384  * @param signature ByteString containing the concatenation of r and s as unsigned integer values
385  * @returns ASN.1 SEQUENCE objects containing two signed integer r and s
386  */
387 CVC.wrapSignature = function(signature) {
388 	var len = signature.length / 2;
389 
390 	// r and s are unsigned big integer. We might need to pad a zero for ASN.1 INTEGER which is signed
391 	var r = signature.bytes(0, len);
392 	while ((r.length > 1) && (r.byteAt(0) == 0)) {
393 		r = r.bytes(1);
394 	}
395 	if (r.byteAt(0) >= 0x80) {
396 		r = ByteString.valueOf(0, 1).concat(r);
397 	}
398 
399 	var s = signature.bytes(len, len);
400 	while ((s.length > 1) && (s.byteAt(0) == 0)) {
401 		s = s.bytes(1);
402 	}
403 	if (s.byteAt(0) >= 0x80) {
404 		s = ByteString.valueOf(0, 1).concat(s);
405 	}
406 
407 	var t = new ASN1(ASN1.SEQUENCE);
408 	t.add(new ASN1(ASN1.INTEGER, r));
409 	t.add(new ASN1(ASN1.INTEGER, s));
410 
411 	return t.getBytes();
412 }
413 
414 
415 
416 /**
417  * Integer to octet string conversion
418  */
419 CVC.I2O = function(value, length) {
420 	if (value.length > length) {
421 		value = value.right(length);
422 	}
423 	while (value.length < length) {
424 		value = CVC.PAD.left((length - value.length - 1 & 15) + 1).concat(value);
425 	}
426 	return value;
427 }
428 CVC.PAD = new ByteString("00000000000000000000000000000000", HEX);
429 
430 
431 
432 /**
433  * Unwrap a ECDSA signature from the TLV encoding according to RFC3279 into the concatenation
434  * of the unsigned integer r and s
435  *
436  * @param signature TLV encoded signature
437  * @returns concatenation of r and s
438  */
439 CVC.unwrapSignature = function(signature, keylen) {
440 	var t = new ASN1(signature);
441 	if (typeof(keylen) != "undefined") {
442 		var r = CVC.I2O(t.get(0).value, keylen);
443 		var s = CVC.I2O(t.get(1).value, keylen);
444 	} else {
445 		var r = t.get(0).value;
446 		if (r.byteAt(0) == 00)
447 			r = r.bytes(1);
448 
449 		var s = t.get(1).value;
450 		if (s.byteAt(0) == 00)
451 			s = s.bytes(1);
452 	}
453 
454 	return r.concat(s);
455 }
456 
457 
458 
459 /**
460  * Rewrap an ECDSA signature that contains redundant leading zeros in integer.
461  *
462  * @param signature the signature
463  * @type ByteString
464  * @param the fixed signature
465  */
466 CVC.rewrapSignature = function(signature) {
467 	var a = new ASN1(signature);
468 	var r = a.get(0).value;
469 	while ((r.byteAt(0) == 0) && (r.byteAt(1) < 0x80)) {
470 		r = r.bytes(1);
471 	}
472 
473 	var s = a.get(1).value;
474 	while ((s.byteAt(0) == 0) && (s.byteAt(1) < 0x80)) {
475 		s = s.bytes(1);
476 	}
477 	return (new ASN1(0x30, new ASN1(0x02, r), new ASN1(0x02, s))).getBytes();
478 }
479 
480 
481 
482 /**
483  * Check a ASN sequence against a structure description
484  *
485  * @private
486  * @param {Object} s the structure description
487  * @param {ASN1} asn the asn sequence
488  * @type String
489  * @return null if valid or the validation error message
490  */
491 CVC.checkStructure = function(s, asn) {
492 	var j = 0;
493 	for (var i = 0; i < s.length; i++) {
494 		var d = s[i];
495 
496 //		print("Checking tag " + d.tag.toString(HEX));
497 
498 		if (j < asn.elements) {
499 			var a = asn.get(j);
500 
501 			if (a.tag == d.tag) {
502 				if ((typeof(d.minlen) != "undefined") && (a.value.length < d.minlen)) {
503 					return "Length (" + a.value.length + ") of " + CVC.OBJECTNAMES[d.tag] + "(" + d.tag.toString(HEX) + ") less than " + d.minlen;
504 				}
505 				if ((typeof(d.maxlen) != "undefined") && (a.value.length > d.maxlen)) {
506 					return "Length (" + a.value.length + ") of " + CVC.OBJECTNAMES[d.tag] + "(" + d.tag.toString(HEX) + ") more than " + d.maxlen;
507 				}
508 				if ((typeof(d.content) != "undefined")) {
509 					var r = CVC.checkStructure(d.content, a);
510 					if (r) {
511 						return r;
512 					}
513 				}
514 				j++;
515 			} else {
516 				if (!d.optional) {
517 					return "Expecting " + CVC.OBJECTNAMES[d.tag] + "(" + d.tag.toString(HEX) + ") but found tag " + a.tag.toString(HEX);
518 				}
519 			}
520 		} else {
521 			if (!d.optional) {
522 				return "Expecting " + CVC.OBJECTNAMES[d.tag] + "(" + d.tag.toString(HEX) + ") but sequence is exhaused";
523 			}
524 		}
525 	}
526 	return null;
527 }
528 
529 
530 
531 /**
532  * Validate ASN1 semantic for card verifiable certificate or certificate request
533  *
534  * @type String
535  * @return null if no error, or error message
536  */
537 CVC.prototype.validate = function() {
538 	var r = null;
539 	if (this.asn.tag == CVC.TAG_AT) {
540 		r = CVC.checkStructure(CVC.ATREQ_SYNTAX, this.asn);
541 	} else {
542 		if (this.body.find(CVC.TAG_CED)) {
543 			r = CVC.checkStructure(CVC.CERT_SYNTAX, this.asn);
544 		} else {
545 			r = CVC.checkStructure(CVC.REQ_SYNTAX, this.asn);
546 		}
547 	}
548 	return r;
549 }
550 
551 
552 
553 /**
554  * Return true of the certificate contains domain parameter
555  *
556  * @type boolean
557  * @return true, if certificate contains domain parameter
558  */
559 CVC.prototype.containsDomainParameter = function() {
560 	var pdo = this.body.find(CVC.TAG_PUK);
561 	if (pdo == null) {
562 		return false;
563 	}
564 
565 	var d = pdo.find(0x84);		// Generator
566 	return (d != null);
567 }
568 
569 
570 
571 /**
572  * Returns the certificate profile indicator (CPI)
573  *
574  * @return the CPI or null
575  * @type Number
576  */
577 CVC.prototype.getCPI = function() {
578 	var cpido = this.body.find(CVC.TAG_CPI);
579 
580 	if (!cpido) {
581 		return null;
582 	}
583 
584 	return cpido.value.toUnsigned();
585 }
586 
587 
588 
589 /**
590  * Returns the certification authority reference (CAR).
591  *
592  * @return the CAR or null
593  * @type PublicKeyReference
594  */
595 CVC.prototype.getCAR = function() {
596 	var cardo = this.body.find(CVC.TAG_CAR);
597 
598 	if (!cardo) {
599 		return null;
600 	}
601 
602 	return new PublicKeyReference(cardo.value);
603 }
604 
605 
606 
607 /**
608  * Returns the certificate holder reference (CHR).
609  *
610  * @return the CHR
611  * @type PublicKeyReference
612  */
613 CVC.prototype.getCHR = function() {
614 	var chrdo = this.body.find(CVC.TAG_CHR);
615 
616 	if (!chrdo) {
617 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a CHR");
618 	}
619 
620 	return new PublicKeyReference(chrdo.value);
621 }
622 
623 
624 
625 /**
626  * Returns the certificate effective date (CED).
627  *
628  * @return the CED or null
629  * @type Date
630  */
631 CVC.prototype.getCED = function() {
632 	var ceddo = this.body.find(CVC.TAG_CED);
633 
634 	if (!ceddo) {
635 		return null
636 	}
637 
638 	var b = ceddo.value;
639 
640 	var d = new Date();
641 	d.setFullYear(b.byteAt(0) * 10 + b.byteAt(1) + 2000,
642 				  b.byteAt(2) * 10 + b.byteAt(3) - 1,
643 				  b.byteAt(4) * 10 + b.byteAt(5));
644 	d.setHours(12, 0, 0, 0);
645 	return d;
646 }
647 
648 
649 
650 /**
651  * Returns the certificate expiration date (CXD).
652  *
653  * @return the CXD or null
654  * @type Date
655  */
656 CVC.prototype.getCXD = function() {
657 	var cxddo = this.body.find(CVC.TAG_CXD);
658 
659 	if (!cxddo) {
660 		return null
661 	}
662 
663 	var b = cxddo.value;
664 
665 	var d = new Date();
666 	d.setFullYear(b.byteAt(0) * 10 + b.byteAt(1) + 2000,
667 				  b.byteAt(2) * 10 + b.byteAt(3) - 1,
668 				  b.byteAt(4) * 10 + b.byteAt(5));
669 	d.setHours(12, 0, 0, 0);
670 	return d;
671 }
672 
673 
674 
675 /**
676  * Returns the outer certification authority reference (CAR).
677  *
678  * @return the outer CAR or null
679  * @type PublicKeyReference
680  */
681 CVC.prototype.getOuterCAR = function() {
682 	if (!this.isAuthenticatedRequest()) {
683 		return null;
684 	}
685 	var cardo = this.asn.get(1);
686 
687 	if (!cardo) {
688 		return null
689 	}
690 
691 	return new PublicKeyReference(cardo.value);
692 }
693 
694 
695 
696 /**
697  * Returns the extension identified by the object identifier.
698  *
699  * @return the extension including the OID or null if not defined
700  * @type ASN1
701  */
702 CVC.prototype.getExtension = function(extoid) {
703 	var extdo = this.body.find(CVC.TAG_EXTN);
704 
705 	if (!extdo) {
706 		return null;
707 	}
708 
709 //	print(extdo);
710 
711 	for (var i = 0; i < extdo.elements; i++) {
712 		var ext = extdo.get(i);
713 		var oid = ext.get(0);
714 		assert(oid.tag == ASN1.OBJECT_IDENTIFIER);
715 		if (oid.value.equals(extoid)) {
716 			return ext;
717 		}
718 	}
719 	return null;
720 }
721 
722 
723 
724 /**
725  * Returns the Certificate Holder Authorization Template.
726  *
727  * @return the chat or null if not defined
728  * @type ASN1
729  */
730 CVC.prototype.getCHAT = function() {
731 	var chat = this.body.find(CVC.TAG_CHAT);
732 
733 	return chat;
734 }
735 
736 
737 
738 /**
739  * Returns the public key object identifier
740  *
741  * @returns the object identifier assigned to the public key
742  * @type ByteString
743  */
744 CVC.prototype.getPublicKeyOID = function() {
745 	var pdo = this.body.find(CVC.TAG_PUK);
746 	if (pdo == null) {
747 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
748 	}
749 
750 	var d = pdo.find(ASN1.OBJECT_IDENTIFIER);
751 	if (d == null) {
752 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Public key does not contain an object identifier");
753 	}
754 	return d.value;
755 }
756 
757 
758 
759 /**
760  * Decode a public key from the TR-03110 format
761  *
762  * @param {ASN1} pdo the public key data object
763  * @param {Key} key the key object to fill
764  */
765 CVC.decodeECPublicKey = function(pdo, key) {
766 
767 	var d = pdo.find(0x86);		// Public point
768 	if (d == null) {
769 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key value");
770 	}
771 
772 	var b = d.value.bytes(1);
773 	key.setComponent(Key.ECC_QX, b.left(b.length >> 1));
774 	key.setComponent(Key.ECC_QY, b.right(b.length >> 1));
775 
776 	var d = pdo.find(0x81);		// Prime modulus
777 	if (d != null) {
778 		key.setComponent(Key.ECC_P, d.value);
779 	}
780 
781 	var d = pdo.find(0x82);		// First coefficient a
782 	if (d != null) {
783 		key.setComponent(Key.ECC_A, d.value);
784 	}
785 
786 	var d = pdo.find(0x83);		// First coefficient b
787 	if (d != null) {
788 		key.setComponent(Key.ECC_B, d.value);
789 	}
790 
791 	var d = pdo.find(0x84);		// Base Point G
792 	if (d != null) {
793 		var b = d.value.bytes(1);
794 		key.setComponent(Key.ECC_GX, b.left(b.length >> 1));
795 		key.setComponent(Key.ECC_GY, b.right(b.length >> 1));
796 	}
797 
798 	var d = pdo.find(0x85);		// Order of the base point
799 	if (d != null) {
800 		key.setComponent(Key.ECC_N, d.value);
801 	}
802 
803 	var d = pdo.find(0x87);		// Cofactor f
804 	if (d != null) {
805 		key.setComponent(Key.ECC_H, d.value);
806 	}
807 }
808 
809 
810 
811 /**
812  * Returns the EC public key contained in the certificate.
813  *
814  * @param {Key} domParam optional domain parameter if they are not contained in certificate
815  * @return the public key object
816  * @type Key
817  */
818 CVC.prototype.getECPublicKey = function(domParam) {
819 	if (typeof(domParam) != "undefined") {
820 		var key = new Key(domParam);
821 	} else {
822 		var key = new Key();
823 	}
824 
825 	key.setType(Key.PUBLIC);
826 
827 	var pdo = this.body.find(CVC.TAG_PUK);
828 	if (pdo == null) {
829 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
830 	}
831 
832 	CVC.decodeECPublicKey(pdo, key);
833 
834 	return key;
835 }
836 
837 
838 
839 /**
840  * Returns the RSA public key contained in the certificate.
841  *
842  * @return the public key object
843  * @type Key
844  */
845 CVC.prototype.getRSAPublicKey = function() {
846 	var key = new Key();
847 
848 	key.setType(Key.PUBLIC);
849 
850 	var pdo = this.body.find(CVC.TAG_PUK);
851 	if (pdo == null) {
852 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
853 	}
854 
855 	var d = pdo.find(0x81);		// modulus
856 	if (d != null) {
857 		key.setComponent(Key.MODULUS, d.value);
858 	}
859 
860 	var d = pdo.find(0x82);		// public exponent
861 	if (d != null) {
862 		key.setComponent(Key.EXPONENT, d.value);
863 	}
864 
865 	return key;
866 }
867 
868 
869 
870 /**
871  * Returns the public key contained in the certificate.
872  *
873  * @param {Key} domParam optional domain parameter if they are not contained in certificate
874  * @return the public key object
875  * @type Key
876  */
877 CVC.prototype.getPublicKey = function(domParam) {
878 	var pkoid = this.getPublicKeyOID();
879 
880 	if (CVC.isECDSA(pkoid)) {
881 		return this.getECPublicKey(domParam);
882 	}
883 	return this.getRSAPublicKey();
884 }
885 
886 
887 
888 /**
889  * Determine the SubjectKeyIdentifier as defined in X.509
890  *
891  * @return the key identifier
892  * @type ByteString
893  */
894 CVC.prototype.determineKeyIdentifier = function() {
895 	var pdo = this.body.find(CVC.TAG_PUK);
896 	if (pdo == null) {
897 		throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
898 	}
899 
900 	var pkoid = this.getPublicKeyOID();
901 
902 	var keybin;
903 	if (CVC.isECDSA(pkoid)) {
904 		var d = pdo.find(0x86);		// Public point
905 		keybin = d.value;
906 	} else {
907 		var modulus = pdo.find(0x81).value;		// modulus
908 		if (modulus.byteAt(0) >= 0x80) {
909 			modulus = ByteString.valueOf(0).concat(modulus);
910 		}
911 		var exponent = pdo.find(0x82).value;		// public exponent
912 		var rsapub = new ASN1("RSAPublicKey", ASN1.SEQUENCE,
913 				      new ASN1("modulus", ASN1.INTEGER, modulus),
914 				      new ASN1("publicKeyExponent", ASN1.INTEGER, exponent));
915 
916 		keybin = rsapub.getBytes();
917 	}
918 	return CVC.crypto.digest(Crypto.SHA_1, keybin);
919 }
920 
921 
922 
923 /**
924  * Determine if this is an authenticated request
925  *
926  * @returns true, if authenticated request
927  * @type Boolean
928  */
929 CVC.prototype.isAuthenticatedRequest = function() {
930 	return (this.asn.tag == CVC.TAG_AT);
931 }
932 
933 
934 
935 /**
936  * Determine if this is a certificate request
937  *
938  * @returns true, if certificate request
939  * @type Boolean
940  */
941 CVC.prototype.isCertificateRequest = function() {
942 	if (this.isAuthenticatedRequest()) {
943 		return true;
944 	}
945 
946 	var ced = this.getCED();
947 	return ced == null;
948 }
949 
950 
951 
952 /**
953  * Determine if this is a countersigned authenticated request
954  *
955  * @returns true, if countersigned authenticated request
956  * @type Boolean
957  */
958 CVC.prototype.isCountersignedRequest = function() {
959 	if (!this.isAuthenticatedRequest()) {
960 		return false;
961 	}
962 	return (this.getCHR().getHolder() != this.getOuterCAR().getHolder());
963 }
964 
965 
966 
967 /**
968  * Determine if this certificate is expired
969  *
970  * @returns true, if certificate is expired
971  * @type Boolean
972  */
973 CVC.prototype.isExpired = function() {
974 	var now = new Date();
975 	now.setHours(12, 0, 0, 0);
976 	return (now.valueOf() > this.getCXD().valueOf());
977 }
978 
979 
980 
981 /**
982  * Verify certificate signature with public key
983  *
984  * @param {Key} puk the public key
985  * @param {ByteString} oid the signature algorithm
986  * @returns true if the signature is valid
987  * @type Boolean
988  */
989 CVC.prototype.verifyWith = function(crypto, puk, oid) {
990 	if (this.asn.tag == CVC.TAG_AT) {
991 		var signature = this.asn.get(0).get(1);
992 	} else {
993 		var signature = this.asn.get(1);
994 	}
995 
996 	if (typeof(oid) == "undefined") {
997 		var oid = this.getPublicKeyOID();
998 	}
999 	var mech = CVC.getSignatureMech(oid);
1000 
1001 	if (CVC.isECDSA(oid)) {
1002 		var signatureValue = CVC.wrapSignature(signature.value);
1003 	} else {
1004 		var signatureValue = signature.value;
1005 	}
1006 
1007 	return crypto.verify(puk, mech, this.body.getBytes(), signatureValue);
1008 }
1009 
1010 
1011 
1012 /**
1013  * Verify certificate signature with public key from card verifiable certificate
1014  *
1015  * @param {CVC} cvc the card verifiable certificate used to obtain the public key
1016  * @returns true if the signature is valid
1017  * @type Boolean
1018  */
1019 CVC.prototype.verifyWithCVC = function(crypto, cvc) {
1020 	return this.verifyWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID());
1021 }
1022 
1023 
1024 
1025 /**
1026  * Verify outer signature of an authenticated request with public key
1027  *
1028  * @param {Key} puk the public key
1029  * @param {ByteString} oid the signature algorithm
1030  * @returns true if the signature is valid
1031  * @type Boolean
1032  */
1033 CVC.prototype.verifyATWith = function(crypto, puk, oid) {
1034 	if (!this.isAuthenticatedRequest()) {
1035 		throw new GPError("CVC", GPError.INVALID_DATA, 0, "Not an authenticated request");
1036 	}
1037 
1038 	var signature = this.asn.get(2);
1039 	var signatureInput = this.asn.get(0).getBytes().concat(this.asn.get(1).getBytes());
1040 
1041 	if (typeof(oid) == "undefined") {
1042 		var oid = this.getPublicKeyOID();
1043 	}
1044 	var mech = CVC.getSignatureMech(oid);
1045 
1046 	if (CVC.isECDSA(oid)) {
1047 		var signatureValue = CVC.wrapSignature(signature.value);
1048 	} else {
1049 		var signatureValue = signature.value;
1050 	}
1051 	return crypto.verify(puk, mech, signatureInput, signatureValue);
1052 }
1053 
1054 
1055 
1056 /**
1057  * Verify outer signature of an authenticated request with public key from card verifiable certificate
1058  *
1059  * @param {CVC} cvc the card verifiable certificate used to obtain the public key
1060  * @returns true if the signature is valid
1061  * @type Boolean
1062  */
1063 CVC.prototype.verifyATWithCVC = function(crypto, cvc) {
1064 	return this.verifyATWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID());
1065 }
1066 
1067 
1068 
1069 /**
1070  * Returns the encoded certificate
1071  *
1072  * @return the DER encoded certificate
1073  * @type ByteString
1074  */
1075 CVC.prototype.getBytes = function() {
1076 	return this.bin;
1077 }
1078 
1079 
1080 
1081 /**
1082  * Returns the certificate as ASN1 structure
1083  *
1084  * @return the certificate as ASN1 structure
1085  * @type ASN1
1086  */
1087 CVC.prototype.getASN1 = function() {
1088 	return this.asn;
1089 }
1090 
1091 
1092 
1093 /**
1094  * Function to recursively walk the ASN.1 tree
1095  */
1096 CVC.decorateTree = function(node) {
1097 	var name = CVC.OBJECTNAMES[node.tag];
1098 
1099 	if (name) {
1100 		node.setName(name);
1101 	}
1102 
1103 	if (node.isconstructed) {
1104 		for (var i = 0; i < node.elements; i++) {
1105 			CVC.decorateTree(node.get(i));
1106 		}
1107 	}
1108 }
1109 
1110 
1111 
1112 /**
1113  * Decorate the ASN.1 object with the correct name
1114  */
1115 CVC.prototype.decorate = function() {
1116 	CVC.decorateTree(this.asn);
1117 	var cxddo = this.body.find(CVC.TAG_CXD);
1118 	if (cxddo == null) {
1119 		if (this.asn.tag == CVC.TAG_AT) {
1120 			this.asn.setName("Authenticated CVC Request");
1121 		} else {
1122 			this.asn.setName("CVC Request");
1123 		}
1124 	}
1125 }
1126 
1127 
1128 
1129 /**
1130  * Return list of rights granted by the certificate
1131  *
1132  * @returns the list of rights
1133  * @type String[]
1134  */
1135 CVC.prototype.getRightsAsList = function() {
1136 	var list = [];
1137 
1138 	var rtab;
1139 	var chat = this.getCHAT();
1140 	if (chat == null) {
1141 		return list;
1142 	}
1143 
1144 	var oid = chat.get(0).value;
1145 
1146 	if (oid.equals(CVC.idIS)) {
1147 		rtab = CVC.ISRIGHTS;
1148 	} else if (oid.equals(CVC.idAT)) {
1149 		rtab = CVC.ATRIGHTS;
1150 	} else if (oid.equals(CVC.idST)) {
1151 		rtab = CVC.STRIGHTS;
1152 	} else {
1153 		return null;
1154 	}
1155 
1156 	var mask = chat.get(1).value;
1157 	var c = 0;
1158 	for (var i = mask.length - 1; i >= 0; i--) {
1159 		var akku = mask.byteAt(i);
1160 		for (var j = 0; j < (i == 0 ? 6 : 8); j++) {
1161 			if (akku & 1) {
1162 				list.push(rtab[c]);
1163 			}
1164 			c++;
1165 			akku >>= 1;
1166 		}
1167 	}
1168 	return list;
1169 }
1170 
1171 
1172 
1173 /**
1174  * Return position of certificate in PKI hierachie
1175  *
1176  * @returns Position in PKI hierachie: 0 unknown, 1-CVCA, 2-DVCA, 3-Terminal
1177  * @type Number
1178  */
1179 CVC.prototype.getLevel = function() {
1180 	var chat = this.getCHAT();
1181 	if (!chat) {
1182 		return 0;
1183 	}
1184 
1185 	var l = chat.get(1).value.byteAt(0) & 0xC0;
1186 	if (l == 0xC0) {
1187 		return 1;
1188 	}
1189 	return l == 0x00 ? 3 : 2;
1190 }
1191 
1192 
1193 
1194 /**
1195  * Return a string describing the certificate type
1196  *
1197  * @returns a describing string
1198  * @type String
1199  */
1200 CVC.prototype.getType = function() {
1201 	var ced = this.getCED();
1202 	var chat = this.getCHAT();
1203 
1204 	// Decode certificate / request type
1205 	var str = "CVC ";
1206 	if (ced == null) {
1207 		if (this.asn.tag == CVC.TAG_AT) {
1208 			str = "AT-CVREQ ";
1209 		} else {
1210 			str = "CVREQ ";
1211 		}
1212 	}
1213 
1214 	// Decode CA type
1215 	if (chat != null) {
1216 		var oid = chat.get(0).value;
1217 
1218 		if (oid.equals(CVC.idSC_HSM)) {
1219 			str += "id-SC-HSM ";
1220 			switch(chat.get(1).value.byteAt(0) & 0xC0) {
1221 				case 0xC0: str += "SRCA "; break;
1222 				case 0x80: str += "DICA "; break;
1223 				case 0x40: str += "MICA "; break;
1224 				case 0x00: str += "Device "; break;
1225 			}
1226 		} else {
1227 			var trustedDV = "";
1228 			var untrustedDV = "";
1229 
1230 			if (oid.equals(CVC.idIS)) {
1231 				str += "id-IS ";
1232 				trustedDV = "(official domestic) ";
1233 				untrustedDV = "(official foreign) ";
1234 			} else if (oid.equals(CVC.idAT)) {
1235 				str += "id-AT ";
1236 				trustedDV = "(official domestic) ";
1237 				untrustedDV = "(non-official / foreign) ";
1238 			} else if (oid.equals(CVC.idST)) {
1239 				str += "id-ST ";
1240 				trustedDV = "(accreditation body) ";
1241 				untrustedDV = "(certification service provider) ";
1242 			} else {
1243 				str += oid.toString(OID) + " ";
1244 			}
1245 
1246 			switch(chat.get(1).value.byteAt(0) & 0xC0) {
1247 				case 0xC0: str += "CVCA "; break;
1248 				case 0x80: str += "DV " + trustedDV; break;
1249 				case 0x40: str += "DV " + untrustedDV; break;
1250 				case 0x00: str += "Terminal "; break;
1251 			}
1252 		}
1253 	}
1254 
1255 	return str;
1256 }
1257 
1258 
1259 
1260 /**
1261  * Return a textual description of the certificate
1262  *
1263  * @returns a string containing information about the certificate
1264  * @type String
1265  */
1266 CVC.prototype.toString = function() {
1267 	var car = this.getCAR();
1268 	var ced = this.getCED();
1269 
1270 	var str = this.getType();
1271 
1272 	if (car) {
1273 		str += "CAR=" + car.toString() + " ";
1274 	}
1275 
1276 	str += "CHR=" + this.getCHR().toString() + " ";
1277 
1278 	if (ced) {
1279 		str += "CED=" + ced.toLocaleDateString() + " ";
1280 	}
1281 
1282 	var cxd = this.getCXD();
1283 	if (cxd) {
1284 		str += "CXD=" + cxd.toLocaleDateString() + " ";
1285 	}
1286 
1287 	if (this.isAuthenticatedRequest()) {
1288 		str += "oCAR=" + this.getOuterCAR().toString() + " ";
1289 	}
1290 
1291 	return str;
1292 }
1293 
1294 
1295 
1296 /**
1297  * Return object suitable for JSON encoding
1298  *
1299  * @returns a JSON encodable object
1300  * @type Object
1301  */
1302 CVC.prototype.toJSON = function() {
1303 	return { clazz: "CVC", value: this.bin };
1304 }
1305 
1306 
1307 
1308 /**
1309  * Recreate object from JSON encoding
1310  *
1311  * @returns the CVC object
1312  * @type Object
1313  */
1314 CVC.fromJSON = function(obj) {
1315 	assert(obj.clazz.equals("CVC"), "JSON object must contain clazz: CVC");
1316 	return new CVC(obj.value);
1317 }
1318