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