rittenhop-dev/versions/5.94.2/node_modules/node-jose/lib/jws/verify.js

272 lines
8.0 KiB
JavaScript
Raw Normal View History

2024-09-23 19:40:12 -04:00
/*!
* jws/verify.js - Verifies from a JWS
*
* Copyright (c) 2015 Cisco Systems, Inc. See LICENSE file.
*/
"use strict";
var clone = require("lodash/clone"),
merge = require("../util/merge"),
base64url = require("../util/base64url"),
AlgConfig = require("../util/algconfig"),
JWK = require("../jwk");
var DEFAULT_OPTIONS = {
algorithms: "*",
allowEmbeddedKey: false
};
/**
* @class JWS.Verifier
* @classdesc Parser of signed content.
*
* @description
* **NOTE:** this class cannot be instantiated directly. Instead call {@link
* JWS.createVerify}.
*/
var JWSVerifier = function(ks, globalOpts) {
var assumedKey,
keystore;
if (JWK.isKey(ks)) {
assumedKey = ks;
keystore = assumedKey.keystore;
} else if (JWK.isKeyStore(ks)) {
keystore = ks;
} else {
keystore = JWK.createKeyStore();
}
globalOpts = merge(DEFAULT_OPTIONS, globalOpts);
Object.defineProperty(this, "defaultKey", {
value: assumedKey || undefined,
enumerable: true
});
Object.defineProperty(this, "keystore", {
value: keystore,
enumerable: true
});
Object.defineProperty(this, "verify", {
value: function(input, opts) {
opts = merge({}, globalOpts, opts || {});
var extraHandlers = opts.handlers || {};
var handlerKeys = Object.keys(extraHandlers);
var algSpec = new AlgConfig(opts.algorithms);
if ("string" === typeof input) {
input = input.split(".");
input = {
payload: input[1],
signatures: [
{
protected: input[0],
signature: input[2]
}
]
};
} else if (!input || "object" !== typeof input) {
throw new Error("invalid input");
}
// fixup "flattened JSON" to look like "general JSON"
if (input.signature) {
input.signatures = [
{
protected: input.protected || undefined,
header: input.header || undefined,
signature: input.signature
}
];
}
// ensure signatories exists
var sigList = input.signatures || [{}];
// combine fields and decode signature per signatory
sigList = sigList.map(function(s) {
var header = clone(s.header || {});
var protect = s.protected ?
JSON.parse(base64url.decode(s.protected, "utf8")) :
{};
header = merge(header, protect);
var signature = base64url.decode(s.signature);
// process allowed algorithims
if (!algSpec.match(header.alg)) {
return Promise.reject(new Error("Algorithm not allowed: " + header.alg));
}
// process "crit" first
var crit = protect.crit;
if (crit) {
if (!Array.isArray(crit)) {
return Promise.reject(new Error("Invalid 'crit' header"));
}
for (var idx = 0; crit.length > idx; idx++) {
if (-1 === handlerKeys.indexOf(crit[idx])) {
return Promise.reject(new Error(
"Critical extension is not supported: " + crit[idx]
));
}
}
}
protect = Object.keys(protect);
return Promise.resolve({
protected: protect,
aad: s.protected || "",
header: header,
signature: signature
});
});
var promise = Promise.all(sigList);
promise = promise.then(function(sigList) {
return new Promise(function(resolve, reject) {
var processSig = function() {
var sig = sigList.shift();
if (!sig) {
reject(new Error("no key found"));
return;
}
sig = merge({}, sig, {
payload: input.payload
});
var p = Promise.resolve(sig);
// find the key
p = p.then(function(sig) {
var algKey;
// TODO: resolve jku, x5c, x5u
if (opts.allowEmbeddedKey && sig.header.jwk) {
algKey = JWK.asKey(sig.header.jwk);
} else if (opts.allowEmbeddedKey && sig.header.x5c) {
algKey = sig.header.x5c[0];
algKey = Buffer.from(algKey, "base64");
// TODO: callback to validate chain
algKey = JWK.asKey(algKey, "pkix");
} else {
algKey = Promise.resolve(assumedKey || keystore.get({
use: "sig",
alg: sig.header.alg,
kid: sig.header.kid
}));
}
return algKey.then(function(k) {
if (!k) {
return Promise.reject(new Error("key does not match"));
}
sig.key = k;
return sig;
});
});
// process any prepare-verify handlers
p = p.then(function(sig) {
var processing = [];
handlerKeys.forEach(function(h) {
h = extraHandlers[h];
var p;
if ("function" === typeof h) {
p = h(sig);
} else if ("object" === typeof h && "function" === typeof h.prepare) {
p = h.prepare(sig);
}
if (p) {
processing.push(Promise.resolve(p));
}
});
return Promise.all(processing).then(function() {
// don't actually care about individual handler results
// assume {sig} is updated
return sig;
});
});
// prepare verify inputs
p = p.then(function(sig) {
var aad = sig.aad || "",
payload = sig.payload || "";
var content = Buffer.alloc(1 + aad.length + payload.length),
pos = 0;
content.write(aad, pos, "ascii");
pos += aad.length;
content.write(".", pos, "ascii");
pos++;
if (Buffer.isBuffer(payload)) {
payload.copy(content, pos);
} else {
content.write(payload, pos, "binary");
}
sig.content = content;
return sig;
});
p = p.then(function(sig) {
return sig.key.verify(sig.header.alg,
sig.content,
sig.signature);
});
p = p.then(function(result) {
var payload = sig.payload;
payload = base64url.decode(payload);
return {
protected: sig.protected,
header: sig.header,
payload: payload,
signature: result.mac,
key: sig.key
};
});
// process any post-verify handlers
p = p.then(function(jws) {
var processing = [];
handlerKeys.forEach(function(h) {
h = extraHandlers[h];
var p;
if ("object" === typeof h && "function" === typeof h.complete) {
p = h.complete(jws);
}
if (p) {
processing.push(Promise.resolve(p));
}
});
return Promise.all(processing).then(function() {
// don't actually care about individual handler results
// assume {jws} is updated
return jws;
});
});
p.then(resolve, processSig);
};
processSig();
});
});
return promise;
}
});
};
/**
* @description
* Creates a new JWS.Verifier with the given Key or KeyStore.
*
* @param {JWK.Key|JWK.KeyStore} ks The Key or KeyStore to use for verification.
* @returns {JWS.Verifier} The new Verifier.
*/
function createVerify(ks, opts) {
var vfy = new JWSVerifier(ks, opts);
return vfy;
}
module.exports = {
verifier: JWSVerifier,
createVerify: createVerify
};