cvc.js
Summary
Support for card verifiable certificates and certificate requests according to EAC 1.1/2.0
Class Summary
|
CVC |
Class implementing a decoder for card verifiable certificates or requests according to
Extended Access Control (EAC) as defined in BSI TR-03110 1.11 and 2.02. |
load("tools/eccutils.js");
if (typeof(__ScriptingServer) == "undefined") {
load("publickeyreference.js");
}
function CVC() {
if (arguments.length > 0) {
var arg = arguments[0];
if (arg instanceof ASN1) {
this.asn = arg;
this.bin = this.asn.getBytes();
} else if (arg instanceof ByteString) {
this.bin = arg;
this.asn = new ASN1(this.bin);
} else {
throw new GPError("CVC", GPError.INVALID_DATA, 0, "Argument must be of type ByteString or ASN1");
}
if (this.asn.tag == CVC.TAG_AT) {
this.body = this.asn.get(0).get(0);
} else if (this.asn.tag == CVC.TAG_CVC) {
this.body = this.asn.get(0);
} else {
throw new GPError("CVC", GPError.INVALID_DATA, 0, "Argument is neither a CVC or CVC request");
}
}
}
CVC.TAG_AT = 0x67;
CVC.TAG_CVC = 0x7F21;
CVC.TAG_BODY = 0x7F4E;
CVC.TAG_CPI = 0x5F29;
CVC.TAG_CAR = 0x42;
CVC.TAG_PUK = 0x7F49;
CVC.TAG_ECC_P = 0x81;
CVC.TAG_ECC_A = 0x82;
CVC.TAG_ECC_B = 0x83;
CVC.TAG_ECC_G = 0x84;
CVC.TAG_ECC_N = 0x85;
CVC.TAG_ECC_Q = 0x86;
CVC.TAG_ECC_H = 0x87;
CVC.TAG_CHR = 0x5F20;
CVC.TAG_CHAT = 0x7F4C;
CVC.TAG_EXTN = 0x65;
CVC.TAG_CED = 0x5F25;
CVC.TAG_CXD = 0x5F24;
CVC.TAG_SIG = 0x5F37;
CVC.OBJECTNAMES = []
CVC.OBJECTNAMES[CVC.TAG_AT] = "Authentication Template";
CVC.OBJECTNAMES[CVC.TAG_CVC] = "CV Certificate";
CVC.OBJECTNAMES[CVC.TAG_BODY] = "Certificate Body";
CVC.OBJECTNAMES[CVC.TAG_CPI] = "Certificate Profile Indicator";
CVC.OBJECTNAMES[CVC.TAG_CAR] = "Certification Authority Reference";
CVC.OBJECTNAMES[CVC.TAG_PUK] = "Public Key";
CVC.OBJECTNAMES[CVC.TAG_ECC_P] = "Prime/Modulus";
CVC.OBJECTNAMES[CVC.TAG_ECC_A] = "First coefficient a/Exponent";
CVC.OBJECTNAMES[CVC.TAG_ECC_B] = "Second coefficient b";
CVC.OBJECTNAMES[CVC.TAG_ECC_G] = "Base Point G";
CVC.OBJECTNAMES[CVC.TAG_ECC_N] = "Order of the base point";
CVC.OBJECTNAMES[CVC.TAG_ECC_Q] = "Public Point y";
CVC.OBJECTNAMES[CVC.TAG_ECC_H] = "Cofactor f";
CVC.OBJECTNAMES[CVC.TAG_CHR] = "Certificate Holder Reference";
CVC.OBJECTNAMES[CVC.TAG_CHAT] = "Certificate Holder Authentication Template";
CVC.OBJECTNAMES[CVC.TAG_EXTN] = "Extension";
CVC.OBJECTNAMES[CVC.TAG_CED] = "Certificate Effective Date";
CVC.OBJECTNAMES[CVC.TAG_CXD] = "Certificate Expiration Date";
CVC.OBJECTNAMES[CVC.TAG_SIG] = "Signature";
CVC.ISRIGHTS = [
"Read access to ePassport application: DG 3 (Fingerprint)",
"Read access to ePassport application: DG 4 (Iris)",
"RFU (Bit 3)",
"RFU (Bit 4)",
"RFU (Bit 5)",
"Read access to eID application"
];
CVC.idIS = new ByteString("id-IS", OID);
CVC.ATRIGHTS = [
"Age Verification",
"Community ID Verification",
"Restricted Identification",
"Privileged Terminal",
"CAN allowed",
"PIN Management",
"Install Certificate",
"Install Qualified Certificate",
"Read Access DG 1 (Document Type)",
"Read Access DG 2 (Issuing State)",
"Read Access DG 3 (Date of Expiration)",
"Read Access DG 4 (Given Name)",
"Read Access DG 5 (Surname)",
"Read Access DG 6 (Pseudonym)",
"Read Access DG 7 (Academic Grade)",
"Read Access DG 8 (Date of Birth)",
"Read Access DG 9 (Place of Birth)",
"Read Access DG 10",
"Read Access DG 11",
"Read Access DG 12",
"Read Access DG 13",
"Read Access DG 14",
"Read Access DG 15",
"Read Access DG 16",
"Read Access DG 17 (Place of Residence)",
"Read Access DG 18 (Community ID)",
"Read Access DG 19 (Conditions I-eAT)",
"Read Access DG 20 (Conditions II-eAT)",
"Read Access DG 21",
"RFU (Bit 29)",
"RFU (Bit 30)",
"RFU (Bit 31)",
"RFU (Bit 32)",
"Write Access DG 21",
"Write Access DG 20 (Conditions II-eAT)",
"Write Access DG 19 (Conditions I-eAT)",
"Write Access DG 18 (Community ID)",
"Write Access DG 17 (Place of Residence)"
];
CVC.idAT = new ByteString("id-AT", OID);
CVC.STRIGHTS = [
"Generate electronic signature",
"Generate qualified electronic signature",
"RFU (Bit 2)",
"RFU (Bit 3)",
"RFU (Bit 4)",
"RFU (Bit 5)"
];
CVC.idST = new ByteString("id-ST", OID);
CVC.idSC_HSM = new ByteString("2B0601040181C31F030101", HEX);
CVC.id_TA_ECDSA = new ByteString("id-TA-ECDSA", OID);
CVC.id_TA_ECDSA_SHA_1 = new ByteString("id-TA-ECDSA-SHA-1", OID);
CVC.id_TA_ECDSA_SHA_224 = new ByteString("id-TA-ECDSA-SHA-224", OID);
CVC.id_TA_ECDSA_SHA_256 = new ByteString("id-TA-ECDSA-SHA-256", OID);
CVC.id_TA_ECDSA_SHA_384 = new ByteString("id-TA-ECDSA-SHA-384", OID);
CVC.id_TA_ECDSA_SHA_512 = new ByteString("id-TA-ECDSA-SHA-512", OID);
CVC.id_TA_RSA_v1_5_SHA_1 = new ByteString("id-TA-RSA-v1-5-SHA-1", OID);
CVC.id_TA_RSA_v1_5_SHA_256 = new ByteString("id-TA-RSA-v1-5-SHA-256", OID);
CVC.id_TA_RSA_v1_5_SHA_512 = new ByteString("id-TA-RSA-v1-5-SHA-512", OID);
CVC.id_TA_RSA_PSS_SHA_1 = new ByteString("id-TA-RSA-PSS-SHA-1", OID);
CVC.id_TA_RSA_PSS_SHA_256 = new ByteString("id-TA-RSA-PSS-SHA-256", OID);
CVC.id_TA_RSA_PSS_SHA_512 = new ByteString("id-TA-RSA-PSS-SHA-512", OID);
CVC.getSignatureMech = function(oid) {
if (oid.equals(CVC.id_TA_ECDSA_SHA_1))
return Crypto.ECDSA_SHA1;
if (oid.equals(CVC.id_TA_ECDSA_SHA_224))
return Crypto.ECDSA_SHA224;
if (oid.equals(CVC.id_TA_ECDSA_SHA_256))
return Crypto.ECDSA_SHA256;
if (oid.equals(CVC.id_TA_ECDSA_SHA_384))
return Crypto.ECDSA_SHA384;
if (oid.equals(CVC.id_TA_ECDSA_SHA_512))
return Crypto.ECDSA_SHA512;
if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_1))
return Crypto.RSA_SHA1;
if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_256))
return Crypto.RSA_SHA256;
if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_512))
return Crypto.RSA_SHA512;
if (oid.equals(CVC.id_TA_RSA_PSS_SHA_1))
return Crypto.RSA_PSS_SHA1;
if (oid.equals(CVC.id_TA_RSA_PSS_SHA_256))
return Crypto.RSA_PSS_SHA256;
if (oid.equals(CVC.id_TA_RSA_PSS_SHA_512))
return Crypto.RSA_PSS_SHA512;
return -1;
}
CVC.getHashMech = function(oid) {
if (oid.equals(CVC.id_TA_ECDSA_SHA_1))
return Crypto.SHA_1;
if (oid.equals(CVC.id_TA_ECDSA_SHA_224))
return Crypto.SHA_224;
if (oid.equals(CVC.id_TA_ECDSA_SHA_256))
return Crypto.SHA_256;
if (oid.equals(CVC.id_TA_ECDSA_SHA_384))
return Crypto.SHA_384;
if (oid.equals(CVC.id_TA_ECDSA_SHA_512))
return Crypto.SHA_512;
if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_1))
return Crypto.SHA1;
if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_256))
return Crypto.SHA_256;
if (oid.equals(CVC.id_TA_RSA_v1_5_SHA_512))
return Crypto.SHA_512;
if (oid.equals(CVC.id_TA_RSA_PSS_SHA_1))
return Crypto.SHA_1;
if (oid.equals(CVC.id_TA_RSA_PSS_SHA_256))
return Crypto.SHA_256;
if (oid.equals(CVC.id_TA_RSA_PSS_SHA_512))
return Crypto.SHA_512;
return -1;
}
CVC.isECDSA = function(oid) {
return oid.startsWith(CVC.id_TA_ECDSA) == CVC.id_TA_ECDSA.length;
}
CVC.prototype.containsDomainParameter = function() {
var pdo = this.body.find(CVC.TAG_PUK);
if (pdo == null) {
return false;
}
var d = pdo.find(0x84);
return (d != null);
}
CVC.prototype.getCAR = function() {
var cardo = this.body.find(CVC.TAG_CAR);
if (!cardo) {
return null;
}
return new PublicKeyReference(cardo.value);
}
CVC.prototype.getCHR = function() {
var chrdo = this.body.find(CVC.TAG_CHR);
if (!chrdo) {
throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a CHR");
}
return new PublicKeyReference(chrdo.value);
}
CVC.prototype.getCED = function() {
var ceddo = this.body.find(CVC.TAG_CED);
if (!ceddo) {
return null
}
var b = ceddo.value;
var d = new Date();
d.setFullYear(b.byteAt(0) * 10 + b.byteAt(1) + 2000,
b.byteAt(2) * 10 + b.byteAt(3) - 1,
b.byteAt(4) * 10 + b.byteAt(5));
d.setHours(12, 0, 0, 0);
return d;
}
CVC.prototype.getCXD = function() {
var cxddo = this.body.find(CVC.TAG_CXD);
if (!cxddo) {
return null
}
var b = cxddo.value;
var d = new Date();
d.setFullYear(b.byteAt(0) * 10 + b.byteAt(1) + 2000,
b.byteAt(2) * 10 + b.byteAt(3) - 1,
b.byteAt(4) * 10 + b.byteAt(5));
d.setHours(12, 0, 0, 0);
return d;
}
CVC.prototype.getOuterCAR = function() {
if (!this.isAuthenticatedRequest()) {
return null;
}
var cardo = this.asn.get(1);
if (!cardo) {
return null
}
return new PublicKeyReference(cardo.value);
}
CVC.prototype.getExtension = function(extoid) {
var extdo = this.body.find(CVC.TAG_EXTN);
if (!extdo) {
return null;
}
for (var i = 0; i < extdo.length; i++) {
var ext = extdo.get(i);
var oid = ext.get(0);
assert(oid.tag == ASN1.OBJECT_IDENTIFIER);
if (oid.value.equals(extoid)) {
return ext;
}
}
return null;
}
CVC.prototype.getCHAT = function() {
var chat = this.body.find(CVC.TAG_CHAT);
return chat;
}
CVC.prototype.getPublicKeyOID = function() {
var pdo = this.body.find(CVC.TAG_PUK);
if (pdo == null) {
throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
}
var d = pdo.find(ASN1.OBJECT_IDENTIFIER);
if (d == null) {
throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Public key does not contain an object identifier");
}
return d.value;
}
CVC.decodeECPublicKey = function(pdo, key) {
var d = pdo.find(0x86);
if (d == null) {
throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key value");
}
var b = d.value.bytes(1);
key.setComponent(Key.ECC_QX, b.left(b.length >> 1));
key.setComponent(Key.ECC_QY, b.right(b.length >> 1));
var d = pdo.find(0x81);
if (d != null) {
key.setComponent(Key.ECC_P, d.value);
}
var d = pdo.find(0x82);
if (d != null) {
key.setComponent(Key.ECC_A, d.value);
}
var d = pdo.find(0x83);
if (d != null) {
key.setComponent(Key.ECC_B, d.value);
}
var d = pdo.find(0x84);
if (d != null) {
var b = d.value.bytes(1);
key.setComponent(Key.ECC_GX, b.left(b.length >> 1));
key.setComponent(Key.ECC_GY, b.right(b.length >> 1));
}
var d = pdo.find(0x85);
if (d != null) {
key.setComponent(Key.ECC_N, d.value);
}
var d = pdo.find(0x87);
if (d != null) {
key.setComponent(Key.ECC_H, d.value);
}
}
CVC.prototype.getECPublicKey = function(domParam) {
if (typeof(domParam) != "undefined") {
var key = new Key(domParam);
} else {
var key = new Key();
}
key.setType(Key.PUBLIC);
var pdo = this.body.find(CVC.TAG_PUK);
if (pdo == null) {
throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
}
CVC.decodeECPublicKey(pdo, key);
return key;
}
CVC.prototype.getRSAPublicKey = function() {
var key = new Key();
key.setType(Key.PUBLIC);
var pdo = this.body.find(CVC.TAG_PUK);
if (pdo == null) {
throw new GPError("CVC", GPError.OBJECT_NOT_FOUND, 0, "Certificate does not contain a public key");
}
var d = pdo.find(0x81);
if (d != null) {
key.setComponent(Key.MODULUS, d.value);
}
var d = pdo.find(0x82);
if (d != null) {
key.setComponent(Key.EXPONENT, d.value);
}
return key;
}
CVC.prototype.getPublicKey = function(domParam) {
var pkoid = this.getPublicKeyOID();
if (CVC.isECDSA(pkoid)) {
return this.getECPublicKey(domParam);
}
return this.getRSAPublicKey();
}
CVC.prototype.isAuthenticatedRequest = function() {
return (this.asn.tag == CVC.TAG_AT);
}
CVC.prototype.isCertificateRequest = function() {
if (isAuthenticatedRequest()) {
return true;
}
var ced = this.getCED();
return ced == null;
}
CVC.prototype.isCountersignedRequest = function() {
if (!this.isAuthenticatedRequest()) {
return false;
}
return (this.getCHR().getHolder() != this.getOuterCAR().getHolder());
}
CVC.prototype.isExpired = function() {
var now = new Date();
now.setHours(12, 0, 0, 0);
return (now.valueOf() > this.getCXD().valueOf());
}
CVC.prototype.verifyWith = function(crypto, puk, oid) {
if (this.asn.tag == CVC.TAG_AT) {
var signature = this.asn.get(0).get(1);
} else {
var signature = this.asn.get(1);
}
if (typeof(oid) == "undefined") {
var oid = this.getPublicKeyOID();
}
var mech = CVC.getSignatureMech(oid);
if (CVC.isECDSA(oid)) {
var signatureValue = ECCUtils.wrapSignature(signature.value);
} else {
var signatureValue = signature.value;
}
return crypto.verify(puk, mech, this.body.getBytes(), signatureValue);
}
CVC.prototype.verifyWithCVC = function(crypto, cvc) {
return this.verifyWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID());
}
CVC.prototype.verifyATWith = function(crypto, puk, oid) {
if (!this.isAuthenticatedRequest()) {
throw new GPError("CVC", GPError.INVALID_DATA, 0, "Not an authenticated request");
}
var signature = this.asn.get(2);
var signatureInput = this.asn.get(0).getBytes().concat(this.asn.get(1).getBytes());
if (typeof(oid) == "undefined") {
var oid = this.getPublicKeyOID();
}
var mech = CVC.getSignatureMech(oid);
if (CVC.isECDSA(oid)) {
var signatureValue = ECCUtils.wrapSignature(signature.value);
} else {
var signatureValue = signature.value;
}
return crypto.verify(puk, mech, signatureInput, signatureValue);
}
CVC.prototype.verifyATWithCVC = function(crypto, cvc) {
return this.verifyATWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID());
}
CVC.prototype.getBytes = function() {
return this.bin;
}
CVC.prototype.getASN1 = function() {
return this.asn;
}
CVC.decorateTree = function(node) {
var name = CVC.OBJECTNAMES[node.tag];
if (name) {
node.setName(name);
}
if (node.isconstructed) {
for (var i = 0; i < node.elements; i++) {
CVC.decorateTree(node.get(i));
}
}
}
CVC.prototype.decorate = function() {
CVC.decorateTree(this.asn);
var cxddo = this.body.find(CVC.TAG_CXD);
if (cxddo == null) {
if (this.asn.tag == CVC.TAG_AT) {
this.asn.setName("Authenticated CVC Request");
} else {
this.asn.setName("CVC Request");
}
}
}
CVC.prototype.getRightsAsList = function() {
var list = [];
var rtab;
var chat = this.getCHAT();
if (chat == null) {
return list;
}
var oid = chat.get(0).value;
if (oid.equals(CVC.idIS)) {
rtab = CVC.ISRIGHTS;
} else if (oid.equals(CVC.idAT)) {
rtab = CVC.ATRIGHTS;
} else if (oid.equals(CVC.idST)) {
rtab = CVC.STRIGHTS;
} else {
return null;
}
var mask = chat.get(1).value;
var c = 0;
for (var i = mask.length - 1; i >= 0; i--) {
var akku = mask.byteAt(i);
for (var j = 0; j < (i == 0 ? 6 : 8); j++) {
if (akku & 1) {
list.push(rtab[c]);
}
c++;
akku >>= 1;
}
}
return list;
}
CVC.prototype.getType = function() {
var ced = this.getCED();
var chat = this.getCHAT();
var str = "CVC ";
if (ced == null) {
if (this.asn.tag == CVC.TAG_AT) {
str = "AT-CVREQ ";
} else {
str = "CVREQ ";
}
}
if (chat != null) {
var oid = chat.get(0).value;
var trustedDV = "";
var untrustedDV = "";
if (oid.equals(CVC.idIS)) {
str += "id-IS ";
trustedDV = "(official domestic) ";
untrustedDV = "(official foreign) ";
} else if (oid.equals(CVC.idAT)) {
str += "id-AT ";
trustedDV = "(official domestic) ";
untrustedDV = "(non-official / foreign) ";
} else if (oid.equals(CVC.idST)) {
str += "id-ST ";
trustedDV = "(accreditation body) ";
untrustedDV = "(certification service provider) ";
} else if (oid.equals(CVC.idSC_HSM)) {
str += "id-SC-HSM ";
trustedDV = "";
} else {
str += oid.toString(OID) + " ";
}
switch(chat.get(1).value.byteAt(0) & 0xC0) {
case 0xC0: str += "CVCA "; break;
case 0x80: str += "DV " + trustedDV; break;
case 0x40: str += "DV " + untrustedDV; break;
case 0x00: str += "Terminal "; break;
}
}
return str;
}
CVC.prototype.toString = function() {
var car = this.getCAR();
var ced = this.getCED();
var str = this.getType();
if (car) {
str += "CAR=" + car.toString() + " ";
}
str += "CHR=" + this.getCHR().toString() + " ";
if (ced) {
str += "CED=" + ced.toLocaleDateString() + " ";
}
var cxd = this.getCXD();
if (cxd) {
str += "CXD=" + cxd.toLocaleDateString() + " ";
}
if (this.isAuthenticatedRequest()) {
str += "oCAR=" + this.getOuterCAR().toString() + " ";
}
return str;
}
Documentation generated by
JSDoc on Tue Sep 3 22:29:38 2013