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