Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/algorithms/ec-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var clone = require("lodash/clone"),

var EC_KEYSIZES = {
"P-256": 256,
"BP-256": 256,
"P-384": 384,
"P-521": 521
};
Expand Down Expand Up @@ -86,6 +87,8 @@ function curveNameToOid(crv) {
return "1.3.132.0.34";
case "P-521":
return "1.3.132.0.35";
case "BP-256":
return "1.3.36.3.3.2.8.1.1.7";
default:
return null;
}
Expand Down
4 changes: 4 additions & 0 deletions lib/algorithms/ecdh.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var pick = require("lodash/pick");
function idealHash(curve) {
switch (curve) {
case "P-256":
case "BP-256":
return "SHA-256";
case "P-384":
return "SHA-384";
Expand Down Expand Up @@ -153,6 +154,9 @@ function ecdhDeriveFn() {
case "P-521":
curve = "secp521r1";
break;
case "BP-256":
curve = "brainpoolP256r1";
break;
default:
return Promise.reject(new Error("invalid curve: " + curve));
}
Expand Down
29 changes: 16 additions & 13 deletions lib/algorithms/ecdsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,26 @@ var ecUtil = require("./ec-util.js"),
helpers = require("./helpers.js"),
sha = require("./sha.js");

// TODO: Handle gracefully
function idealCurve(hash) {
switch (hash) {
case "SHA-256":
return "P-256";
return ["P-256", "BP-256"];
case "SHA-384":
return "P-384";
return ["P-384"];
case "SHA-512":
return "P-521";
return ["P-521"];
default:
throw new Error("unsupported hash: " + hash);
}
}

function ecdsaSignFN(hash) {
var curve = idealCurve(hash);
var curves = idealCurve(hash);

// ### Fallback implementation -- uses forge
var fallback = function(key, pdata /*, props */) {
if (curve !== key.crv) {
if (!curves.includes(key.crv)) {
return Promise.reject(new Error("invalid curve"));
}
var pk = ecUtil.convertToForge(key, false);
Expand All @@ -49,7 +50,7 @@ function ecdsaSignFN(hash) {

// ### WebCrypto API implementation
var webcrypto = function(key, pdata /*, props */) {
if (curve !== key.crv) {
if (!curves.includes(key.crv)) {
return Promise.reject(new Error("invalid curve"));
}
var pk = ecUtil.convertToJWK(key, false);
Expand Down Expand Up @@ -84,7 +85,7 @@ function ecdsaSignFN(hash) {
var nodeHash = hash.toLowerCase().replace("-", "");
if (helpers.nodeCrypto && helpers.nodeCrypto.getHashes().indexOf(nodeHash) > -1) {
nodejs = function(key, pdata) {
if (curve !== key.crv) {
if (!curves.includes(key.crv)) {
return Promise.reject(new Error("invalid curve"));
}

Expand Down Expand Up @@ -127,11 +128,11 @@ function ecdsaSignFN(hash) {
}

function ecdsaVerifyFN(hash) {
var curve = idealCurve(hash);
var curves = idealCurve(hash);

// ### Fallback implementation -- uses forge
var fallback = function(key, pdata, mac /*, props */) {
if (curve !== key.crv) {
if (!curves.includes(key.crv)) {
return Promise.reject(new Error("invalid curve"));
}
var pk = ecUtil.convertToForge(key, true);
Expand Down Expand Up @@ -160,7 +161,7 @@ function ecdsaVerifyFN(hash) {

// ### WebCrypto API implementation
var webcrypto = function(key, pdata, mac /* , props */) {
if (curve !== key.crv) {
if (!curves.includes(key.crv)) {
return Promise.reject(new Error("invalid curve"));
}
var pk = ecUtil.convertToJWK(key, true);
Expand Down Expand Up @@ -198,7 +199,7 @@ function ecdsaVerifyFN(hash) {
var nodeHash = hash.toLowerCase().replace("-", "");
if (helpers.nodeCrypto && helpers.nodeCrypto.getHashes().indexOf(nodeHash) > -1) {
nodejs = function(key, pdata, mac /* , props */) {
if (curve !== key.crv) {
if (!curves.includes(key.crv)) {
return Promise.reject(new Error("invalid curve"));
}

Expand Down Expand Up @@ -247,11 +248,13 @@ var ecdsa = {};
[
"ES256",
"ES384",
"ES512"
"ES512",
"BP256R1"
].forEach(function(name) {
var hash = name.replace(/ES(\d+)/g, function(m, size) {
var hash = name.replace(/(?:ES|BP)(\d+)\S*/g, function(m, size) {
return "SHA-" + size;
});

ecdsa[name] = {
sign: ecdsaSignFN(hash),
verify: ecdsaVerifyFN(hash)
Expand Down
11 changes: 4 additions & 7 deletions lib/algorithms/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,14 @@ exports.setupFallback = function(nodejs, webcrypto, fallback) {
var args = arguments,
promise;

function check(err) {
if (0 === err.message.indexOf("unsupported algorithm:")) {
impl = fallback;
return impl.apply(null, args);
}

return Promise.reject(err);
function check(_err) {
impl = fallback;
return impl.apply(null, args);
}

try {
promise = Promise.resolve(nodejs.apply(null, args));
promise = promise.catch(check);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nodejs.apply(null, args) is a synchronous operation so i think this statement is unreachable.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aaccenture good point! nodejs.apply is a synchronous function that can throw. However, it also returns a promise that can reject. Hence we need to both wrap the call in a try/catch, and call promise.catch here. I'll add a comment to clarify.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the link, I saw return Promise.reject() so this catch here save us from unhandled promise rejection. Legitimate.

} catch(err) {
promise = check(err);
}
Expand Down
17 changes: 16 additions & 1 deletion lib/deps/ecc/curves.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,31 @@ function secp521r1() {
return new X9ECParameters(curve, G, n, h);
}

function bp256() {
var p = new BigInteger("76884956397045344220809746629001649093037950200943055203735601445031516197751");
var a = new BigInteger("56698187605326110043627228396178346077120614539475214109386828188763884139993");
var b = new BigInteger("17577232497321838841075697789794520262950426058923084567046852300633325438902");
var curve = new ec.ECCurveFp(p, a, b);
var G = curve.decodePointHex("04"
+ "8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262"
+ "547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997");
var n = new BigInteger("76884956397045344220809746629001649092737531784414529538755519063063536359079");
var h = BigInteger.ONE;
return new X9ECParameters(curve, G, n, h);
}

// ----------------
// Public API

var CURVES = module.exports = {
"secp256r1": secp256r1(),
"secp384r1": secp384r1(),
"secp521r1": secp521r1()
"secp521r1": secp521r1(),
"brainpoolP256r1": bp256()
};

// also export NIST names
CURVES["P-256"] = CURVES.secp256r1;
CURVES["P-384"] = CURVES.secp384r1;
CURVES["P-521"] = CURVES.secp521r1;
CURVES["BP-256"] = CURVES.brainpoolP256r1;
17 changes: 11 additions & 6 deletions lib/jwk/eckey.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ var JWK = {
var SIG_ALGS = [
"ES256",
"ES384",
"ES512"
"ES512",
"BP256R1"
];
var WRAP_ALGS = [
"ECDH-ES",
Expand All @@ -35,6 +36,8 @@ function oidToCurveName(oid) {
return "P-384";
case "1.3.132.0.35":
return "P-521";
case "1.3.36.3.3.2.8.1.1.7":
return "BP-256";
default:
return null;
}
Expand Down Expand Up @@ -107,16 +110,18 @@ var JWKEcCfg = {
if (!keys.private) {
return [];
}
return SIG_ALGS.filter(function(a) {
return (a === ("ES" + len));
var sigAlgs = SIG_ALGS.filter(function(a) {
return (a === ("ES" + len) || a === ("BP" + len + "R1"));
});
return sigAlgs;
case "verify":
if (!keys.public) {
return [];
}
return SIG_ALGS.filter(function(a) {
return (a === ("ES" + len));
var sigAlgs = SIG_ALGS.filter(function(a) {
return (a === ("ES" + len) || a === ("BP" + len + "R1"));
});
return sigAlgs
}
},

Expand Down Expand Up @@ -285,7 +290,7 @@ var JWKEcFactory = {
crv = oidToCurveName(crv);
}
if (!crv) {
return null;
throw new Error('Unsupported curve specified')
}

if (!input.parsed) {
Expand Down
2 changes: 2 additions & 0 deletions lib/jws/verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ var JWSVerifier = function(ks, globalOpts) {
var processSig = function() {
var sig = sigList.shift();
if (!sig) {
// TODO: Here is a bug - the promise resolves twice with sigList = []
// in case of Error is thrown on line 227
reject(new Error("no key found"));
return;
}
Expand Down
4 changes: 2 additions & 2 deletions test/jwk/eckey-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ describe("jwk/EC", function() {
assert.deepEqual(algs, []);

algs = JWK.EC.config.algorithms(keys, "verify");
assert.deepEqual(algs, ["ES256"]);
assert.deepEqual(algs, ["ES256", "BP256R1"]);
});
it("returns suite for private key", function() {
var keys = pick(keyPair, "private");
Expand All @@ -248,7 +248,7 @@ describe("jwk/EC", function() {
assert.deepEqual(algs, ["ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"]);

algs = JWK.EC.config.algorithms(keys, "sign");
assert.deepEqual(algs, ["ES256"]);
assert.deepEqual(algs, ["ES256", "BP256R1"]);

algs = JWK.EC.config.algorithms(keys, "verify");
assert.deepEqual(algs, []);
Expand Down