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