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