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 * Return the x-coordinate of the EC Public Key. 984 * 985 * @return the key identifier 986 * @type ByteString 987 */ 988 CVC.prototype.getECPublicKeyIdentifier = function() { 989 var pdo = this.body.find(CVC.TAG_PUK); 990 if (pdo == null) { 991 throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key"); 992 } 993 994 var pkoid = this.getPublicKeyOID(); 995 if (!CVC.isECDSA(pkoid)) { 996 throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a EC Public Key"); 997 } 998 999 var d = pdo.find(0x86); // Public point 1000 var l = d.length >> 1; 1001 return d.value.bytes(1, l); 1002 } 1003 1004 1005 1006 /** 1007 * Determine if this is an authenticated request 1008 * 1009 * @returns true, if authenticated request 1010 * @type Boolean 1011 */ 1012 CVC.prototype.isAuthenticatedRequest = function() { 1013 return (this.asn.tag == CVC.TAG_AT); 1014 } 1015 1016 1017 1018 /** 1019 * Determine if this is a certificate request 1020 * 1021 * @returns true, if certificate request 1022 * @type Boolean 1023 */ 1024 CVC.prototype.isCertificateRequest = function() { 1025 if (this.isAuthenticatedRequest()) { 1026 return true; 1027 } 1028 1029 var ced = this.getCED(); 1030 return ced == null; 1031 } 1032 1033 1034 1035 /** 1036 * Determine if this is a countersigned authenticated request 1037 * 1038 * @returns true, if countersigned authenticated request 1039 * @type Boolean 1040 */ 1041 CVC.prototype.isCountersignedRequest = function() { 1042 if (!this.isAuthenticatedRequest()) { 1043 return false; 1044 } 1045 return (this.getCHR().getHolder() != this.getOuterCAR().getHolder()); 1046 } 1047 1048 1049 1050 /** 1051 * Determine if this certificate is expired 1052 * 1053 * @returns true, if certificate is expired 1054 * @type Boolean 1055 */ 1056 CVC.prototype.isExpired = function() { 1057 var now = new Date(); 1058 now.setHours(12, 0, 0, 0); 1059 return (now.valueOf() > this.getCXD().valueOf()); 1060 } 1061 1062 1063 1064 /** 1065 * Verify certificate signature with public key 1066 * 1067 * @param {Crypto} crypto the crypto instance to use for verification 1068 * @param {Key} puk the public key 1069 * @param {ByteString} oid the signature algorithm 1070 * @returns true if the signature is valid 1071 * @type Boolean 1072 */ 1073 CVC.prototype.verifyWith = function(crypto, puk, oid) { 1074 if (this.asn.tag == CVC.TAG_AT) { 1075 var signature = this.asn.get(0).get(1); 1076 } else { 1077 var signature = this.asn.get(1); 1078 } 1079 1080 if (typeof(oid) == "undefined") { 1081 var oid = this.getPublicKeyOID(); 1082 } 1083 var mech = CVC.getSignatureMech(oid, puk.getSize()); 1084 1085 if (CVC.isECDSA(oid)) { 1086 var signatureValue = CVC.wrapSignature(signature.value); 1087 this.domParam = puk; 1088 } else { 1089 var signatureValue = signature.value; 1090 } 1091 1092 return crypto.verify(puk, mech, this.body.getBytes(), signatureValue); 1093 } 1094 1095 1096 1097 /** 1098 * Verify certificate signature with public key from card verifiable certificate 1099 * 1100 * @param {CVC} cvc the card verifiable certificate used to obtain the public key 1101 * @returns true if the signature is valid 1102 * @type Boolean 1103 */ 1104 CVC.prototype.verifyWithCVC = function(crypto, cvc) { 1105 return this.verifyWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID()); 1106 } 1107 1108 1109 1110 /** 1111 * Verify outer signature of an authenticated request with public key 1112 * 1113 * @param {Key} puk the public key 1114 * @param {ByteString} oid the signature algorithm 1115 * @returns true if the signature is valid 1116 * @type Boolean 1117 */ 1118 CVC.prototype.verifyATWith = function(crypto, puk, oid) { 1119 if (!this.isAuthenticatedRequest()) { 1120 throw new GPError("CVC", GPError.INVALID_DATA, 0, "Not an authenticated request"); 1121 } 1122 1123 var signature = this.asn.get(2); 1124 var signatureInput = this.asn.get(0).getBytes().concat(this.asn.get(1).getBytes()); 1125 1126 if (typeof(oid) == "undefined") { 1127 var oid = this.getPublicKeyOID(); 1128 } 1129 var mech = CVC.getSignatureMech(oid); 1130 1131 if (CVC.isECDSA(oid)) { 1132 var signatureValue = CVC.wrapSignature(signature.value); 1133 } else { 1134 var signatureValue = signature.value; 1135 } 1136 return crypto.verify(puk, mech, signatureInput, signatureValue); 1137 } 1138 1139 1140 1141 /** 1142 * Verify outer signature of an authenticated request with public key from card verifiable certificate 1143 * 1144 * @param {CVC} cvc the card verifiable certificate used to obtain the public key 1145 * @returns true if the signature is valid 1146 * @type Boolean 1147 */ 1148 CVC.prototype.verifyATWithCVC = function(crypto, cvc) { 1149 return this.verifyATWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID()); 1150 } 1151 1152 1153 1154 /** 1155 * Returns the encoded certificate 1156 * 1157 * @return the DER encoded certificate 1158 * @type ByteString 1159 */ 1160 CVC.prototype.getBytes = function() { 1161 return this.bin; 1162 } 1163 1164 1165 1166 /** 1167 * Returns the certificate as ASN1 structure 1168 * 1169 * @return the certificate as ASN1 structure 1170 * @type ASN1 1171 */ 1172 CVC.prototype.getASN1 = function() { 1173 return this.asn; 1174 } 1175 1176 1177 1178 /** 1179 * Function to recursively walk the ASN.1 tree 1180 */ 1181 CVC.decorateTree = function(node) { 1182 var name = CVC.OBJECTNAMES[node.tag]; 1183 1184 if (name) { 1185 node.setName(name); 1186 } 1187 1188 if (node.isconstructed) { 1189 for (var i = 0; i < node.elements; i++) { 1190 CVC.decorateTree(node.get(i)); 1191 } 1192 } 1193 } 1194 1195 1196 1197 /** 1198 * Decorate the ASN.1 object with the correct name 1199 */ 1200 CVC.prototype.decorate = function() { 1201 CVC.decorateTree(this.asn); 1202 var cxddo = this.body.find(CVC.TAG_CXD); 1203 if (cxddo == null) { 1204 if (this.asn.tag == CVC.TAG_AT) { 1205 this.asn.setName("Authenticated CVC Request"); 1206 } else { 1207 this.asn.setName("CVC Request"); 1208 } 1209 } 1210 } 1211 1212 1213 1214 /** 1215 * Return list of rights granted by the certificate 1216 * 1217 * @returns the list of rights 1218 * @type String[] 1219 */ 1220 CVC.prototype.getRightsAsList = function() { 1221 var list = []; 1222 1223 var rtab; 1224 var chat = this.getCHAT(); 1225 if (chat == null) { 1226 return list; 1227 } 1228 1229 var oid = chat.get(0).value; 1230 1231 if (oid.equals(CVC.idIS)) { 1232 rtab = CVC.ISRIGHTS; 1233 } else if (oid.equals(CVC.idAT)) { 1234 rtab = CVC.ATRIGHTS; 1235 } else if (oid.equals(CVC.idST)) { 1236 rtab = CVC.STRIGHTS; 1237 } else { 1238 return null; 1239 } 1240 1241 var mask = chat.get(1).value; 1242 var c = 0; 1243 for (var i = mask.length - 1; i >= 0; i--) { 1244 var akku = mask.byteAt(i); 1245 for (var j = 0; j < (i == 0 ? 6 : 8); j++) { 1246 if (akku & 1) { 1247 list.push(rtab[c]); 1248 } 1249 c++; 1250 akku >>= 1; 1251 } 1252 } 1253 return list; 1254 } 1255 1256 1257 1258 /** 1259 * Return position of certificate in PKI hierachie 1260 * 1261 * @returns Position in PKI hierachie: 0 unknown, 1-CVCA, 2-DVCA, 3-Terminal 1262 * @type Number 1263 */ 1264 CVC.prototype.getLevel = function() { 1265 var chat = this.getCHAT(); 1266 if (!chat) { 1267 return 0; 1268 } 1269 1270 var l = chat.get(1).value.byteAt(0) & 0xC0; 1271 if (l == 0xC0) { 1272 return 1; 1273 } 1274 return l == 0x00 ? 3 : 2; 1275 } 1276 1277 1278 1279 /** 1280 * Return a string describing the certificate type 1281 * 1282 * @returns a describing string 1283 * @type String 1284 */ 1285 CVC.prototype.getType = function() { 1286 var ced = this.getCED(); 1287 var chat = this.getCHAT(); 1288 1289 // Decode certificate / request type 1290 var str = "CVC "; 1291 if (ced == null) { 1292 if (this.asn.tag == CVC.TAG_AT) { 1293 str = "AT-CVREQ "; 1294 } else { 1295 str = "CVREQ "; 1296 } 1297 } 1298 1299 // Decode CA type 1300 if (chat != null) { 1301 var oid = chat.get(0).value; 1302 1303 if (oid.equals(CVC.idSC_HSM) || oid.equals(CVC.idBW_SE)) { 1304 if (oid.equals(CVC.idSC_HSM)) { 1305 str += "id-SC-HSM "; 1306 } else { 1307 str += "id-BW-SE "; 1308 } 1309 switch(chat.get(1).value.byteAt(0) & 0xC0) { 1310 case 0xC0: str += "SRCA "; break; 1311 case 0x80: str += "DICA "; break; 1312 case 0x40: str += "MICA "; break; 1313 case 0x00: str += "Device "; break; 1314 } 1315 } else { 1316 var trustedDV = ""; 1317 var untrustedDV = ""; 1318 1319 if (oid.equals(CVC.idIS)) { 1320 str += "id-IS "; 1321 trustedDV = "(official domestic) "; 1322 untrustedDV = "(official foreign) "; 1323 } else if (oid.equals(CVC.idAT)) { 1324 str += "id-AT "; 1325 trustedDV = "(official domestic) "; 1326 untrustedDV = "(non-official / foreign) "; 1327 } else if (oid.equals(CVC.idST)) { 1328 str += "id-ST "; 1329 trustedDV = "(accreditation body) "; 1330 untrustedDV = "(certification service provider) "; 1331 } else { 1332 str += oid.toString(OID) + " "; 1333 } 1334 1335 switch(chat.get(1).value.byteAt(0) & 0xC0) { 1336 case 0xC0: str += "CVCA "; break; 1337 case 0x80: str += "DV " + trustedDV; break; 1338 case 0x40: str += "DV " + untrustedDV; break; 1339 case 0x00: str += "Terminal "; break; 1340 } 1341 } 1342 } 1343 1344 return str; 1345 } 1346 1347 1348 1349 /** 1350 * Return a textual description of the certificate 1351 * 1352 * @returns a string containing information about the certificate 1353 * @type String 1354 */ 1355 CVC.prototype.toString = function() { 1356 var car = this.getCAR(); 1357 var ced = this.getCED(); 1358 1359 var str = this.getType(); 1360 1361 if (car) { 1362 str += "CAR=" + car.toString() + " "; 1363 } 1364 1365 str += "CHR=" + this.getCHR().toString() + " "; 1366 1367 if (ced) { 1368 str += "CED=" + ced.toLocaleDateString() + " "; 1369 } 1370 1371 var cxd = this.getCXD(); 1372 if (cxd) { 1373 str += "CXD=" + cxd.toLocaleDateString() + " "; 1374 } 1375 1376 if (this.isAuthenticatedRequest()) { 1377 str += "oCAR=" + this.getOuterCAR().toString() + " "; 1378 } 1379 1380 return str; 1381 } 1382 1383 1384 1385 /** 1386 * Return object suitable for JSON encoding 1387 * 1388 * @returns a JSON encodable object 1389 * @type Object 1390 */ 1391 CVC.prototype.toJSON = function() { 1392 return { clazz: "CVC", value: this.bin }; 1393 } 1394 1395 1396 1397 /** 1398 * Recreate object from JSON encoding 1399 * 1400 * @returns the CVC object 1401 * @type Object 1402 */ 1403 CVC.fromJSON = function(obj) { 1404 assert(obj.clazz.equals("CVC"), "JSON object must contain clazz: CVC"); 1405 return new CVC(obj.value); 1406 } 1407