node-jose-browserify
Version:
An advanced version of Cisco's node-jose module that works in both browser and the server.
386 lines (375 loc) • 11.6 kB
JavaScript
/*!
* jwk/helpers.js - JWK Internal Helper Functions and Constants
*
* Copyright (c) 2015 Cisco Systems, Inc. See LICENSE file.
*/
"use strict";
var clone = require("lodash").clone,
util = require("../util"),
forge = require("../deps/forge");
var ALGORITHMS = require("../algorithms");
// ### ASN.1 Validators
// Adapted from digitalbazaar/node-forge/js/asn1.js
// PrivateKeyInfo
var privateKeyValidator = {
name: "PrivateKeyInfo",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
value: [
{
// Version (INTEGER)
name: "PrivateKeyInfo.version",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.INTEGER,
constructed: false,
capture: "keyVersion"
},
{
name: "PrivateKeyInfo.privateKeyAlgorithm",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
value: [
{
name: "AlgorithmIdentifier.algorithm",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.OID,
constructed: false,
capture: "keyOid"
},
{
name: "AlgorithmIdentifier.parameters",
captureAsn1: "keyParams"
}
]
},
{
name: "PrivateKeyInfo",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.OCTETSTRING,
constructed: false,
capture: "keyValue"
}
]
};
// Adapted from digitalbazaar/node-forge/x509.js
var publicKeyValidator = {
name: "SubjectPublicKeyInfo",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
value: [
{
name: "SubjectPublicKeyInfo.AlgorithmIdentifier",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
value: [
{
name: "AlgorithmIdentifier.algorithm",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.OID,
constructed: false,
capture: "keyOid"
},
{
name: "AlgorithmIdentifier.parameters",
captureAsn1: "keyParams"
}
]
},
{
name: "SubjectPublicKeyInfo.subjectPublicKey",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.BITSTRING,
constructed: false,
capture: "keyValue"
}
]
};
// Adapted from digitalbazaar/node-forge/x509.js
var X509CertificateValidator = {
name: "Certificate",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
value: [
{
name: "Certificate.TBSCertificate",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: "certificate",
value: [
{
name: "Certificate.TBSCertificate.version",
tagClass: forge.asn1.Class.CONTEXT_SPECIFIC,
type: 0,
constructed: true,
optional: true,
value: [
{
name: "Certificate.TBSCertificate.version.integer",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.INTEGER,
constructed: false,
capture: "certVersion"
}
]
},
{
name: "Certificate.TBSCertificate.serialNumber",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.INTEGER,
constructed: false,
capture: "certSerialNumber"
},
{
name: "Certificate.TBSCertificate.signature",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
value: [
{
name: "Certificate.TBSCertificate.signature.algorithm",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.OID,
constructed: false,
capture: "certSignatureOid"
}, {
name: "Certificate.TBSCertificate.signature.parameters",
tagClass: forge.asn1.Class.UNIVERSAL,
optional: true,
captureAsn1: "certSignatureParams"
}
]
},
{
name: "Certificate.TBSCertificate.issuer",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: "certIssuer"
},
{
name: "Certificate.TBSCertificate.validity",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
// Note: UTC and generalized times may both appear so the capture
// names are based on their detected order, the names used below
// are only for the common case, which validity time really means
// "notBefore" and which means "notAfter" will be determined by order
value: [
{
// notBefore (Time) (UTC time case)
name: "Certificate.TBSCertificate.validity.notBefore (utc)",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.UTCTIME,
constructed: false,
optional: true,
capture: "certValidity1UTCTime"
},
{
// notBefore (Time) (generalized time case)
name: "Certificate.TBSCertificate.validity.notBefore (generalized)",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.GENERALIZEDTIME,
constructed: false,
optional: true,
capture: "certValidity2GeneralizedTime"
},
{
// notAfter (Time) (only UTC time is supported)
name: "Certificate.TBSCertificate.validity.notAfter (utc)",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.UTCTIME,
constructed: false,
optional: true,
capture: "certValidity3UTCTime"
},
{
// notAfter (Time) (only UTC time is supported)
name: "Certificate.TBSCertificate.validity.notAfter (generalized)",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.GENERALIZEDTIME,
constructed: false,
optional: true,
capture: "certValidity4GeneralizedTime"
}
]
}, {
// Name (subject) (RDNSequence)
name: "Certificate.TBSCertificate.subject",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: "certSubject"
},
// SubjectPublicKeyInfo
publicKeyValidator,
{
// issuerUniqueID (optional)
name: "Certificate.TBSCertificate.issuerUniqueID",
tagClass: forge.asn1.Class.CONTEXT_SPECIFIC,
type: 1,
constructed: true,
optional: true,
value: [
{
name: "Certificate.TBSCertificate.issuerUniqueID.id",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.BITSTRING,
constructed: false,
capture: "certIssuerUniqueId"
}
]
},
{
// subjectUniqueID (optional)
name: "Certificate.TBSCertificate.subjectUniqueID",
tagClass: forge.asn1.Class.CONTEXT_SPECIFIC,
type: 2,
constructed: true,
optional: true,
value: [
{
name: "Certificate.TBSCertificate.subjectUniqueID.id",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.BITSTRING,
constructed: false,
capture: "certSubjectUniqueId"
}
]
},
{
// Extensions (optional)
name: "Certificate.TBSCertificate.extensions",
tagClass: forge.asn1.Class.CONTEXT_SPECIFIC,
type: 3,
constructed: true,
captureAsn1: "certExtensions",
optional: true
}
]
},
{
// AlgorithmIdentifier (signature algorithm)
name: "Certificate.signatureAlgorithm",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
value: [
{
// algorithm
name: "Certificate.signatureAlgorithm.algorithm",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.OID,
constructed: false,
capture: "certSignatureOid"
},
{
name: "Certificate.TBSCertificate.signature.parameters",
tagClass: forge.asn1.Class.UNIVERSAL,
optional: true,
captureAsn1: "certSignatureParams"
}
]
},
{
// SignatureValue
name: "Certificate.signatureValue",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.BITSTRING,
constructed: false,
capture: "certSignature"
}
]
};
var INTERNALS = {
THUMBPRINT_KEY: "internal\u0000thumbprint",
THUMBPRINT_HASH: "SHA-256"
};
module.exports = {
validators: {
privateKey: privateKeyValidator,
publicKey: publicKeyValidator,
certificate: X509CertificateValidator
},
thumbprint: function(cfg, json, hash) {
if ("function" !== typeof cfg.thumbprint) {
return Promise.reject(new Error("thumbprint not supported"));
}
hash = (hash || INTERNALS.THUMBPRINT_HASH).toUpperCase();
var fields = cfg.thumbprint(json);
var input = Object.keys(fields).
sort().
map(function(k) {
var v = fields[k];
if (Buffer.isBuffer(v)) {
v = util.base64url.encode(v);
}
return JSON.stringify(k) + ":" + JSON.stringify(v);
});
input = "{" + input.join(",") + "}";
try {
return ALGORITHMS.digest(hash, Buffer.from(input, "utf8"));
} catch (err) {
return Promise.reject(err);
}
},
unpackProps: function(props, allowed) {
var output;
// apply all of the existing values
allowed.forEach(function(cfg) {
if (!(cfg.name in props)) {
return;
}
output = output || {};
var value = props[cfg.name];
switch (cfg.type) {
case "binary":
if (Buffer.isBuffer(value)) {
props[cfg.name] = util.base64url.encode(value);
} else {
value = util.base64url.decode(value);
}
break;
case "string":
case "number":
case "boolean":
break;
case "array":
value = [].concat(value);
break;
case "object":
value = clone(value);
break;
default:
// TODO: deep clone?
break;
}
output[cfg.name] = value;
});
// remove any from json that didn't apply
var check = output || {};
Object.keys(props).
forEach(function(n) {
if (n in check) { return; }
delete props[n];
});
return output;
},
COMMON_PROPS: [
{name: "kty", type: "string"},
{name: "kid", type: "string"},
{name: "use", type: "string"},
{name: "alg", type: "string"},
{name: "x5c", type: "array"},
{name: "x5t", type: "binary"},
{name: "x5u", type: "string"},
{name: "key_ops", type: "array"}
],
INTERNALS: INTERNALS
};