From a9f1d4b1959b5a12a50323f78be37201e709edd0 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Tue, 3 Dec 2019 15:30:56 +0100 Subject: [PATCH] refactor: introduce various registries to prepare for extension modules --- lib/help/consts.js | 23 --------- lib/help/generate_iv.js | 14 +---- lib/help/key_utils.js | 2 +- lib/jwa/aes_cbc_hmac_sha2.js | 12 +++-- lib/jwa/aes_gcm.js | 11 ++-- lib/jwa/aes_gcm_kw.js | 24 +++++---- lib/jwa/aes_kw.js | 11 ++-- lib/jwa/ecdh/dir.js | 20 ++++---- lib/jwa/ecdh/kw.js | 10 ++-- lib/jwa/ecdsa.js | 40 +++++++++++++-- lib/jwa/eddsa.js | 17 ++++--- lib/jwa/hmac.js | 3 +- lib/jwa/index.js | 35 +++++-------- lib/jwa/pbes2.js | 9 ++-- lib/jwa/rsaes.js | 19 ++++++- lib/jwa/rsassa.js | 10 +++- lib/jwa/rsassa_pss.js | 10 +++- lib/jwe/generate_cek.js | 4 +- lib/jwk/key/base.js | 46 +++++++++++++++-- lib/jwk/key/ec.js | 76 ++++----------------------- lib/jwk/key/oct.js | 84 +++++------------------------- lib/jwk/key/okp.js | 61 +++------------------- lib/jwk/key/rsa.js | 99 +++--------------------------------- lib/registry/ec_curves.js | 23 +++++++++ lib/registry/index.js | 15 ++++++ lib/registry/iv_lengths.js | 11 ++++ lib/registry/jwa.js | 8 +++ lib/registry/jwk.js | 38 ++++++++++++++ lib/registry/key_lengths.js | 8 +++ lib/registry/okp_curves.js | 9 ++++ test/jwk/key_ops.test.js | 8 ++- test/jwk/rsa.test.js | 20 ++++---- test/jwks/keystore.test.js | 3 +- 33 files changed, 367 insertions(+), 416 deletions(-) create mode 100644 lib/registry/ec_curves.js create mode 100644 lib/registry/index.js create mode 100644 lib/registry/iv_lengths.js create mode 100644 lib/registry/jwa.js create mode 100644 lib/registry/jwk.js create mode 100644 lib/registry/key_lengths.js create mode 100644 lib/registry/okp_curves.js diff --git a/lib/help/consts.js b/lib/help/consts.js index 55f1d3fc62..ca9571b931 100644 --- a/lib/help/consts.js +++ b/lib/help/consts.js @@ -1,5 +1,3 @@ -const { name: secp256k1 } = require('../jwk/key/secp256k1_crv') - module.exports.KEYOBJECT = Symbol('KEYOBJECT') module.exports.PRIVATE_MEMBERS = Symbol('PRIVATE_MEMBERS') module.exports.PUBLIC_MEMBERS = Symbol('PUBLIC_MEMBERS') @@ -18,24 +16,3 @@ const USES = new Set(Object.keys(USES_MAPPING)) module.exports.USES_MAPPING = USES_MAPPING module.exports.OPS = OPS module.exports.USES = USES - -module.exports.OKP_CURVES = new Set(['Ed25519', 'Ed448', 'X25519', 'X448']) -module.exports.EC_CURVES = new Set(['P-256', secp256k1, 'P-384', 'P-521']) -module.exports.ECDH_ALGS = ['ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW'] - -module.exports.KEYLENGTHS = { - 'A128CBC-HS256': 256, - 'A192CBC-HS384': 384, - 'A256CBC-HS512': 512, - A128GCM: 128, - A192GCM: 192, - A256GCM: 256 -} - -if ('electron' in process.versions) { - module.exports.OKP_CURVES.delete('Ed448') - module.exports.OKP_CURVES.delete('X25519') - module.exports.OKP_CURVES.delete('X448') - module.exports.EC_CURVES.delete(secp256k1) - module.exports.ECDH_ALGS.splice(1, module.exports.ECDH_ALGS.length - 1) -} diff --git a/lib/help/generate_iv.js b/lib/help/generate_iv.js index 260daf9178..b9a58a20c9 100644 --- a/lib/help/generate_iv.js +++ b/lib/help/generate_iv.js @@ -1,15 +1,5 @@ const { randomBytes } = require('crypto') -const IVLENGTHS = { - 'A128CBC-HS256': 128 / 8, - A128GCM: 96 / 8, - A128GCMKW: 96 / 8, - 'A192CBC-HS384': 128 / 8, - A192GCM: 96 / 8, - A192GCMKW: 96 / 8, - 'A256CBC-HS512': 128 / 8, - A256GCM: 96 / 8, - A256GCMKW: 96 / 8 -} +const { IVLENGTHS } = require('../registry') -module.exports = alg => randomBytes(IVLENGTHS[alg]) +module.exports = alg => randomBytes(IVLENGTHS.get(alg) / 8) diff --git a/lib/help/key_utils.js b/lib/help/key_utils.js index 36b9578f52..838b47daf5 100644 --- a/lib/help/key_utils.js +++ b/lib/help/key_utils.js @@ -7,7 +7,7 @@ const { createPublicKey } = require('./key_object') const base64url = require('./base64url') const asn1 = require('./asn1') const computePrimes = require('./rsa_primes') -const { OKP_CURVES, EC_CURVES } = require('./consts') +const { OKP_CURVES, EC_CURVES } = require('../registry') const BN = asn1.bignum const oidHexToCurve = new Map([ diff --git a/lib/jwa/aes_cbc_hmac_sha2.js b/lib/jwa/aes_cbc_hmac_sha2.js index 11607a83ad..4a4e9c2ca4 100644 --- a/lib/jwa/aes_cbc_hmac_sha2.js +++ b/lib/jwa/aes_cbc_hmac_sha2.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert') -const { createCipheriv, createDecipheriv } = require('crypto') +const { createCipheriv, createDecipheriv, getCiphers } = require('crypto') const uint64be = require('../help/uint64be') const timingSafeEqual = require('../help/timing_safe_equal') @@ -58,14 +58,18 @@ const decrypt = (size, sign, { [KEYOBJECT]: keyObject }, ciphertext, { iv, tag = return cleartext } -module.exports = (JWA) => { +module.exports = (JWA, JWK) => { ['A128CBC-HS256', 'A192CBC-HS384', 'A256CBC-HS512'].forEach((jwaAlg) => { const size = parseInt(jwaAlg.substr(1, 3), 10) assert(!JWA.encrypt.has(jwaAlg), `encrypt alg ${jwaAlg} already registered`) assert(!JWA.decrypt.has(jwaAlg), `decrypt alg ${jwaAlg} already registered`) - JWA.encrypt.set(jwaAlg, encrypt.bind(undefined, size, JWA.sign.get(`HS${size * 2}`))) - JWA.decrypt.set(jwaAlg, decrypt.bind(undefined, size, JWA.sign.get(`HS${size * 2}`))) + const sign = JWA.sign.get(`HS${size * 2}`) + if (getCiphers().includes(`aes-${size}-cbc`)) { + JWA.encrypt.set(jwaAlg, encrypt.bind(undefined, size, sign)) + JWA.decrypt.set(jwaAlg, decrypt.bind(undefined, size, sign)) + JWK.oct.encrypt[jwaAlg] = JWK.oct.decrypt[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length / 2 === size + } }) } diff --git a/lib/jwa/aes_gcm.js b/lib/jwa/aes_gcm.js index cf50f18c73..0fa4f5dd55 100644 --- a/lib/jwa/aes_gcm.js +++ b/lib/jwa/aes_gcm.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert') -const { createCipheriv, createDecipheriv } = require('crypto') +const { createCipheriv, createDecipheriv, getCiphers } = require('crypto') const { KEYOBJECT } = require('../help/consts') const { JWEInvalid, JWEDecryptionFailed } = require('../errors') @@ -44,14 +44,17 @@ const decrypt = (size, { [KEYOBJECT]: keyObject }, ciphertext, { iv, tag = Buffe } } -module.exports = (JWA) => { +module.exports = (JWA, JWK) => { ['A128GCM', 'A192GCM', 'A256GCM'].forEach((jwaAlg) => { const size = parseInt(jwaAlg.substr(1, 3), 10) assert(!JWA.encrypt.has(jwaAlg), `encrypt alg ${jwaAlg} already registered`) assert(!JWA.decrypt.has(jwaAlg), `decrypt alg ${jwaAlg} already registered`) - JWA.encrypt.set(jwaAlg, encrypt.bind(undefined, size)) - JWA.decrypt.set(jwaAlg, decrypt.bind(undefined, size)) + if (getCiphers().includes(`aes-${size}-gcm`)) { + JWA.encrypt.set(jwaAlg, encrypt.bind(undefined, size)) + JWA.decrypt.set(jwaAlg, decrypt.bind(undefined, size)) + JWK.oct.encrypt[jwaAlg] = JWK.oct.decrypt[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length === size + } }) } diff --git a/lib/jwa/aes_gcm_kw.js b/lib/jwa/aes_gcm_kw.js index eb5121941c..6375ea8e61 100644 --- a/lib/jwa/aes_gcm_kw.js +++ b/lib/jwa/aes_gcm_kw.js @@ -3,23 +3,27 @@ const { strict: assert } = require('assert') const generateIV = require('../help/generate_iv') const base64url = require('../help/base64url') -module.exports = (JWA) => { +module.exports = (JWA, JWK) => { ['A128GCMKW', 'A192GCMKW', 'A256GCMKW'].forEach((jwaAlg) => { assert(!JWA.keyManagementEncrypt.has(jwaAlg), `keyManagementEncrypt alg ${jwaAlg} already registered`) assert(!JWA.keyManagementDecrypt.has(jwaAlg), `keyManagementDecrypt alg ${jwaAlg} already registered`) const encAlg = jwaAlg.substr(0, 7) + const size = parseInt(jwaAlg.substr(1, 3), 10) const encrypt = JWA.encrypt.get(encAlg) const decrypt = JWA.decrypt.get(encAlg) - JWA.keyManagementEncrypt.set(jwaAlg, (key, payload) => { - const iv = generateIV(jwaAlg) - const { ciphertext, tag } = encrypt(key, payload, { iv }) - return { - wrapped: ciphertext, - header: { tag: base64url.encodeBuffer(tag), iv: base64url.encodeBuffer(iv) } - } - }) - JWA.keyManagementDecrypt.set(jwaAlg, decrypt) + if (encrypt && decrypt) { + JWA.keyManagementEncrypt.set(jwaAlg, (key, payload) => { + const iv = generateIV(jwaAlg) + const { ciphertext, tag } = encrypt(key, payload, { iv }) + return { + wrapped: ciphertext, + header: { tag: base64url.encodeBuffer(tag), iv: base64url.encodeBuffer(iv) } + } + }) + JWA.keyManagementDecrypt.set(jwaAlg, decrypt) + JWK.oct.wrapKey[jwaAlg] = JWK.oct.unwrapKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length === size + } }) } diff --git a/lib/jwa/aes_kw.js b/lib/jwa/aes_kw.js index 2330f04c4f..180d225bda 100644 --- a/lib/jwa/aes_kw.js +++ b/lib/jwa/aes_kw.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert') -const { createCipheriv, createDecipheriv } = require('crypto') +const { createCipheriv, createDecipheriv, getCiphers } = require('crypto') const uint64be = require('../help/uint64be') const timingSafeEqual = require('../help/timing_safe_equal') @@ -88,14 +88,17 @@ const unwrapKey = (size, { [KEYOBJECT]: keyObject }, payload) => { return Buffer.concat(R) } -module.exports = (JWA) => { +module.exports = (JWA, JWK) => { ['A128KW', 'A192KW', 'A256KW'].forEach((jwaAlg) => { const size = parseInt(jwaAlg.substr(1, 3), 10) assert(!JWA.keyManagementEncrypt.has(jwaAlg), `keyManagementEncrypt alg ${jwaAlg} already registered`) assert(!JWA.keyManagementDecrypt.has(jwaAlg), `keyManagementDecrypt alg ${jwaAlg} already registered`) - JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, size)) - JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, size)) + if (getCiphers().includes(`aes${size}`)) { + JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, size)) + JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, size)) + JWK.oct.wrapKey[jwaAlg] = JWK.oct.unwrapKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length === size + } }) } diff --git a/lib/jwa/ecdh/dir.js b/lib/jwa/ecdh/dir.js index 9aae306812..1a16019b05 100644 --- a/lib/jwa/ecdh/dir.js +++ b/lib/jwa/ecdh/dir.js @@ -1,14 +1,15 @@ const { strict: assert } = require('assert') -const { KEYLENGTHS } = require('../../help/consts') +const { KEYLENGTHS } = require('../../registry') const { generateSync } = require('../../jwk/generate') +const { name: secp256k1 } = require('../../jwk/key/secp256k1_crv') const derive = require('./derive') const wrapKey = (key, payload, { enc }) => { const epk = generateSync(key.kty, key.crv) - const derivedKey = derive(enc, KEYLENGTHS[enc], epk, key) + const derivedKey = derive(enc, KEYLENGTHS.get(enc), epk, key) return { wrapped: derivedKey, @@ -17,15 +18,14 @@ const wrapKey = (key, payload, { enc }) => { } const unwrapKey = (key, payload, { apu, apv, epk, enc }) => { - return derive(enc, KEYLENGTHS[enc], key, epk, { apu, apv }) + return derive(enc, KEYLENGTHS.get(enc), key, epk, { apu, apv }) } -const ALG = 'ECDH-ES' +module.exports = (JWA, JWK) => { + assert(!JWA.keyManagementEncrypt.has('ECDH-ES'), 'keyManagementEncrypt alg ECDH-ES already registered') + assert(!JWA.keyManagementDecrypt.has('ECDH-ES'), 'keyManagementDecrypt alg ECDH-ES already registered') -module.exports = (JWA) => { - assert(!JWA.keyManagementEncrypt.has(ALG), `keyManagementEncrypt alg ${ALG} already registered`) - assert(!JWA.keyManagementDecrypt.has(ALG), `keyManagementDecrypt alg ${ALG} already registered`) - - JWA.keyManagementEncrypt.set(ALG, wrapKey) - JWA.keyManagementDecrypt.set(ALG, unwrapKey) + JWA.keyManagementEncrypt.set('ECDH-ES', wrapKey) + JWA.keyManagementDecrypt.set('ECDH-ES', unwrapKey) + JWK.EC.deriveKey['ECDH-ES'] = key => (key.use === 'enc' || key.use === undefined) && key.crv !== secp256k1 } diff --git a/lib/jwa/ecdh/kw.js b/lib/jwa/ecdh/kw.js index 12990b67c9..ebf8af38ac 100644 --- a/lib/jwa/ecdh/kw.js +++ b/lib/jwa/ecdh/kw.js @@ -2,6 +2,7 @@ const { strict: assert } = require('assert') const { KEYOBJECT } = require('../../help/consts') const { generateSync } = require('../../jwk/generate') +const { name: secp256k1 } = require('../../jwk/key/secp256k1_crv') const derive = require('./derive') @@ -22,7 +23,7 @@ const unwrapKey = (unwrap, derive, key, payload, { apu, apv, epk }) => { return unwrap({ [KEYOBJECT]: derivedKey }, payload) } -module.exports = (JWA) => { +module.exports = (JWA, JWK) => { ['ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW'].forEach((jwaAlg) => { assert(!JWA.keyManagementEncrypt.has(jwaAlg), `keyManagementEncrypt alg ${jwaAlg} already registered`) assert(!JWA.keyManagementDecrypt.has(jwaAlg), `keyManagementDecrypt alg ${jwaAlg} already registered`) @@ -32,7 +33,10 @@ module.exports = (JWA) => { const kwUnwrap = JWA.keyManagementDecrypt.get(kw) const keylen = parseInt(jwaAlg.substr(9, 3), 10) - JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, kwWrap, derive.bind(undefined, jwaAlg, keylen))) - JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, kwUnwrap, derive.bind(undefined, jwaAlg, keylen))) + if (kwWrap && kwUnwrap) { + JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, kwWrap, derive.bind(undefined, jwaAlg, keylen))) + JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, kwUnwrap, derive.bind(undefined, jwaAlg, keylen))) + JWK.EC.deriveKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.crv !== secp256k1 + } }) } diff --git a/lib/jwa/ecdsa.js b/lib/jwa/ecdsa.js index db7ba2e39d..91e56240d9 100644 --- a/lib/jwa/ecdsa.js +++ b/lib/jwa/ecdsa.js @@ -1,11 +1,12 @@ const { strict: assert } = require('assert') -const { sign: signOneShot, verify: verifyOneShot, createSign, createVerify } = require('crypto') +const { sign: signOneShot, verify: verifyOneShot, createSign, createVerify, getCurves } = require('crypto') const { derToJose, joseToDer } = require('../help/ecdsa_signatures') const { KEYOBJECT } = require('../help/consts') const resolveNodeAlg = require('../help/node_alg') const { asInput } = require('../help/key_object') const { dsaEncodingSupported } = require('../help/runtime_support') +const { name: secp256k1 } = require('../jwk/key/secp256k1_crv') let sign, verify @@ -49,8 +50,39 @@ if (dsaEncodingSupported) { // >= 13.2.0 } } -module.exports = (JWA) => { - ['ES256', 'ES384', 'ES512', 'ES256K'].forEach((jwaAlg) => { +const crvToAlg = (crv) => { + switch (crv) { + case 'P-256': + return 'ES256' + case secp256k1: + return 'ES256K' + case 'P-384': + return 'ES384' + case 'P-521': + return 'ES512' + } +} + +module.exports = (JWA, JWK) => { + const algs = [] + + if (getCurves().includes('prime256v1')) { + algs.push('ES256') + } + + if (getCurves().includes('secp256k1')) { + algs.push('ES256K') + } + + if (getCurves().includes('secp384r1')) { + algs.push('ES384') + } + + if (getCurves().includes('secp521r1')) { + algs.push('ES512') + } + + algs.forEach((jwaAlg) => { const nodeAlg = resolveNodeAlg(jwaAlg) assert(!JWA.sign.has(jwaAlg), `sign alg ${jwaAlg} already registered`) @@ -58,5 +90,7 @@ module.exports = (JWA) => { JWA.sign.set(jwaAlg, sign.bind(undefined, jwaAlg, nodeAlg)) JWA.verify.set(jwaAlg, verify.bind(undefined, jwaAlg, nodeAlg)) + JWK.EC.sign[jwaAlg] = key => key.private && JWK.EC.verify[jwaAlg](key) + JWK.EC.verify[jwaAlg] = key => (key.use === 'sig' || key.use === undefined) && crvToAlg(key.crv) === jwaAlg }) } diff --git a/lib/jwa/eddsa.js b/lib/jwa/eddsa.js index 7ebf188535..05012d43c6 100644 --- a/lib/jwa/eddsa.js +++ b/lib/jwa/eddsa.js @@ -2,6 +2,7 @@ const { strict: assert } = require('assert') const { sign: signOneShot, verify: verifyOneShot } = require('crypto') const { KEYOBJECT } = require('../help/consts') +const { edDSASupported } = require('../help/runtime_support') const sign = ({ [KEYOBJECT]: keyObject }, payload) => { return signOneShot(undefined, payload, keyObject) @@ -11,12 +12,14 @@ const verify = ({ [KEYOBJECT]: keyObject }, payload, signature) => { return verifyOneShot(undefined, payload, keyObject, signature) } -const ALG = 'EdDSA' +module.exports = (JWA, JWK) => { + assert(!JWA.sign.has('EdDSA'), 'sign alg EdDSA already registered') + assert(!JWA.verify.has('EdDSA'), 'verify alg EdDSA already registered') -module.exports = (JWA) => { - assert(!JWA.sign.has(ALG), `sign alg ${ALG} already registered`) - assert(!JWA.verify.has(ALG), `verify alg ${ALG} already registered`) - - JWA.sign.set(ALG, sign) - JWA.verify.set(ALG, verify) + if (edDSASupported) { + JWA.sign.set('EdDSA', sign) + JWA.verify.set('EdDSA', verify) + JWK.OKP.sign.EdDSA = key => key.private && JWK.OKP.verify.EdDSA(key) + JWK.OKP.verify.EdDSA = key => (key.use === 'sig' || key.use === undefined) && key.keyObject.asymmetricKeyType.startsWith('ed') + } } diff --git a/lib/jwa/hmac.js b/lib/jwa/hmac.js index a95932c138..c6ef260929 100644 --- a/lib/jwa/hmac.js +++ b/lib/jwa/hmac.js @@ -21,7 +21,7 @@ const verify = (jwaAlg, hmacAlg, { [KEYOBJECT]: keyObject }, payload, signature) return timingSafeEqual(actual, expected) } -module.exports = (JWA) => { +module.exports = (JWA, JWK) => { ['HS256', 'HS384', 'HS512'].forEach((jwaAlg) => { const hmacAlg = resolveNodeAlg(jwaAlg) @@ -30,5 +30,6 @@ module.exports = (JWA) => { JWA.sign.set(jwaAlg, sign.bind(undefined, jwaAlg, hmacAlg)) JWA.verify.set(jwaAlg, verify.bind(undefined, jwaAlg, hmacAlg)) + JWK.oct.sign[jwaAlg] = JWK.oct.verify[jwaAlg] = key => key.use === 'sig' || key.use === undefined }) } diff --git a/lib/jwa/index.js b/lib/jwa/index.js index 1d35cfc492..0505a0adee 100644 --- a/lib/jwa/index.js +++ b/lib/jwa/index.js @@ -1,35 +1,28 @@ const { JWKKeySupport, JOSENotSupported } = require('../errors') const { KEY_MANAGEMENT_ENCRYPT, KEY_MANAGEMENT_DECRYPT } = require('../help/consts') -const JWA = { - sign: new Map(), - verify: new Map(), - keyManagementEncrypt: new Map(), - keyManagementDecrypt: new Map(), - encrypt: new Map(), - decrypt: new Map() -} +const { JWA, JWK } = require('../registry') // sign, verify -require('./hmac')(JWA) -require('./ecdsa')(JWA) -require('./eddsa')(JWA) -require('./rsassa')(JWA) -require('./rsassa_pss')(JWA) +require('./hmac')(JWA, JWK) +require('./ecdsa')(JWA, JWK) +require('./eddsa')(JWA, JWK) +require('./rsassa_pss')(JWA, JWK) +require('./rsassa')(JWA, JWK) // encrypt, decrypt -require('./aes_cbc_hmac_sha2')(JWA) -require('./aes_gcm')(JWA) +require('./aes_cbc_hmac_sha2')(JWA, JWK) +require('./aes_gcm')(JWA, JWK) // wrapKey, unwrapKey -require('./rsaes')(JWA) -require('./aes_gcm_kw')(JWA) -require('./aes_kw')(JWA) +require('./rsaes')(JWA, JWK) +require('./aes_kw')(JWA, JWK) +require('./aes_gcm_kw')(JWA, JWK) // deriveKey -require('./pbes2')(JWA) -require('./ecdh/kw')(JWA) -require('./ecdh/dir')(JWA) +require('./pbes2')(JWA, JWK) +require('./ecdh/dir')(JWA, JWK) +require('./ecdh/kw')(JWA, JWK) const check = (key, op, alg) => { let label diff --git a/lib/jwa/pbes2.js b/lib/jwa/pbes2.js index f716936b89..62b4381607 100644 --- a/lib/jwa/pbes2.js +++ b/lib/jwa/pbes2.js @@ -38,7 +38,7 @@ const unwrapKey = (keylen, sha, concat, unwrap, { [KEYOBJECT]: keyObject }, payl return unwrap({ [KEYOBJECT]: derivedKey }, payload) } -module.exports = (JWA) => { +module.exports = (JWA, JWK) => { ['PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW'].forEach((jwaAlg) => { assert(!JWA.keyManagementEncrypt.has(jwaAlg), `keyManagementEncrypt alg ${jwaAlg} already registered`) assert(!JWA.keyManagementDecrypt.has(jwaAlg), `keyManagementDecrypt alg ${jwaAlg} already registered`) @@ -49,7 +49,10 @@ module.exports = (JWA) => { const keylen = parseInt(jwaAlg.substr(13, 3), 10) / 8 const sha = `sha${jwaAlg.substr(8, 3)}` - JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, keylen, sha, concatSalt.bind(undefined, jwaAlg), kwWrap)) - JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, keylen, sha, concatSalt.bind(undefined, jwaAlg), kwUnwrap)) + if (kwWrap && kwUnwrap) { + JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, keylen, sha, concatSalt.bind(undefined, jwaAlg), kwWrap)) + JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, keylen, sha, concatSalt.bind(undefined, jwaAlg), kwUnwrap)) + JWK.oct.deriveKey[jwaAlg] = key => key.use === 'sig' || key.use === undefined + } }) } diff --git a/lib/jwa/rsaes.js b/lib/jwa/rsaes.js index 8b1de74967..5ed9a56b25 100644 --- a/lib/jwa/rsaes.js +++ b/lib/jwa/rsaes.js @@ -1,6 +1,7 @@ const { strict: assert } = require('assert') const { publicEncrypt, privateDecrypt, constants } = require('crypto') +const { oaepHashSupported } = require('../help/runtime_support') const { KEYOBJECT } = require('../help/consts') const { asInput } = require('../help/key_object') @@ -35,8 +36,20 @@ const unwrapKey = (padding, oaepHash, { [KEYOBJECT]: keyObject }, payload) => { return privateDecrypt({ key, oaepHash, padding }, payload) } -module.exports = (JWA) => { - ['RSA1_5', 'RSA-OAEP', 'RSA-OAEP-256'].forEach((jwaAlg) => { +const LENGTHS = { + RSA1_5: 0, + 'RSA-OAEP': 592, + 'RSA-OAEP-256': 784 +} + +module.exports = (JWA, JWK) => { + const algs = ['RSA-OAEP', 'RSA1_5'] + + if (oaepHashSupported) { + algs.splice(1, 0, 'RSA-OAEP-256') + } + + algs.forEach((jwaAlg) => { const padding = resolvePadding(jwaAlg) const oaepHash = resolveOaepHash(jwaAlg) @@ -45,5 +58,7 @@ module.exports = (JWA) => { JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, padding, oaepHash)) JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, padding, oaepHash)) + JWK.RSA.wrapKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length >= LENGTHS[jwaAlg] + JWK.RSA.unwrapKey[jwaAlg] = key => key.private && (key.use === 'enc' || key.use === undefined) && key.length >= LENGTHS[jwaAlg] }) } diff --git a/lib/jwa/rsassa.js b/lib/jwa/rsassa.js index 801082cf6b..b033a6ce52 100644 --- a/lib/jwa/rsassa.js +++ b/lib/jwa/rsassa.js @@ -31,7 +31,13 @@ if (verifyOneShot) { } } -module.exports = (JWA) => { +const LENGTHS = { + RS256: 0, + RS384: 624, + RS512: 752 +} + +module.exports = (JWA, JWK) => { ['RS256', 'RS384', 'RS512'].forEach((jwaAlg) => { const nodeAlg = resolveNodeAlg(jwaAlg) @@ -40,5 +46,7 @@ module.exports = (JWA) => { JWA.sign.set(jwaAlg, sign.bind(undefined, nodeAlg)) JWA.verify.set(jwaAlg, verify.bind(undefined, nodeAlg)) + JWK.RSA.sign[jwaAlg] = key => key.private && JWK.RSA.verify[jwaAlg](key) + JWK.RSA.verify[jwaAlg] = key => (key.use === 'sig' || key.use === undefined) && key.length >= LENGTHS[jwaAlg] }) } diff --git a/lib/jwa/rsassa_pss.js b/lib/jwa/rsassa_pss.js index faf84cacac..44095cc8ee 100644 --- a/lib/jwa/rsassa_pss.js +++ b/lib/jwa/rsassa_pss.js @@ -51,7 +51,13 @@ if (verifyOneShot) { } } -module.exports = (JWA) => { +const LENGTHS = { + PS256: 528, + PS384: 784, + PS512: 1040 +} + +module.exports = (JWA, JWK) => { ['PS256', 'PS384', 'PS512'].forEach((jwaAlg) => { const nodeAlg = resolveNodeAlg(jwaAlg) @@ -60,5 +66,7 @@ module.exports = (JWA) => { JWA.sign.set(jwaAlg, sign.bind(undefined, nodeAlg)) JWA.verify.set(jwaAlg, verify.bind(undefined, nodeAlg)) + JWK.RSA.sign[jwaAlg] = key => key.private && JWK.RSA.verify[jwaAlg](key) + JWK.RSA.verify[jwaAlg] = key => (key.use === 'sig' || key.use === undefined) && key.length >= LENGTHS[jwaAlg] }) } diff --git a/lib/jwe/generate_cek.js b/lib/jwe/generate_cek.js index d87d4efb62..81568a916b 100644 --- a/lib/jwe/generate_cek.js +++ b/lib/jwe/generate_cek.js @@ -1,7 +1,7 @@ const { randomBytes } = require('crypto') const { createSecretKey } = require('../help/key_object') -const { KEYLENGTHS } = require('../help/consts') +const { KEYLENGTHS } = require('../registry') const importKey = require('../jwk/import') -module.exports = alg => importKey(createSecretKey(randomBytes(KEYLENGTHS[alg] / 8)), { use: 'enc', alg }) +module.exports = alg => importKey(createSecretKey(randomBytes(KEYLENGTHS.get(alg) / 8)), { use: 'enc', alg }) diff --git a/lib/jwk/key/base.js b/lib/jwk/key/base.js index e7a328d9b0..bfad01265d 100644 --- a/lib/jwk/key/base.js +++ b/lib/jwk/key/base.js @@ -13,6 +13,9 @@ const isObject = require('../../help/is_object') const thumbprint = require('../thumbprint') const errors = require('../../errors') +const privateApi = Symbol('privateApi') +const { JWK } = require('../../registry') + class Key { constructor (keyObject, { alg, use, kid, key_ops: ops, x5c, x5t, 'x5t#S256': x5t256 } = {}) { if (use !== undefined) { @@ -257,9 +260,46 @@ class Key { throw new Error(`"[THUMBPRINT_MATERIAL]()" is not implemented on ${this.constructor.name}`) } - /* c8 ignore next 3 */ - algorithms () { - throw new Error(`"algorithms()" is not implemented on ${this.constructor.name}`) + algorithms (operation, /* the rest is private API */ int, opts) { + const { use = this.use, alg = this.alg, key_ops: ops = this.key_ops } = int === privateApi ? opts : {} + if (alg) { + return new Set(this.algorithms(operation, privateApi, { alg: null, use, key_ops: ops }).has(alg) ? [alg] : undefined) + } + + if (typeof operation === 'symbol') { + try { + return this[operation]() + } catch (err) { + return new Set() + } + } + + if (operation && ops && !ops.includes(operation)) { + return new Set() + } + + switch (operation) { + case 'decrypt': + case 'deriveKey': + case 'encrypt': + case 'sign': + case 'unwrapKey': + case 'verify': + case 'wrapKey': + return new Set(Object.entries(JWK[this.kty][operation]).map(([alg, fn]) => fn(this) ? alg : undefined).filter(Boolean)) + case undefined: + return new Set([ + ...this.algorithms('sign'), + ...this.algorithms('verify'), + ...this.algorithms('decrypt'), + ...this.algorithms('encrypt'), + ...this.algorithms('unwrapKey'), + ...this.algorithms('wrapKey'), + ...this.algorithms('deriveKey') + ]) + default: + throw new TypeError('invalid key operation') + } } /* c8 ignore next 3 */ diff --git a/lib/jwk/key/ec.js b/lib/jwk/key/ec.js index 4d4a79bf75..0f7ff294bc 100644 --- a/lib/jwk/key/ec.js +++ b/lib/jwk/key/ec.js @@ -2,9 +2,10 @@ const { generateKeyPairSync, generateKeyPair: async } = require('crypto') const { promisify } = require('util') const { - THUMBPRINT_MATERIAL, JWK_MEMBERS, PUBLIC_MEMBERS, EC_CURVES, - PRIVATE_MEMBERS, KEY_MANAGEMENT_DECRYPT, KEY_MANAGEMENT_ENCRYPT, ECDH_ALGS + THUMBPRINT_MATERIAL, JWK_MEMBERS, PUBLIC_MEMBERS, + PRIVATE_MEMBERS, KEY_MANAGEMENT_DECRYPT, KEY_MANAGEMENT_ENCRYPT } = require('../../help/consts') +const { EC_CURVES } = require('../../registry') const { keyObjectSupported } = require('../../help/runtime_support') const { createPublicKey, createPrivateKey } = require('../../help/key_object') @@ -20,21 +21,6 @@ Object.freeze(EC_PUBLIC) const EC_PRIVATE = new Set([...EC_PUBLIC, 'd']) Object.freeze(EC_PRIVATE) -const crvToDSA = (crv) => { - switch (crv) { - case 'P-256': - return 'ES256' - case secp256k1: - return 'ES256K' - case 'P-384': - return 'ES384' - case 'P-521': - return 'ES512' - } -} - -const privateApi = Symbol('privateApi') - // Elliptic Curve Key Type class ECKey extends Key { constructor (...args) { @@ -63,59 +49,15 @@ class ECKey extends Key { return { crv: this.crv, kty: 'EC', x: this.x, y: this.y } } - algorithms (operation, /* the rest is private API */ int, opts) { - const { use = this.use, alg = this.alg, key_ops: ops = this.key_ops } = int === privateApi ? opts : {} - if (alg) { - return new Set(this.algorithms(operation, privateApi, { alg: null, use, key_ops: ops }).has(alg) ? [alg] : undefined) - } - - if (operation === KEY_MANAGEMENT_ENCRYPT) { - operation = 'deriveKey' - } else if (operation === KEY_MANAGEMENT_DECRYPT) { - if (this.public) { - return new Set() - } - operation = 'deriveKey' - } + [KEY_MANAGEMENT_ENCRYPT] () { + return this.algorithms('deriveKey') + } - if (operation && ops && !ops.includes(operation)) { + [KEY_MANAGEMENT_DECRYPT] () { + if (this.public) { return new Set() } - - switch (operation) { - case 'wrapKey': - case 'unwrapKey': - case 'encrypt': - case 'decrypt': - return new Set() - case 'sign': - if (this.public || use === 'enc') { - return new Set() - } - - return new Set([crvToDSA(this.crv)]) - case 'verify': - if (use === 'enc') { - return new Set() - } - - return new Set([crvToDSA(this.crv)]) - case 'deriveKey': - if (use === 'sig' || this.crv === secp256k1) { - return new Set() - } - - return new Set(ECDH_ALGS) - case undefined: - // just the ops needed to return all algs regardless of its use - return new Set([ - ...this.algorithms('sign'), - ...this.algorithms('verify'), - ...this.algorithms('deriveKey') - ]) - default: - throw new TypeError('invalid key operation') - } + return this.algorithms('deriveKey') } static async generate (crv = 'P-256', privat = true) { diff --git a/lib/jwk/key/oct.js b/lib/jwk/key/oct.js index 8efce63600..9bb9567122 100644 --- a/lib/jwk/key/oct.js +++ b/lib/jwk/key/oct.js @@ -2,25 +2,18 @@ const { randomBytes } = require('crypto') const { createSecretKey } = require('../../help/key_object') const base64url = require('../../help/base64url') -const { KEYOBJECT } = require('../../help/consts') const { THUMBPRINT_MATERIAL, PUBLIC_MEMBERS, PRIVATE_MEMBERS, - KEY_MANAGEMENT_DECRYPT, KEY_MANAGEMENT_ENCRYPT + KEY_MANAGEMENT_DECRYPT, KEY_MANAGEMENT_ENCRYPT, KEYOBJECT } = require('../../help/consts') const Key = require('./base') -const ENC_ALGS = new Set(['A128CBC-HS256', 'A128GCM', 'A192CBC-HS384', 'A192GCM', 'A256CBC-HS512', 'A256GCM']) -const ENC_LEN = new Set([128, 192, 256, 384, 512]) -const WRAP_LEN = new Set([128, 192, 256]) - const OCT_PUBLIC = new Set() Object.freeze(OCT_PUBLIC) const OCT_PRIVATE = new Set(['k']) Object.freeze(OCT_PRIVATE) -const privateApi = Symbol('privateApi') - // Octet sequence Key Type class OctKey extends Key { constructor (...args) { @@ -72,74 +65,23 @@ class OctKey extends Key { return { k: this.k, kty: 'oct' } } - algorithms (operation, /* the rest is private API */ int, opts) { - const { use = this.use, alg = this.alg, key_ops: ops = this.key_ops } = int === privateApi ? opts : {} - - if (!this[KEYOBJECT]) { - return new Set() - } + [KEY_MANAGEMENT_ENCRYPT] () { + return new Set([ + ...this.algorithms('wrapKey'), + ...this.algorithms('deriveKey') + ]) + } - if (operation === KEY_MANAGEMENT_ENCRYPT || operation === KEY_MANAGEMENT_DECRYPT) { - return new Set([ - ...this.algorithms('wrapKey'), - ...this.algorithms('deriveKey') - ]) - } + [KEY_MANAGEMENT_DECRYPT] () { + return this[KEY_MANAGEMENT_ENCRYPT]() + } - if (operation && ops && !ops.includes(operation)) { + algorithms (...args) { + if (!this[KEYOBJECT]) { return new Set() } - if (alg) { - return new Set(this.algorithms(operation, privateApi, { alg: null, use, key_ops: ops }).has(alg) ? [alg] : undefined) - } - - switch (operation) { - case 'deriveKey': - if (use === 'sig' || 'electron' in process.versions) { - return new Set() - } - - return new Set(['PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW']) - case 'encrypt': - case 'decrypt': - if (this.use === 'sig' || !ENC_LEN.has(this.length)) { - return new Set() - } - - return new Set([`A${this.length / 2}CBC-HS${this.length}`, `A${this.length}GCM`].filter(a => ENC_ALGS.has(a))) - case 'sign': - case 'verify': - if (use === 'enc') { - return new Set() - } - - return new Set(['HS256', 'HS384', 'HS512']) - case 'wrapKey': - case 'unwrapKey': - if (use === 'sig' || !WRAP_LEN.has(this.length)) { - return new Set() - } - - if ('electron' in process.versions) { - return new Set([`A${this.length}GCMKW`]) - } - - return new Set([`A${this.length}KW`, `A${this.length}GCMKW`]) - case undefined: - return new Set([ - // just the ops needed to return all algs regardless of its use - symmetric keys - ...this.algorithms('encrypt'), - ...this.algorithms('decrypt'), - ...this.algorithms('sign'), - ...this.algorithms('verify'), - ...this.algorithms('wrapKey'), - ...this.algorithms('unwrapKey'), - ...this.algorithms('deriveKey') - ]) - default: - throw new TypeError('invalid key operation') - } + return Key.prototype.algorithms.call(this, ...args) } static async generate (...args) { diff --git a/lib/jwk/key/okp.js b/lib/jwk/key/okp.js index 116cac6af7..9f07371544 100644 --- a/lib/jwk/key/okp.js +++ b/lib/jwk/key/okp.js @@ -3,8 +3,9 @@ const { promisify } = require('util') const { THUMBPRINT_MATERIAL, JWK_MEMBERS, PUBLIC_MEMBERS, - PRIVATE_MEMBERS, KEY_MANAGEMENT_DECRYPT, KEY_MANAGEMENT_ENCRYPT, OKP_CURVES + PRIVATE_MEMBERS, KEY_MANAGEMENT_DECRYPT, KEY_MANAGEMENT_ENCRYPT } = require('../../help/consts') +const { OKP_CURVES } = require('../../registry') const { edDSASupported } = require('../../help/runtime_support') const errors = require('../../errors') @@ -17,8 +18,6 @@ Object.freeze(OKP_PUBLIC) const OKP_PRIVATE = new Set([...OKP_PUBLIC, 'd']) Object.freeze(OKP_PRIVATE) -const privateApi = Symbol('privateApi') - // Octet string key pairs Key Type class OKPKey extends Key { constructor (...args) { @@ -47,59 +46,15 @@ class OKPKey extends Key { return { crv: this.crv, kty: 'OKP', x: this.x } } - algorithms (operation, /* the rest is private API */ int, opts) { - const { use = this.use, alg = this.alg, key_ops: ops = this.key_ops } = int === privateApi ? opts : {} - if (alg) { - return new Set(this.algorithms(operation, privateApi, { alg: null, use, key_ops: ops }).has(alg) ? [alg] : undefined) - } - - if (operation === KEY_MANAGEMENT_ENCRYPT) { - operation = 'deriveKey' - } else if (operation === KEY_MANAGEMENT_DECRYPT) { - if (this.public) { - return new Set() - } - operation = 'deriveKey' - } + [KEY_MANAGEMENT_ENCRYPT] () { + return this.algorithms('deriveKey') + } - if (operation && ops && !ops.includes(operation)) { + [KEY_MANAGEMENT_DECRYPT] () { + if (this.public) { return new Set() } - - switch (operation) { - case 'wrapKey': - case 'unwrapKey': - case 'encrypt': - case 'decrypt': - return new Set() - case 'sign': - if (this.public || use === 'enc' || this.crv.startsWith('X')) { - return new Set() - } - - return new Set(['EdDSA']) - case 'verify': - if (use === 'enc' || this.crv.startsWith('X')) { - return new Set() - } - - return new Set(['EdDSA']) - case 'deriveKey': - if (use === 'sig' || this.crv.startsWith('Ed')) { - return new Set() - } - - // return new Set(ECDH_ALGS) - return new Set() - case undefined: - return new Set([ - ...this.algorithms('sign'), - ...this.algorithms('verify'), - ...this.algorithms('deriveKey') - ]) - default: - throw new TypeError('invalid key operation') - } + return this.algorithms('deriveKey') } static async generate (crv = 'Ed25519', privat = true) { diff --git a/lib/jwk/key/rsa.js b/lib/jwk/key/rsa.js index 630026693f..210441c7a0 100644 --- a/lib/jwk/key/rsa.js +++ b/lib/jwk/key/rsa.js @@ -5,55 +5,18 @@ const { THUMBPRINT_MATERIAL, JWK_MEMBERS, PUBLIC_MEMBERS, PRIVATE_MEMBERS, KEY_MANAGEMENT_DECRYPT, KEY_MANAGEMENT_ENCRYPT } = require('../../help/consts') -const { oaepHashSupported, keyObjectSupported } = require('../../help/runtime_support') +const { keyObjectSupported } = require('../../help/runtime_support') const { createPublicKey, createPrivateKey } = require('../../help/key_object') const Key = require('./base') const generateKeyPair = promisify(async) -const SIG_ALGS = ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512'] -const WRAP_ALGS = ['RSA-OAEP', 'RSA1_5'] - -if (oaepHashSupported) { - WRAP_ALGS.splice(1, 0, 'RSA-OAEP-256') -} - const RSA_PUBLIC = new Set(['e', 'n']) Object.freeze(RSA_PUBLIC) const RSA_PRIVATE = new Set([...RSA_PUBLIC, 'd', 'p', 'q', 'dp', 'dq', 'qi']) Object.freeze(RSA_PRIVATE) -const sigAlgsAvailableFor = (length) => { - switch (true) { - case length >= 1040: - return new Set(SIG_ALGS) - case length >= 784: - return new Set(['PS256', 'RS256', 'PS384', 'RS384', 'RS512']) - case length >= 752: - return new Set(['PS256', 'RS256', 'RS384', 'RS512']) - case length >= 624: - return new Set(['PS256', 'RS256', 'RS384']) - case length >= 528: - return new Set(['PS256', 'RS256']) - default: - return new Set(['RS256']) - } -} - -const wrapAlgsAvailableFor = (length) => { - switch (true) { - case length >= 784: - return new Set(WRAP_ALGS) - case length >= 592: - return new Set(['RSA-OAEP', 'RSA1_5']) - default: - return new Set(['RSA1_5']) - } -} - -const privateApi = Symbol('privateApi') - // RSA Key Type class RSAKey extends Key { constructor (...args) { @@ -92,62 +55,12 @@ class RSAKey extends Key { return { e: this.e, kty: 'RSA', n: this.n } } - algorithms (operation, /* the rest is private API */ int, opts) { - const { use = this.use, alg = this.alg, key_ops: ops = this.key_ops } = int === privateApi ? opts : {} - if (alg) { - return new Set(this.algorithms(operation, privateApi, { alg: null, use, key_ops: ops }).has(alg) ? [alg] : undefined) - } - - if (operation === KEY_MANAGEMENT_ENCRYPT) { - operation = 'wrapKey' - } else if (operation === KEY_MANAGEMENT_DECRYPT) { - operation = 'unwrapKey' - } - - if (operation && ops && !ops.includes(operation)) { - return new Set() - } + [KEY_MANAGEMENT_ENCRYPT] () { + return this.algorithms('wrapKey') + } - switch (operation) { - case 'deriveKey': - case 'encrypt': - case 'decrypt': - return new Set() - case 'sign': - if (this.public || use === 'enc') { - return new Set() - } - - return sigAlgsAvailableFor(this.length) - case 'verify': - if (use === 'enc') { - return new Set() - } - - return sigAlgsAvailableFor(this.length) - case 'wrapKey': - if (use === 'sig') { - return new Set() - } - - return wrapAlgsAvailableFor(this.length) - case 'unwrapKey': - if (this.public || use === 'sig') { - return new Set() - } - - return wrapAlgsAvailableFor(this.length) - case undefined: - // just the ops needed to return all algs regardless of its use - return new Set([ - ...this.algorithms('sign'), - ...this.algorithms('verify'), - ...this.algorithms('wrapKey'), - ...this.algorithms('unwrapKey') - ]) - default: - throw new TypeError('invalid key operation') - } + [KEY_MANAGEMENT_DECRYPT] () { + return this.algorithms('unwrapKey') } static async generate (len = 2048, privat = true) { diff --git a/lib/registry/ec_curves.js b/lib/registry/ec_curves.js new file mode 100644 index 0000000000..08e553d3e5 --- /dev/null +++ b/lib/registry/ec_curves.js @@ -0,0 +1,23 @@ +const { getCurves } = require('crypto') + +const { name: secp256k1 } = require('../jwk/key/secp256k1_crv') + +const curves = new Set() + +if (getCurves().includes('prime256v1')) { + curves.add('P-256') +} + +if (getCurves().includes('secp256k1')) { + curves.add(secp256k1) +} + +if (getCurves().includes('secp384r1')) { + curves.add('P-384') +} + +if (getCurves().includes('secp521r1')) { + curves.add('P-521') +} + +module.exports = curves diff --git a/lib/registry/index.js b/lib/registry/index.js new file mode 100644 index 0000000000..ecf04d396b --- /dev/null +++ b/lib/registry/index.js @@ -0,0 +1,15 @@ +const EC_CURVES = require('./ec_curves') +const IVLENGTHS = require('./iv_lengths') +const JWA = require('./jwa') +const JWK = require('./jwk') +const KEYLENGTHS = require('./key_lengths') +const OKP_CURVES = require('./okp_curves') + +module.exports = { + EC_CURVES, + IVLENGTHS, + JWA, + JWK, + KEYLENGTHS, + OKP_CURVES +} diff --git a/lib/registry/iv_lengths.js b/lib/registry/iv_lengths.js new file mode 100644 index 0000000000..51229f129c --- /dev/null +++ b/lib/registry/iv_lengths.js @@ -0,0 +1,11 @@ +module.exports = new Map([ + ['A128CBC-HS256', 128], + ['A128GCM', 96], + ['A128GCMKW', 96], + ['A192CBC-HS384', 128], + ['A192GCM', 96], + ['A192GCMKW', 96], + ['A256CBC-HS512', 128], + ['A256GCM', 96], + ['A256GCMKW', 96] +]) diff --git a/lib/registry/jwa.js b/lib/registry/jwa.js new file mode 100644 index 0000000000..dea7dd1267 --- /dev/null +++ b/lib/registry/jwa.js @@ -0,0 +1,8 @@ +module.exports = { + sign: new Map(), + verify: new Map(), + keyManagementEncrypt: new Map(), + keyManagementDecrypt: new Map(), + encrypt: new Map(), + decrypt: new Map() +} diff --git a/lib/registry/jwk.js b/lib/registry/jwk.js new file mode 100644 index 0000000000..b4dd4d291f --- /dev/null +++ b/lib/registry/jwk.js @@ -0,0 +1,38 @@ +module.exports = { + oct: { + decrypt: {}, + deriveKey: {}, + encrypt: {}, + sign: {}, + unwrapKey: {}, + verify: {}, + wrapKey: {} + }, + EC: { + decrypt: {}, + deriveKey: {}, + encrypt: {}, + sign: {}, + unwrapKey: {}, + verify: {}, + wrapKey: {} + }, + RSA: { + decrypt: {}, + deriveKey: {}, + encrypt: {}, + sign: {}, + unwrapKey: {}, + verify: {}, + wrapKey: {} + }, + OKP: { + decrypt: {}, + deriveKey: {}, + encrypt: {}, + sign: {}, + unwrapKey: {}, + verify: {}, + wrapKey: {} + } +} diff --git a/lib/registry/key_lengths.js b/lib/registry/key_lengths.js new file mode 100644 index 0000000000..fc3c1d8dab --- /dev/null +++ b/lib/registry/key_lengths.js @@ -0,0 +1,8 @@ +module.exports = new Map([ + ['A128CBC-HS256', 256], + ['A128GCM', 128], + ['A192CBC-HS384', 384], + ['A192GCM', 192], + ['A256CBC-HS512', 512], + ['A256GCM', 256] +]) diff --git a/lib/registry/okp_curves.js b/lib/registry/okp_curves.js new file mode 100644 index 0000000000..8406598d42 --- /dev/null +++ b/lib/registry/okp_curves.js @@ -0,0 +1,9 @@ +const curves = new Set(['Ed25519']) + +if (!('electron' in process.versions)) { + curves.add('Ed448') + curves.add('X25519') + curves.add('X448') +} + +module.exports = curves diff --git a/test/jwk/key_ops.test.js b/test/jwk/key_ops.test.js index 99f200dd14..72c5f9fa37 100644 --- a/test/jwk/key_ops.test.js +++ b/test/jwk/key_ops.test.js @@ -1,8 +1,6 @@ const test = require('ava') -const errors = require('../../lib/errors') -const asKey = require('../../lib/jwk/import') -const { generateSync } = require('../../lib/jwk/generate') +const { errors, JWK: { asKey, generateSync } } = require('../../lib') const { generateKeyPairSync } = require('../macros/generate') const { edDSASupported } = require('../../lib/help/runtime_support') @@ -80,8 +78,8 @@ test('PEM asKey with invalid use / key_ops throws', t => { test('RSA key key_ops', t => { const k = generateSync('RSA', 2048, { key_ops: ['sign'] }) - t.deepEqual([...k.algorithms()], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512']) - t.deepEqual([...k.algorithms('sign')], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512']) + t.deepEqual([...k.algorithms()], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512']) + t.deepEqual([...k.algorithms('sign')], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512']) t.deepEqual([...k.algorithms('verify')], []) }) diff --git a/test/jwk/rsa.test.js b/test/jwk/rsa.test.js index b5be08dbe4..43dbca9fe1 100644 --- a/test/jwk/rsa.test.js +++ b/test/jwk/rsa.test.js @@ -36,13 +36,13 @@ test('RSA key .algorithms invalid operation', t => { test('RSA Private key algorithms (no operation)', t => { const result = key.algorithms() t.is(result.constructor, Set) - t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512', 'RSA-OAEP', 'RSA-OAEP-256', 'RSA1_5']) + t.deepEqual([...result], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512', 'RSA-OAEP', 'RSA-OAEP-256', 'RSA1_5']) }) } else { test('RSA Private key algorithms (no operation)', t => { const result = key.algorithms() t.is(result.constructor, Set) - t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512', 'RSA-OAEP', 'RSA1_5']) + t.deepEqual([...result], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512', 'RSA-OAEP', 'RSA1_5']) }) } @@ -56,27 +56,27 @@ test('RSA key .algorithms invalid operation', t => { test('RSA Private key supports sign alg (no use)', t => { const result = key.algorithms('sign') t.is(result.constructor, Set) - t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512']) + t.deepEqual([...result], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512']) }) test('RSA Private key supports verify alg (no use)', t => { const result = key.algorithms('verify') t.is(result.constructor, Set) - t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512']) + t.deepEqual([...result], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512']) }) test('RSA Private key supports sign alg when `use` is "sig")', t => { const sigKey = new RSAKey(keyObject, { use: 'sig' }) const result = sigKey.algorithms('sign') t.is(result.constructor, Set) - t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512']) + t.deepEqual([...result], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512']) }) test('RSA Private key supports verify alg when `use` is "sig")', t => { const sigKey = new RSAKey(keyObject, { use: 'sig' }) const result = sigKey.algorithms('verify') t.is(result.constructor, Set) - t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512']) + t.deepEqual([...result], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512']) }) test('RSA Private key supports single sign alg when `alg` is set)', t => { @@ -192,13 +192,13 @@ test('RSA key .algorithms invalid operation', t => { test('RSA EC Public key algorithms (no operation)', t => { const result = key.algorithms() t.is(result.constructor, Set) - t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512', 'RSA-OAEP', 'RSA-OAEP-256', 'RSA1_5']) + t.deepEqual([...result], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512', 'RSA-OAEP', 'RSA-OAEP-256', 'RSA1_5']) }) } else { test('RSA EC Public key algorithms (no operation)', t => { const result = key.algorithms() t.is(result.constructor, Set) - t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512', 'RSA-OAEP', 'RSA1_5']) + t.deepEqual([...result], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512', 'RSA-OAEP', 'RSA1_5']) }) } @@ -218,7 +218,7 @@ test('RSA key .algorithms invalid operation', t => { test('RSA Public key supports verify alg (no use)', t => { const result = key.algorithms('verify') t.is(result.constructor, Set) - t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512']) + t.deepEqual([...result], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512']) }) test('RSA Public key cannot sign even when `use` is "sig")', t => { @@ -232,7 +232,7 @@ test('RSA key .algorithms invalid operation', t => { const sigKey = new RSAKey(keyObject, { use: 'sig' }) const result = sigKey.algorithms('verify') t.is(result.constructor, Set) - t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512']) + t.deepEqual([...result], ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512']) }) test('RSA Public key cannot sign even when `alg` is set)', t => { diff --git a/test/jwks/keystore.test.js b/test/jwks/keystore.test.js index 7b8587e996..3654bc5ea0 100644 --- a/test/jwks/keystore.test.js +++ b/test/jwks/keystore.test.js @@ -1,7 +1,6 @@ const test = require('ava') -const { KeyStore, asKeyStore } = require('../../lib/jwks') -const { asKey, generateSync } = require('../../lib/jwk') +const { JWK: { asKey, generateSync }, JWKS: { KeyStore, asKeyStore } } = require('../../lib') const errors = require('../../lib/errors') const withX5C = {