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