From 5b53cb01552fcff3d48a97ff465ffbf69b286db7 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Thu, 20 Jun 2019 23:32:13 +0200 Subject: [PATCH] fix: limit calculation of missing RSA private components - this deprecates the use of `JWK.importKey` in favor of `JWK.asKey` - this deprecates the use of `JWKS.KeyStore.fromJWKS` in favor of `JWKS.asKeyStore` Both `JWK.importKey` and `JWKS.KeyStore.fromJWKS` could have resulted in the process getting blocked when large bitsize RSA private keys were missing their components and could also result in an endless calculation loop when the private key's private exponent was outright invalid or tampered with. The new methods still allow to import private RSA keys with these optimization key parameters missing but its disabled by default and one should choose to enable it when working with keys from trusted sources It is recommended not to use @panva/jose versions with this feature in its original on-by-default form - v1.1.0 and v1.2.0 These will --- CHANGELOG.md | 4 +- README.md | 6 +- docs/README.md | 92 ++++++++++++------- lib/help/base64url.js | 12 ++- lib/help/key_utils.js | 12 ++- lib/help/rsa_primes.js | 20 ++++ lib/index.d.ts | 22 +++++ lib/jwe/decrypt.js | 10 +- lib/jwe/encrypt.js | 2 +- lib/jwk/import.js | 14 ++- lib/jwk/index.js | 8 +- lib/jwks/index.js | 2 +- lib/jwks/keystore.js | 35 ++++--- lib/jws/sign.js | 2 +- lib/jws/verify.js | 4 +- lib/jwt/verify.js | 2 +- test/cookbook/4_1.rsa_v15_signature.test.js | 6 +- test/cookbook/4_2.rsa-pss_signature.test.js | 6 +- test/cookbook/4_3.ecdsa_signature.test.js | 6 +- ...4_4.hmac-sha2_integrity_protection.test.js | 6 +- ....protecting_specific_header_fields.test.js | 6 +- .../4_7.protecting_content_only.test.js | 6 +- test/cookbook/4_8.multiple_signatures.test.js | 4 +- ...on_using_rsa_v15_and_aes-hmac-sha2.test.js | 6 +- ...ing_additional_authentication_data.test.js | 6 +- ....protecting_specific_header_fields.test.js | 6 +- .../5_12.protecting_content_only.test.js | 6 +- ....encrypting_to_multiple_recipients.test.js | 4 +- ...yption_using_rsa-oaep_with_aes-gcm.test.js | 6 +- ...aes-keywrap_with-aes-cbc-hmac-sha2.test.js | 6 +- ...dh-es_and_aes-keywrap_with_aes-gcm.test.js | 6 +- ...ing_ecdh-es_with_aes-cbc-hmac-sha2.test.js | 6 +- ..._6.direct_encryption_using_aes-gcm.test.js | 6 +- ...gcm_keywrap_with_aes-cbc-hmac-sha2.test.js | 6 +- ...rap_using_aes-keywrap_with_aes-gcm.test.js | 6 +- test/cookbook/5_9.compressed_content.test.js | 6 +- test/cookbook/jwk.test.js | 18 ++-- .../rfc7797.4_1.hmac-sha2_b64_false.test.js | 6 +- .../rfc7797.4_2.hmac-sha2_b64_false.js | 6 +- test/cookbook/rfc8037.a4.ed25519.test.js | 6 +- test/help/base64url.test.js | 4 - test/help/ecdsa_signatures.test.js | 8 +- test/jwe/sanity.test.js | 4 +- test/jwe/smoke.test.js | 12 +-- test/jwk/general.test.js | 4 +- test/jwk/import.test.js | 53 ++++++----- test/jwk/key_ops.test.js | 34 +++---- test/jwk/oct.test.js | 8 +- test/jwk/x5c_thumbprints.test.js | 12 +-- test/jwks/keystore.test.js | 50 ++++++---- test/jws/b64.test.js | 2 +- test/jws/sanity.test.js | 4 +- test/jws/smoke.test.js | 10 +- 53 files changed, 359 insertions(+), 245 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46328875f5..6d8a1a6d77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -# [1.2.0](https://github.com/panva/jose/compare/v1.1.0...v1.2.0) (2019-05-25) +# YANKED [1.2.0](https://github.com/panva/jose/compare/v1.1.0...v1.2.0) (2019-05-25) ### Features @@ -12,7 +12,7 @@ All notable changes to this project will be documented in this file. See [standa -# [1.1.0](https://github.com/panva/jose/compare/v1.0.2...v1.1.0) (2019-05-23) +# YANKED [1.1.0](https://github.com/panva/jose/compare/v1.0.2...v1.1.0) (2019-05-23) ### Bug Fixes diff --git a/README.md b/README.md index 60283f4fca..e2e51e0739 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Pending Node.js Support 🤞: Won't implement: - ✕ JWS embedded key / referenced verification - - one can decode the header and pass the (`x5c`, `jwk`) to `JWK.importKey` and validate with that + - one can decode the header and pass the (`x5c`, `jwk`) to `JWK.asKey` and validate with that key, similarly the application can handle fetching and then instantiating the referenced `x5u` or `jku` in its own code. This way you opt-in to these behaviours. - ✕ JWS detached content @@ -137,14 +137,14 @@ const { Prepare your Keys and KeyStores. See the [documentation][documentation-jwk] for more. ```js -const key = jose.JWK.importKey(fs.readFileSync('path/to/key/file')) +const key = jose.JWK.asKey(fs.readFileSync('path/to/key/file')) const jwk = { kty: 'EC', kid: 'dl4M_fcI7XoFCsQ22PYrQBkuxZ2pDcbDimcdFmmXM98', crv: 'P-256', x: 'v37avifcL-xgh8cy6IFzcINqqmFLc2JF20XUpn4Y2uQ', y: 'QTwy27XgP7ZMOdGOSopAHB-FU1JMQn3J9GEWGtUXreQ' } -const anotherKey = jose.JWK.importKey(jwk) +const anotherKey = jose.JWK.asKey(jwk) const keystore = new jose.JWK.KeyStore(key, key2) ``` diff --git a/docs/README.md b/docs/README.md index f6239ba769..e3420fc72f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -45,10 +45,10 @@ I can continue maintaining it and adding new features carefree. You may also don - [key.algorithms([operation])](#keyalgorithmsoperation) - [key.toJWK([private])](#keytojwkprivate) - [key.toPEM([private[, encoding]])](#keytopemprivate-encoding) -- JWK.importKey - - [JWK.importKey(key[, options]) asymmetric key import](#jwkimportkeykey-options-asymmetric-key-import) - - [JWK.importKey(secret[, options]) secret key import](#jwkimportkeysecret-options-secret-key-import) - - [JWK.importKey(jwk) JWK-formatted key import](#jwkimportkeyjwk-jwk-formatted-key-import) +- JWK.asKey + - [JWK.asKey(key[, options]) asymmetric key import](#jwkaskeykey-options-asymmetric-key-import) + - [JWK.asKey(secret[, options]) secret key import](#jwkaskeysecret-options-secret-key-import) + - [JWK.asKey(jwk[, options]) JWK-formatted key import](#jwkaskeyjwk-options-jwk-formatted-key-import) - [JWK.generate(kty[, crvOrSize[, options[, private]]]) generating new keys](#jwkgeneratekty-crvorsize-options-private-generating-new-keys) - [JWK.generateSync(kty[, crvOrSize[, options[, private]]])](#jwkgeneratesynckty-crvorsize-options-private) - [JWK.isKey(object)](#jwkiskeyobject) @@ -60,7 +60,7 @@ how to get a `` instances generated or instantiated from existing key m ```js const { JWK } = require('@panva/jose') -// { importKey: [Function: importKey], +// { asKey: [Function: asKey], // generate: [AsyncFunction: generate], // generateSync: [Function: generateSync] } ``` @@ -70,7 +70,7 @@ const { JWK } = require('@panva/jose') #### Class: `` and `` | `` | `` | `` ``, ``, `` and `` represent a key usable for JWS and JWE operations. -The `JWK.importKey()` method is used to retrieve a key representation of an existing key or secret. +The `JWK.asKey()` method is used to retrieve a key representation of an existing key or secret. `JWK.generate()` method is used to generate a new random key. ``, ``, `` and `` inherit methods from `` and in addition @@ -330,7 +330,7 @@ key.toPEM(true, { passphrase: 'super-strong', cipher: 'aes-256-cbc' }) --- -#### `JWK.importKey(key[, options])` asymmetric key import +#### `JWK.asKey(key[, options])` asymmetric key import Imports an asymmetric private or public key. Supports importing JWK formatted keys (private, public, secrets), `pem` and `der` formatted private and public keys, `pem` formatted X.509 certificates. @@ -362,9 +362,9 @@ formats ```js const { readFileSync } = require('fs') -const { JWK: { importKey } } = require('@panva/jose') +const { JWK: { asKey } } = require('@panva/jose') -const key = importKey(readFileSync('path/to/key/file')) +const key = asKey(readFileSync('path/to/key/file')) // ECKey { // kty: 'EC', // public: true, @@ -377,7 +377,7 @@ const key = importKey(readFileSync('path/to/key/file')) --- -#### `JWK.importKey(secret[, options])` secret key import +#### `JWK.asKey(secret[, options])` secret key import Imports a symmetric key. @@ -394,9 +394,9 @@ Imports a symmetric key. Example (Click to expand) ```js -const { JWK: { importKey } } = require('@panva/jose') +const { JWK: { asKey } } = require('@panva/jose') -const key = importKey(Buffer.from('8yHym6h5CG5FylbzrCn8fhxEbp3kOaTsgLaawaaJ')) +const key = asKey(Buffer.from('8yHym6h5CG5FylbzrCn8fhxEbp3kOaTsgLaawaaJ')) // OctKey { // kty: 'oct', // kid: [Getter], @@ -406,7 +406,7 @@ const key = importKey(Buffer.from('8yHym6h5CG5FylbzrCn8fhxEbp3kOaTsgLaawaaJ')) --- -#### `JWK.importKey(jwk)` JWK-formatted key import +#### `JWK.asKey(jwk[, options])` JWK-formatted key import Imports a JWK formatted key. This supports JWK formatted RSA, EC, OKP and oct keys. Asymmetrical keys may be both private and public. @@ -420,18 +420,28 @@ keys may be both private and public. [RFC7638][spec-thumbprint] - `e`, `n` properties as `` for RSA public keys - `e`, `n`, `d`, `p`, `q`, `dp`, `dq`, `qi` properties as `` for RSA private keys + - `e`, `n`, `d` properties as `` for RSA private keys without optimization parametes (only + with `calculateMissingRSAPrimes` option, see below) - `crv`, `x`, `y` properties as `` for EC public keys - `crv`, `x`, `y`, `d` properties as `` for EC private keys - `crv`, `x`, properties as `` for OKP public keys - `crv`, `x`, `d` properties as `` for OKP private keys - `k` properties as `` for secret oct keys +- `options`: `` + - `calculateMissingRSAPrimes`: `` **Default** 'false'. This option is really only in + effect when importing private RSA JWK keys, by default, keys without the optimization private + key parameters (p, q, dp, dq, qi) won't imported because their calculation is heavy and prone + to blocking the process. Setting this option to true will enable these keys to be imported, + albeit at your own risk. Depending on the key size the calculation takes long and it should + only be used for JWK keys from trusted sources. - Returns: `` | `` | `` | `` +
Example (Click to expand) ```js -const { JWK: { importKey } } = require('@panva/jose') +const { JWK: { asKey } } = require('@panva/jose') const jwk = { kty: 'RSA', kid: 'r1LkbBo3925Rb2ZFFrKyU3MVex9T2817Kx0vbi6i_Kc', @@ -440,7 +450,7 @@ const jwk = { n: 'xwQ72P9z9OYshiQ-ntDYaPnnfwG6u9JAdLMZ5o0dmjlcyrvwQRdoFIKPnO65Q8mh6F_LDSxjxa2Yzo_wdjhbPZLjfUJXgCzm54cClXzT5twzo7lzoAfaJlkTsoZc2HFWqmcri0BuzmTFLZx2Q7wYBm0pXHmQKF0V-C1O6NWfd4mfBhbM-I1tHYSpAMgarSm22WDMDx-WWI7TEzy2QhaBVaENW9BKaKkJklocAZCxk18WhR0fckIGiWiSM5FcU1PY2jfGsTmX505Ub7P5Dz75Ygqrutd5tFrcqyPAtPTFDk8X1InxkkUwpP3nFU5o50DGhwQolGYKPGtQ-ZtmbOfcWQ' } -const key = importKey(jwk) +const key = asKey(jwk) // RSAKey { // kty: 'RSA', // public: true, @@ -563,7 +573,7 @@ Returns 'true' if the value is an instance of ``. - [keystore.generate(...)](#keystoregenerate) - [keystore.generateSync(...)](#keystoregeneratesync) - [keystore.toJWKS([private])](#keystoretojwksprivate) - - [JWKS.KeyStore.fromJWKS(jwks)](#jwkskeystorefromjwksjwks) + - [JWKS.asKeyStore(jwks[, options])](#jwksaskeystorejwks-options) ```js @@ -584,7 +594,7 @@ an existing store. Creates a new KeyStore, either empty or populated. -- `keys`: `` Array of key keys instantiated by `JWK.importKey()` +- `keys`: `` Array of key keys instantiated by `JWK.asKey()` - Returns: `` --- @@ -671,29 +681,41 @@ Exports the keystore to a JSON Web Key Set formatted object. --- -#### `JWKS.KeyStore.fromJWKS(jwks)` +#### `JWKS.asKeyStore(jwks[, options])` Creates a new KeyStore from a JSON Web Key Set. - `jwks`: `` JWKS formatted object (`{ keys: [{ kty: '...', ... }, ...] }`) +- `options`: `` + - `calculateMissingRSAPrimes`: `` **Default** 'false'. This option is really only in + effect when the JWKS contains private RSA JWK keys, by default, keys without the optimization + private key parameters (p, q, dp, dq, qi) won't imported because their calculation is heavy and + prone to blocking the process. Setting this option to true will enable these keys to be + imported, albeit at your own risk. Depending on the key size the calculation takes long and it + should only be used for JWKS from trusted sources. - Returns: ``
Example (Click to expand) ```js -const { JWKS: { KeyStore } } = require('@panva/jose') -const jwks = { keys: - [ { kty: 'RSA', - kid: 'gqUcZ2TjhmNrVOd1d27tedkabhOTs9WghMHIyjIBn7Y', - e: 'AQAB', - n: - 'vi1Aui6R0rUL_7pdcFKKMhBF25h4x8WiTZ4w66eNZhwIp48lz-vBuyUUrSR-RwcuvnxlXdjBdSaN-PZkNRDv2bXE3mVtjZgoYyzQlGLJ1wduQaBXIkrQWxc7yzL91MvtP1kWwFHHrQHZRlpiFQQm9gNCy2wXCTbWGT9kjrR1W1bkwhmOKK4rF-hMgaCNDrtEQ6xWknxV8aXW4itouJ0pJv8xplc6J14f_SNq6arVUcAZ26EzJYC2fcvqwsrnKzvW7QxQGQzh-u9Tn82Tl14Omh1KDV8C7Vb_m8XClv_9zOrKBGdaTl1zgINyMEaa_IMophnBgK_kAXvtVvEThQ93GQ', - use: 'enc' } ] } -const ks = KeyStore.fromJWKS(jwks) +const { JWKS: { KeyStore, asKeyStore } } = require('@panva/jose') +const jwks = { + keys: [ + { kty: 'RSA', + kid: 'gqUcZ2TjhmNrVOd1d27tedkabhOTs9WghMHIyjIBn7Y', + e: 'AQAB', + n: + 'vi1Aui6R0rUL_7pdcFKKMhBF25h4x8WiTZ4w66eNZhwIp48lz-vBuyUUrSR-RwcuvnxlXdjBdSaN-PZkNRDv2bXE3mVtjZgoYyzQlGLJ1wduQaBXIkrQWxc7yzL91MvtP1kWwFHHrQHZRlpiFQQm9gNCy2wXCTbWGT9kjrR1W1bkwhmOKK4rF-hMgaCNDrtEQ6xWknxV8aXW4itouJ0pJv8xplc6J14f_SNq6arVUcAZ26EzJYC2fcvqwsrnKzvW7QxQGQzh-u9Tn82Tl14Omh1KDV8C7Vb_m8XClv_9zOrKBGdaTl1zgINyMEaa_IMophnBgK_kAXvtVvEThQ93GQ', + use: 'enc' } + ] +} +const ks = asKeyStore(jwks) // KeyStore {} ks.size // 1 +ks instanceof KeyStore +// true ```
@@ -750,7 +772,7 @@ that will be used to sign with is either provided as part of the 'options.algori ```js const { JWT, JWK } = require('@panva/jose') -const key = JWK.importKey({ +const key = JWK.asKey({ kty: 'oct', k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg' }) @@ -820,7 +842,7 @@ Verifies the claims and signature of a JSON Web Token. ```js const { JWK, JWT } = require('@panva/jose') -const key = JWK.importKey({ +const key = JWK.asKey({ kty: 'oct', k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg' }) @@ -914,11 +936,11 @@ signatures of the same payload) using the General JWS JSON Serialization Syntax. ```js const { JWK, JWS } = require('@panva/jose') -const key = JWK.importKey({ +const key = JWK.asKey({ kty: 'oct', k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg' }) -const key2 = JWK.importKey({ +const key2 = JWK.asKey({ kty: 'oct', k: 'AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8' }) @@ -997,7 +1019,7 @@ provided `` instance. ```js const { JWK, JWS } = require('@panva/jose') -const key = JWK.importKey({ +const key = JWK.asKey({ kty: 'oct', k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg' }) @@ -1031,7 +1053,7 @@ inferred from the provided `` instance. ```js const { JWK, JWS } = require('@panva/jose') -const key = JWK.importKey({ +const key = JWK.asKey({ kty: 'oct', k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg' }) @@ -1073,11 +1095,11 @@ Verifies the provided JWS in either serialization with a given `` or `< ```js const { JWK, JWS, JWKS } = require('@panva/jose') -const key = JWK.importKey({ +const key = JWK.asKey({ kty: 'oct', k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg' }) -const key2 = JWK.importKey({ +const key2 = JWK.asKey({ kty: 'oct', k: 'AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8' }) diff --git a/lib/help/base64url.js b/lib/help/base64url.js index df95d14be7..83b07bb801 100644 --- a/lib/help/base64url.js +++ b/lib/help/base64url.js @@ -1,3 +1,5 @@ +const b64uRegExp = /^[a-zA-Z0-9_-]*$/ + const fromBase64 = (base64) => { return base64.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_') } @@ -14,11 +16,17 @@ const encodeBuffer = (buf) => { return fromBase64(buf.toString('base64')) } -const decode = (input, encoding = 'utf8') => { - return Buffer.from(toBase64(input), 'base64').toString(encoding) +const decode = (input) => { + if (!b64uRegExp.test(input)) { + throw new TypeError('input is not a valid base64url encoded string') + } + return Buffer.from(toBase64(input), 'base64').toString('utf8') } const decodeToBuffer = (input) => { + if (!b64uRegExp.test(input)) { + throw new TypeError('input is not a valid base64url encoded string') + } return Buffer.from(toBase64(input), 'base64') } diff --git a/lib/help/key_utils.js b/lib/help/key_utils.js index 3654a64f7b..3814849a10 100644 --- a/lib/help/key_utils.js +++ b/lib/help/key_utils.js @@ -188,7 +188,7 @@ const concatEcPublicKey = (x, y) => ({ const jwkToPem = { RSA: { - private (jwk) { + private (jwk, { calculateMissingRSAPrimes }) { const RSAPrivateKey = asn1.get('RSAPrivateKey') if ('oth' in jwk) { @@ -197,10 +197,12 @@ const jwkToPem = { if (jwk.p || jwk.q || jwk.dp || jwk.dq || jwk.qi) { if (!(jwk.p && jwk.q && jwk.dp && jwk.dq && jwk.qi)) { - throw new errors.JWKImportFailed('all other private key parameters must be present when any one of them is present') + throw new errors.JWKInvalid('all other private key parameters must be present when any one of them is present') } - } else { + } else if (calculateMissingRSAPrimes) { jwk = computePrimes(jwk) + } else if (!calculateMissingRSAPrimes) { + throw new errors.JOSENotSupported('importing private RSA keys without all other private key parameters is not enabled, see documentation and its advisory on how and when its ok to enable it') } return RSAPrivateKey.encode({ @@ -293,7 +295,7 @@ const okpCrvToOid = (crv) => { } } -module.exports.jwkToPem = (jwk) => { +module.exports.jwkToPem = (jwk, { calculateMissingRSAPrimes = false } = {}) => { switch (jwk.kty) { case 'EC': if (!EC_CURVES.has(jwk.crv)) { @@ -312,7 +314,7 @@ module.exports.jwkToPem = (jwk) => { } if (jwk.d) { - return jwkToPem[jwk.kty].private(jwk) + return jwkToPem[jwk.kty].private(jwk, { calculateMissingRSAPrimes }) } return jwkToPem[jwk.kty].public(jwk) diff --git a/lib/help/rsa_primes.js b/lib/help/rsa_primes.js index 539640371b..5d77ec069a 100644 --- a/lib/help/rsa_primes.js +++ b/lib/help/rsa_primes.js @@ -3,6 +3,7 @@ const { randomBytes } = require('crypto') const base64url = require('./base64url') +const errors = require('../errors') const ZERO = BigInt(0) const ONE = BigInt(1) @@ -106,14 +107,29 @@ const odd = (n) => { return r } +// not sold on these values +const maxCountWhileNoY = 30 +const maxCountWhileInot0 = 22 + const getPrimeFactors = (e, d, n) => { const r = odd(e * d - ONE) + let countWhileNoY = 0 let y do { + countWhileNoY++ + if (countWhileNoY === maxCountWhileNoY) { + throw new errors.JWKImportFailed('failed to calculate missing primes') + } + + let countWhileInot0 = 0 let i = modPow(randBetween(TWO, n), r, n) let o = ZERO while (i !== ONE) { + countWhileInot0++ + if (countWhileInot0 === maxCountWhileInot0) { + throw new errors.JWKImportFailed('failed to calculate missing primes') + } o = i i = (i * i) % n } @@ -133,6 +149,10 @@ module.exports = (jwk) => { const d = fromBuffer(base64url.decodeToBuffer(jwk.d)) const n = fromBuffer(base64url.decodeToBuffer(jwk.n)) + if (d >= n) { + throw new errors.JWKInvalid('invalid RSA private exponent') + } + const { p, q } = getPrimeFactors(e, d, n) const dp = d % (p - ONE) const dq = d % (q - ONE) diff --git a/lib/index.d.ts b/lib/index.d.ts index fc4e75fbe6..8d112ae843 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -59,6 +59,10 @@ interface JSONWebKeySet { keys: JSONWebKey[] } +interface ImportOptions { + calculateMissingRSAPrimes?: boolean +} + export namespace JWK { interface pemEncodingOptions { @@ -139,6 +143,17 @@ export namespace JWK { export function isKey(object: any): boolean + export function asKey(keyObject: KeyObject, parameters?: KeyParameters): RSAKey | ECKey | OKPKey | OctKey + export function asKey(key: PrivateKeyInput | PublicKeyInput | string | Buffer, parameters?: KeyParameters): RSAKey | ECKey | OKPKey | OctKey + export function asKey(jwk: JWKOctKey): OctKey + export function asKey(jwk: JWKRSAKey, options?: ImportOptions): RSAKey + export function asKey(jwk: JWKECKey): ECKey + export function asKey(jwk: JWKOKPKey): OKPKey + + + /* + * @deprecated in favor of asKey + */ export function importKey(keyObject: KeyObject, parameters?: KeyParameters): RSAKey | ECKey | OKPKey | OctKey export function importKey(key: PrivateKeyInput | PublicKeyInput | string | Buffer, parameters?: KeyParameters): RSAKey | ECKey | OKPKey | OctKey export function importKey(jwk: JWKOctKey): OctKey @@ -185,7 +200,14 @@ export namespace JWKS { generateSync(kty: 'OKP', crv?: OKPCurve, parameters?: BasicParameters, private?: boolean): void generateSync(kty: 'RSA', bitlength?: number, parameters?: BasicParameters, private?: boolean): void generateSync(kty: 'oct', bitlength?: number, parameters?: BasicParameters): void + + /* + * @deprecated in favor of JWKS.asKeyStore + */ + static fromJWKS(jwks: JSONWebKeySet): KeyStore } + + export function asKeyStore(jwks: JSONWebKeySet, options?: ImportOptions): KeyStore } export namespace JWS { diff --git a/lib/jwe/decrypt.js b/lib/jwe/decrypt.js index f01f6ac400..8490d3236c 100644 --- a/lib/jwe/decrypt.js +++ b/lib/jwe/decrypt.js @@ -2,7 +2,7 @@ const { createSecretKey } = require('crypto') const { inflateRawSync } = require('zlib') const base64url = require('../help/base64url') -const KeyStore = require('../jwks/keystore') +const { KeyStore } = require('../jwks') const Key = require('../jwk/key/base') const errors = require('../errors') const { check, decrypt, keyManagementDecrypt } = require('../jwa') @@ -42,7 +42,7 @@ const combineHeader = (prot = {}, unprotected = {}, header = {}) => { */ const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], complete = false, algorithms } = {}) => { if (!(key instanceof Key) && !(key instanceof KeyStore)) { - throw new TypeError('key must be an instance of a key instantiated by JWK.importKey or a JWKS.KeyStore') + throw new TypeError('key must be an instance of a key instantiated by JWK.asKey or a JWKS.KeyStore') } if (algorithms !== undefined && (!Array.isArray(algorithms) || algorithms.some(s => typeof s !== 'string' || !s))) { @@ -130,13 +130,13 @@ const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], c try { if (alg === 'dir') { - cek = JWK.importKey(key, { alg: enc, use: 'enc' }) + cek = JWK.asKey(key, { alg: enc, use: 'enc' }) } else if (alg === 'ECDH-ES') { const unwrapped = keyManagementDecrypt(alg, key, undefined, opts) - cek = JWK.importKey(createSecretKey(unwrapped), { alg: enc, use: 'enc' }) + cek = JWK.asKey(createSecretKey(unwrapped), { alg: enc, use: 'enc' }) } else { const unwrapped = keyManagementDecrypt(alg, key, base64url.decodeToBuffer(encryptedKey), opts) - cek = JWK.importKey(createSecretKey(unwrapped), { alg: enc, use: 'enc' }) + cek = JWK.asKey(createSecretKey(unwrapped), { alg: enc, use: 'enc' }) } } catch (err) { // To mitigate the attacks described in RFC 3218, the diff --git a/lib/jwe/encrypt.js b/lib/jwe/encrypt.js index 0c9dec7278..b3fa074a0f 100644 --- a/lib/jwe/encrypt.js +++ b/lib/jwe/encrypt.js @@ -56,7 +56,7 @@ class Encrypt { */ recipient (key, header) { if (!(key instanceof Key)) { - throw new TypeError('key must be an instance of a key instantiated by JWK.importKey') + throw new TypeError('key must be an instance of a key instantiated by JWK.asKey') } if (header !== undefined && !isObject(header)) { diff --git a/lib/jwk/import.js b/lib/jwk/import.js index 61646f8975..6bf31393e8 100644 --- a/lib/jwk/import.js +++ b/lib/jwk/import.js @@ -1,4 +1,5 @@ const { createPublicKey, createPrivateKey, createSecretKey, KeyObject } = require('crypto') +const { deprecate } = require('util') const base64url = require('../help/base64url') const isObject = require('../help/is_object') @@ -24,7 +25,7 @@ const mergedParameters = (target = {}, source = {}) => { }, target) } -const importKey = (key, parameters) => { +const asKey = (key, parameters, { calculateMissingRSAPrimes = false } = {}) => { let privateKey, publicKey, secret if (!importable.has(typeof key)) { @@ -57,9 +58,10 @@ const importKey = (key, parameters) => { } parameters = mergedParameters(parameters, key) } else if (typeof key === 'object' && 'kty' in key) { // assume JWK formatted asymmetric key + ({ calculateMissingRSAPrimes = false } = parameters || { calculateMissingRSAPrimes }) let pem try { - pem = jwkToPem(key) + pem = jwkToPem(key, { calculateMissingRSAPrimes }) } catch (err) { if (err instanceof errors.JOSEError) { throw err @@ -70,7 +72,7 @@ const importKey = (key, parameters) => { } else if (pem) { publicKey = createPublicKey(pem) } - parameters = mergedParameters(parameters, key) + parameters = mergedParameters({}, key) } else { // | | passed to crypto.createPrivateKey or crypto.createPublicKey or passed to crypto.createSecretKey try { privateKey = createPrivateKey(key) @@ -110,4 +112,8 @@ const importKey = (key, parameters) => { throw new errors.JWKImportFailed('import failed') } -module.exports = importKey +module.exports = asKey +Object.defineProperty(asKey, 'deprecated', { + value: deprecate((key, parameters) => { return asKey(key, parameters, { calculateMissingRSAPrimes: true }) }, 'JWK.importKey() is deprecated, use JWK.asKey() instead'), + enumerable: false +}) diff --git a/lib/jwk/index.js b/lib/jwk/index.js index e6c57248ad..580908dc78 100644 --- a/lib/jwk/index.js +++ b/lib/jwk/index.js @@ -2,7 +2,13 @@ const Key = require('./key/base') const importKey = require('./import') const { generate, generateSync } = require('./generate') -module.exports.importKey = importKey +module.exports.asKey = importKey module.exports.generate = generate module.exports.generateSync = generateSync module.exports.isKey = input => input instanceof Key + +/* deprecated */ +Object.defineProperty(module.exports, 'importKey', { + value: importKey.deprecated, + enumerable: false +}) diff --git a/lib/jwks/index.js b/lib/jwks/index.js index 5b56614630..285a2798d4 100644 --- a/lib/jwks/index.js +++ b/lib/jwks/index.js @@ -1,3 +1,3 @@ const KeyStore = require('./keystore') -module.exports = { KeyStore } +module.exports = KeyStore diff --git a/lib/jwks/keystore.js b/lib/jwks/keystore.js index 8abb875135..1eb425f533 100644 --- a/lib/jwks/keystore.js +++ b/lib/jwks/keystore.js @@ -1,4 +1,4 @@ -const { inspect } = require('util') +const { deprecate, inspect } = require('util') const isObject = require('../help/is_object') const { generate, generateSync } = require('../jwk/generate') @@ -44,22 +44,12 @@ class KeyStore { keys = keys.flat() } if (keys.some(k => !(k instanceof Key))) { - throw new TypeError('all keys must be an instances of a key instantiated by JWK.importKey') + throw new TypeError('all keys must be an instances of a key instantiated by JWK.asKey') } this.#keys = new Set(keys) } - static fromJWKS (jwks) { - if (!isObject(jwks) || !Array.isArray(jwks.keys) || jwks.keys.some(k => !isObject(k) || !('kty' in k))) { - throw new TypeError('jwks must be a JSON Web Key Set formatted object') - } - - const keys = jwks.keys.map((jwk) => importKey(jwk)) - - return new KeyStore(...keys) - } - all ({ alg, kid, use, kty, key_ops: ops, x5t, 'x5t#S256': x5t256 } = {}) { if (ops !== undefined && (!Array.isArray(ops) || !ops.length || ops.some(x => typeof x !== 'string'))) { throw new TypeError('`key_ops` must be a non-empty array of strings') @@ -117,7 +107,7 @@ class KeyStore { add (key) { if (!(key instanceof Key)) { - throw new TypeError('key must be an instance of a key instantiated by JWK.importKey') + throw new TypeError('key must be an instance of a key instantiated by JWK.asKey') } this.#keys.add(key) @@ -125,7 +115,7 @@ class KeyStore { remove (key) { if (!(key instanceof Key)) { - throw new TypeError('key must be an instance of a key instantiated by JWK.importKey') + throw new TypeError('key must be an instance of a key instantiated by JWK.asKey') } this.#keys.delete(key) @@ -164,4 +154,19 @@ class KeyStore { } } -module.exports = KeyStore +function asKeyStore (jwks, { calculateMissingRSAPrimes = false } = {}) { + if (!isObject(jwks) || !Array.isArray(jwks.keys) || jwks.keys.some(k => !isObject(k) || !('kty' in k))) { + throw new TypeError('jwks must be a JSON Web Key Set formatted object') + } + + const keys = jwks.keys.map((jwk) => importKey(jwk, { calculateMissingRSAPrimes })) + + return new KeyStore(...keys) +} + +Object.defineProperty(KeyStore, 'fromJWKS', { + value: deprecate(jwks => asKeyStore(jwks, { calculateMissingRSAPrimes: true }), 'JWKS.KeyStore.fromJWKS() is deprecated, use JWKS.asKeyStore() instead'), + enumerable: false +}) + +module.exports = { KeyStore, asKeyStore } diff --git a/lib/jws/sign.js b/lib/jws/sign.js index 06fa97f6ff..e75f86c29c 100644 --- a/lib/jws/sign.js +++ b/lib/jws/sign.js @@ -35,7 +35,7 @@ class Sign { */ recipient (key, protectedHeader, unprotectedHeader) { if (!(key instanceof Key)) { - throw new TypeError('key must be an instance of a key instantiated by JWK.importKey') + throw new TypeError('key must be an instance of a key instantiated by JWK.asKey') } if (protectedHeader !== undefined && !isObject(protectedHeader)) { diff --git a/lib/jws/verify.js b/lib/jws/verify.js index 16910f8a52..904fc7d5d3 100644 --- a/lib/jws/verify.js +++ b/lib/jws/verify.js @@ -1,7 +1,7 @@ const base64url = require('../help/base64url') const isDisjoint = require('../help/is_disjoint') let validateCrit = require('../help/validate_crit') -const KeyStore = require('../jwks/keystore') +const { KeyStore } = require('../jwks') const Key = require('../jwk/key/base') const errors = require('../errors') const { check, verify } = require('../jwa') @@ -16,7 +16,7 @@ const SINGLE_RECIPIENT = new Set(['compact', 'flattened']) */ const jwsVerify = (skipDisjointCheck, serialization, jws, key, { crit = [], complete = false, algorithms } = {}) => { if (!(key instanceof Key) && !(key instanceof KeyStore)) { - throw new TypeError('key must be an instance of a key instantiated by JWK.importKey or a JWKS.KeyStore') + throw new TypeError('key must be an instance of a key instantiated by JWK.asKey or a JWKS.KeyStore') } if (algorithms !== undefined && (!Array.isArray(algorithms) || algorithms.some(s => typeof s !== 'string' || !s))) { diff --git a/lib/jwt/verify.js b/lib/jwt/verify.js index 8c61afe9c8..977c05315d 100644 --- a/lib/jwt/verify.js +++ b/lib/jwt/verify.js @@ -2,7 +2,7 @@ const isObject = require('../help/is_object') const epoch = require('../help/epoch') const secs = require('../help/secs') const JWS = require('../jws') -const KeyStore = require('../jwks/keystore') +const { KeyStore } = require('../jwks') const { JWTClaimInvalid } = require('../errors') const { isStringOptional, isNotString } = require('./shared_validations') diff --git a/test/cookbook/4_1.rsa_v15_signature.test.js b/test/cookbook/4_1.rsa_v15_signature.test.js index 1cdcd60b95..a16a041b12 100644 --- a/test/cookbook/4_1.rsa_v15_signature.test.js +++ b/test/cookbook/4_1.rsa_v15_signature.test.js @@ -2,15 +2,15 @@ const test = require('ava') const recipe = require('./recipes').get('4.1') -const { JWS, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWS, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { payload, key: jwk }, signing: { protected: header } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact sign`, t => { diff --git a/test/cookbook/4_2.rsa-pss_signature.test.js b/test/cookbook/4_2.rsa-pss_signature.test.js index 83630c6aa0..7ee91fb54c 100644 --- a/test/cookbook/4_2.rsa-pss_signature.test.js +++ b/test/cookbook/4_2.rsa-pss_signature.test.js @@ -3,15 +3,15 @@ const test = require('ava') const recipe = require('./recipes').get('4.2') const { sig: verifiers } = require('./verifiers') -const { JWS, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWS, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { payload, key: jwk }, signing: { protected: header } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact sign (random)`, t => { diff --git a/test/cookbook/4_3.ecdsa_signature.test.js b/test/cookbook/4_3.ecdsa_signature.test.js index e09155082a..85c14b0510 100644 --- a/test/cookbook/4_3.ecdsa_signature.test.js +++ b/test/cookbook/4_3.ecdsa_signature.test.js @@ -3,15 +3,15 @@ const test = require('ava') const recipe = require('./recipes').get('4.3') const { sig: verifiers } = require('./verifiers') -const { JWS, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWS, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { payload, key: jwk }, signing: { protected: header } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.crv, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.crv, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.crv, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact sign (random)`, t => { diff --git a/test/cookbook/4_4.hmac-sha2_integrity_protection.test.js b/test/cookbook/4_4.hmac-sha2_integrity_protection.test.js index 841c8e1259..6decf67eab 100644 --- a/test/cookbook/4_4.hmac-sha2_integrity_protection.test.js +++ b/test/cookbook/4_4.hmac-sha2_integrity_protection.test.js @@ -2,15 +2,15 @@ const test = require('ava') const recipe = require('./recipes').get('4.4') -const { JWS, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWS, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { payload, key: jwk }, signing: { protected: header } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact sign`, t => { diff --git a/test/cookbook/4_6.protecting_specific_header_fields.test.js b/test/cookbook/4_6.protecting_specific_header_fields.test.js index 9061ae03a6..7ca420fb10 100644 --- a/test/cookbook/4_6.protecting_specific_header_fields.test.js +++ b/test/cookbook/4_6.protecting_specific_header_fields.test.js @@ -2,15 +2,15 @@ const test = require('ava') const recipe = require('./recipes').get('4.6') -const { JWS, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWS, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { payload, key: jwk }, signing: { protected: protec, unprotected } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - flattened sign`, t => { diff --git a/test/cookbook/4_7.protecting_content_only.test.js b/test/cookbook/4_7.protecting_content_only.test.js index 8d216d669c..34e46a48c4 100644 --- a/test/cookbook/4_7.protecting_content_only.test.js +++ b/test/cookbook/4_7.protecting_content_only.test.js @@ -2,15 +2,15 @@ const test = require('ava') const recipe = require('./recipes').get('4.7') -const { JWS, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWS, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { payload, key: jwk }, signing: { unprotected } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - flattened sign`, t => { diff --git a/test/cookbook/4_8.multiple_signatures.test.js b/test/cookbook/4_8.multiple_signatures.test.js index 4a1c46129f..cb0cb5a82f 100644 --- a/test/cookbook/4_8.multiple_signatures.test.js +++ b/test/cookbook/4_8.multiple_signatures.test.js @@ -2,11 +2,11 @@ const test = require('ava') const recipe = require('./recipes').get('4.8') -const { JWS, JWK: { importKey }, JWKS: { KeyStore }, errors } = require('../..') +const { JWS, JWK: { asKey }, JWKS: { KeyStore }, errors } = require('../..') const { input: { payload, key: jwks }, signing: recipients } = recipe -const keys = jwks.map((jwk) => importKey(jwk)) +const keys = jwks.map((jwk) => asKey(jwk)) const keystoreEmpty = new KeyStore() const keystore = new KeyStore(...keys) diff --git a/test/cookbook/5_1.key_encryption_using_rsa_v15_and_aes-hmac-sha2.test.js b/test/cookbook/5_1.key_encryption_using_rsa_v15_and_aes-hmac-sha2.test.js index a5f3a02037..b8de073e32 100644 --- a/test/cookbook/5_1.key_encryption_using_rsa_v15_and_aes-hmac-sha2.test.js +++ b/test/cookbook/5_1.key_encryption_using_rsa_v15_and_aes-hmac-sha2.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.1') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk }, encrypting_content: { protected: prot } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact encrypt`, t => { diff --git a/test/cookbook/5_10.including_additional_authentication_data.test.js b/test/cookbook/5_10.including_additional_authentication_data.test.js index 651b43d56d..f566492052 100644 --- a/test/cookbook/5_10.including_additional_authentication_data.test.js +++ b/test/cookbook/5_10.including_additional_authentication_data.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.10') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk, aad }, encrypting_content: { protected: prot } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - flattened encrypt`, t => { diff --git a/test/cookbook/5_11.protecting_specific_header_fields.test.js b/test/cookbook/5_11.protecting_specific_header_fields.test.js index 3f2e44f615..32ddb81e0d 100644 --- a/test/cookbook/5_11.protecting_specific_header_fields.test.js +++ b/test/cookbook/5_11.protecting_specific_header_fields.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.11') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk }, encrypting_content: { protected: prot, unprotected } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - flattened encrypt`, t => { diff --git a/test/cookbook/5_12.protecting_content_only.test.js b/test/cookbook/5_12.protecting_content_only.test.js index 874665d076..95410df8f1 100644 --- a/test/cookbook/5_12.protecting_content_only.test.js +++ b/test/cookbook/5_12.protecting_content_only.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.12') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk }, encrypting_content: { unprotected } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - flattened encrypt`, t => { diff --git a/test/cookbook/5_13.encrypting_to_multiple_recipients.test.js b/test/cookbook/5_13.encrypting_to_multiple_recipients.test.js index 6ed304f688..72bb225a8b 100644 --- a/test/cookbook/5_13.encrypting_to_multiple_recipients.test.js +++ b/test/cookbook/5_13.encrypting_to_multiple_recipients.test.js @@ -3,7 +3,7 @@ const test = require('ava') const recipe = require('./recipes').get('5.13') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwks }, @@ -11,7 +11,7 @@ const { encrypting_key: recipients } = recipe -const keys = jwks.map((jwk) => importKey(jwk)) +const keys = jwks.map((jwk) => asKey(jwk)) const keystoreEmpty = new KeyStore() const keystore = new KeyStore(...keys) diff --git a/test/cookbook/5_2.key_encryption_using_rsa-oaep_with_aes-gcm.test.js b/test/cookbook/5_2.key_encryption_using_rsa-oaep_with_aes-gcm.test.js index 3e28e0b753..7ba5171e6e 100644 --- a/test/cookbook/5_2.key_encryption_using_rsa-oaep_with_aes-gcm.test.js +++ b/test/cookbook/5_2.key_encryption_using_rsa-oaep_with_aes-gcm.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.2') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk }, encrypting_content: { protected: prot } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact encrypt`, t => { diff --git a/test/cookbook/5_3.key_wrap_using_pbes2-aes-keywrap_with-aes-cbc-hmac-sha2.test.js b/test/cookbook/5_3.key_wrap_using_pbes2-aes-keywrap_with-aes-cbc-hmac-sha2.test.js index fd8b217623..e5603fc20d 100644 --- a/test/cookbook/5_3.key_wrap_using_pbes2-aes-keywrap_with-aes-cbc-hmac-sha2.test.js +++ b/test/cookbook/5_3.key_wrap_using_pbes2-aes-keywrap_with-aes-cbc-hmac-sha2.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.3') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, pwd }, encrypting_content: { protected: prot } } = recipe -const key = importKey(Buffer.from(pwd)) +const key = asKey(Buffer.from(pwd)) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync('EC'), generateSync('RSA')) test(`${recipe.title} - compact encrypt`, t => { diff --git a/test/cookbook/5_4.key_agreement_with_key_wrapping_using_ecdh-es_and_aes-keywrap_with_aes-gcm.test.js b/test/cookbook/5_4.key_agreement_with_key_wrapping_using_ecdh-es_and_aes-keywrap_with_aes-gcm.test.js index 68349e436f..3c929c8c2d 100644 --- a/test/cookbook/5_4.key_agreement_with_key_wrapping_using_ecdh-es_and_aes-keywrap_with_aes-gcm.test.js +++ b/test/cookbook/5_4.key_agreement_with_key_wrapping_using_ecdh-es_and_aes-keywrap_with_aes-gcm.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.4') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk }, encrypting_content: { protected: prot } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.crv, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.crv, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.crv, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact encrypt`, t => { diff --git a/test/cookbook/5_5.key_agreement_using_ecdh-es_with_aes-cbc-hmac-sha2.test.js b/test/cookbook/5_5.key_agreement_using_ecdh-es_with_aes-cbc-hmac-sha2.test.js index f69d5f92ca..f3900f0421 100644 --- a/test/cookbook/5_5.key_agreement_using_ecdh-es_with_aes-cbc-hmac-sha2.test.js +++ b/test/cookbook/5_5.key_agreement_using_ecdh-es_with_aes-cbc-hmac-sha2.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.5') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk }, encrypting_content: { protected: prot } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.crv, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.crv, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.crv, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact encrypt`, t => { diff --git a/test/cookbook/5_6.direct_encryption_using_aes-gcm.test.js b/test/cookbook/5_6.direct_encryption_using_aes-gcm.test.js index c59bd5e120..96d674f780 100644 --- a/test/cookbook/5_6.direct_encryption_using_aes-gcm.test.js +++ b/test/cookbook/5_6.direct_encryption_using_aes-gcm.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.6') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk }, encrypting_content: { protected: prot } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact encrypt`, t => { diff --git a/test/cookbook/5_7.key_wrap_using_aes-gcm_keywrap_with_aes-cbc-hmac-sha2.test.js b/test/cookbook/5_7.key_wrap_using_aes-gcm_keywrap_with_aes-cbc-hmac-sha2.test.js index e7edee31fd..03d031807e 100644 --- a/test/cookbook/5_7.key_wrap_using_aes-gcm_keywrap_with_aes-cbc-hmac-sha2.test.js +++ b/test/cookbook/5_7.key_wrap_using_aes-gcm_keywrap_with_aes-cbc-hmac-sha2.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.7') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk }, encrypting_content: { protected: prot } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact encrypt`, t => { diff --git a/test/cookbook/5_8.key_wrap_using_aes-keywrap_with_aes-gcm.test.js b/test/cookbook/5_8.key_wrap_using_aes-keywrap_with_aes-gcm.test.js index decbf5631b..dfb5ff50ad 100644 --- a/test/cookbook/5_8.key_wrap_using_aes-keywrap_with_aes-gcm.test.js +++ b/test/cookbook/5_8.key_wrap_using_aes-keywrap_with_aes-gcm.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.8') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk }, encrypting_content: { protected: prot } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact encrypt`, t => { diff --git a/test/cookbook/5_9.compressed_content.test.js b/test/cookbook/5_9.compressed_content.test.js index 2629d52ac3..ed685ad97d 100644 --- a/test/cookbook/5_9.compressed_content.test.js +++ b/test/cookbook/5_9.compressed_content.test.js @@ -3,18 +3,18 @@ const test = require('ava') const recipe = require('./recipes').get('5.9') const { enc: verifiers } = require('./verifiers') -const { JWE, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWE, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { plaintext, key: jwk }, encrypting_content: { protected: prot } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - compact encrypt`, t => { diff --git a/test/cookbook/jwk.test.js b/test/cookbook/jwk.test.js index 23aac3c72b..0f4000e33a 100644 --- a/test/cookbook/jwk.test.js +++ b/test/cookbook/jwk.test.js @@ -2,11 +2,11 @@ const test = require('ava') const recipes = require('./recipes') -const { JWK: { importKey }, JWKS: { KeyStore } } = require('../..') +const { JWK: { asKey }, JWKS: { KeyStore } } = require('../..') test('public EC', t => { const jwk = recipes.get('3.1') - const key = importKey(jwk) + const key = asKey(jwk) t.true(key.toPEM().includes('BEGIN PUBLIC KEY')) t.deepEqual(key.toJWK(), jwk) t.deepEqual(key.toJWK(false), jwk) @@ -26,7 +26,7 @@ test('public EC', t => { test('private EC', t => { const jwk = recipes.get('3.2') - const key = importKey(jwk) + const key = asKey(jwk) t.true(key.toPEM(true, { cipher: 'aes-256-cbc', passphrase: 'top secret' }).includes('BEGIN ENCRYPTED PRIVATE KEY')) t.true(key.toPEM(true, { type: 'sec1' }).includes('BEGIN EC PRIVATE KEY')) t.true(key.toPEM(true, { type: 'sec1', cipher: 'aes-256-cbc', passphrase: 'top secret' }).includes('ENCRYPTED')) @@ -46,7 +46,7 @@ test('private EC', t => { test('public RSA', t => { const jwk = recipes.get('3.3') - const key = importKey(jwk) + const key = asKey(jwk) t.true(key.toPEM().includes('BEGIN PUBLIC KEY')) t.deepEqual(key.toJWK(), jwk) t.deepEqual(key.toJWK(false), jwk) @@ -60,7 +60,7 @@ test('public RSA', t => { test('private RSA', t => { const jwk = recipes.get('3.4') - const key = importKey(jwk) + const key = asKey(jwk) t.true(key.toPEM(true, { type: 'pkcs1' }).includes('BEGIN RSA PRIVATE KEY')) t.true(key.toPEM(true, { cipher: 'aes-256-cbc', passphrase: 'top secret', type: 'pkcs1' }).includes('ENCRYPTED')) t.true(key.toPEM(true, { type: 'pkcs1', cipher: 'aes-256-cbc', passphrase: 'top secret' }).includes('BEGIN RSA PRIVATE KEY')) @@ -75,7 +75,7 @@ test('private RSA', t => { test('oct (1/2)', t => { const jwk = recipes.get('3.5') - const key = importKey(jwk) + const key = asKey(jwk) t.throws(() => { key.toPEM() }, { instanceOf: TypeError, message: 'symmetric keys cannot be exported as PEM' }) @@ -87,7 +87,7 @@ test('oct (1/2)', t => { test('oct (2/2)', t => { const jwk = recipes.get('3.6') - const key = importKey(jwk) + const key = asKey(jwk) t.deepEqual(key.toJWK(true), jwk) const { k, ...pub } = jwk t.deepEqual(key.toJWK(), pub) @@ -101,11 +101,11 @@ test('keystore .toJWKS()', t => { const { d, dp, dq, p, q, qi, ...pubRsa } = rsa const oct = recipes.get('3.5') const { k, ...pubOct } = oct - const ks = new KeyStore(importKey(ec), importKey(rsa), importKey(oct)) + const ks = new KeyStore(asKey(ec), asKey(rsa), asKey(oct)) t.deepEqual(ks.toJWKS(true), { keys: [ec, rsa, oct] }) t.deepEqual(ks.toJWKS(), { keys: [pubEc, pubRsa, pubOct] }) t.deepEqual(ks.toJWKS(false), { keys: [pubEc, pubRsa, pubOct] }) - ks.add(importKey(pubRsa)) + ks.add(asKey(pubRsa)) t.throws(() => { ks.toJWKS(true) }, { instanceOf: TypeError, message: 'public key cannot be exported as private' }) diff --git a/test/cookbook/rfc7797.4_1.hmac-sha2_b64_false.test.js b/test/cookbook/rfc7797.4_1.hmac-sha2_b64_false.test.js index f6c9e72acb..455e132cdb 100644 --- a/test/cookbook/rfc7797.4_1.hmac-sha2_b64_false.test.js +++ b/test/cookbook/rfc7797.4_1.hmac-sha2_b64_false.test.js @@ -2,15 +2,15 @@ const test = require('ava') const recipe = require('./recipes').get('4.1 rfc7797') -const { JWS, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWS, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { payload, key: jwk }, signing: { protected: header } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync('EC'), generateSync('RSA')) test(`${recipe.title} - compact sign`, t => { diff --git a/test/cookbook/rfc7797.4_2.hmac-sha2_b64_false.js b/test/cookbook/rfc7797.4_2.hmac-sha2_b64_false.js index 560a067418..ae6ca3f072 100644 --- a/test/cookbook/rfc7797.4_2.hmac-sha2_b64_false.js +++ b/test/cookbook/rfc7797.4_2.hmac-sha2_b64_false.js @@ -2,15 +2,15 @@ const test = require('ava') const recipe = require('./recipes').get('4.2 rfc7797') -const { JWS, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWS, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { payload, key: jwk }, signing: { protected: header } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync(key.kty), generateSync(key.kty)) test(`${recipe.title} - flattened sign`, t => { diff --git a/test/cookbook/rfc8037.a4.ed25519.test.js b/test/cookbook/rfc8037.a4.ed25519.test.js index 821a0f3ccf..667d7ebaec 100644 --- a/test/cookbook/rfc8037.a4.ed25519.test.js +++ b/test/cookbook/rfc8037.a4.ed25519.test.js @@ -2,11 +2,11 @@ const test = require('ava') const recipe = require('./recipes').get('A.4 rfc8037') -const { JWS, JWK: { importKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') +const { JWS, JWK: { asKey, generateSync }, JWKS: { KeyStore }, errors } = require('../..') const { input: { payload, key: jwk }, signing: { protected: header } } = recipe -const key = importKey(jwk) +const key = asKey(jwk) test('OKP JWK Thumbprint Canonicalization', t => { t.is(key.kid, 'kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k') @@ -14,7 +14,7 @@ test('OKP JWK Thumbprint Canonicalization', t => { const keystoreEmpty = new KeyStore() const keystoreMatchOne = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use }), key) -const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, importKey(key)) +const keystoreMatchMore = new KeyStore(generateSync(key.kty, key.length, { alg: key.alg, use: key.use, kid: key.kid }), key, asKey(key)) const keystoreMatchNone = new KeyStore(generateSync('EC'), generateSync('RSA')) test(`${recipe.title} - compact sign`, t => { diff --git a/test/help/base64url.test.js b/test/help/base64url.test.js index e51c6fc6c2..9a561f0597 100644 --- a/test/help/base64url.test.js +++ b/test/help/base64url.test.js @@ -21,10 +21,6 @@ test('.decode with default encoding', t => { t.is(base64url.decode('Zm9v'), 'foo') }) -test('.decode with non-default encoding', t => { - t.is(base64url.decode('Zm9v', 'hex'), '666f6f') -}) - test('.decodeToBuffer', t => { t.deepEqual(base64url.decodeToBuffer('fmkIOj-kafqtjMl-iC32a-9YGz0cKj_JT9Jt31uXR1la7FSXkjoBzg_F-huYm0udbM5z5qGlmPBNZASsixJLcA'), testBuf) }) diff --git a/test/help/ecdsa_signatures.test.js b/test/help/ecdsa_signatures.test.js index bfb2f7bda0..65c2ecedc2 100644 --- a/test/help/ecdsa_signatures.test.js +++ b/test/help/ecdsa_signatures.test.js @@ -152,7 +152,7 @@ test('.joseToDer non buffer or base64 signature', t => { }) test('.joseToDer unknown algorithm', t => { - t.throws(() => joseToDer(decodeToBuffer('Zm9vLmJhci5iYXo='), 'foobar'), { instanceOf: Error, message: /"foobar"/ }) + t.throws(() => joseToDer(decodeToBuffer('Zm9vLmJhci5iYXo'), 'foobar'), { instanceOf: Error, message: /"foobar"/ }) }) test('.joseToDer incorrect signature length (ES256)', t => { @@ -176,7 +176,7 @@ test('ES256 should jose -> der -> jose', t => { }) test('ES256 should der -> jose -> der', t => { - const expected = decodeToBuffer('MEUCIQD0nDQE4uBS6JuklnyACfPQRB/LMEh5Stq6sAfp38k6ewIgHvhX59iuruBiFpVkg3dQKJ3+Wk29lJmXfxp6ciRdj+Q=') + const expected = decodeToBuffer('MEUCIQD0nDQE4uBS6JuklnyACfPQRB_LMEh5Stq6sAfp38k6ewIgHvhX59iuruBiFpVkg3dQKJ3-Wk29lJmXfxp6ciRdj-Q') const jose = derToJose(expected, 'ES256') const actual = joseToDer(jose, 'ES256') @@ -192,7 +192,7 @@ test('ES384 should jose -> der -> jose', t => { }) test('ES384 should der -> jose -> der', t => { - const expected = decodeToBuffer('MGUCMADcY5icKo+sLF0YCh5eVzju55Elt3Dfu4geMMDnUlLNaEO8NiCFzCHeqMx7mW5GMwIxAI6sp8ihHjRJ0sn/WV6mZCxN6/5lEg1QZJ5eiUHYv2kBgmiJ/Yv1pnqqFY3gVDBp/g==') + const expected = decodeToBuffer('MGUCMADcY5icKo-sLF0YCh5eVzju55Elt3Dfu4geMMDnUlLNaEO8NiCFzCHeqMx7mW5GMwIxAI6sp8ihHjRJ0sn_WV6mZCxN6_5lEg1QZJ5eiUHYv2kBgmiJ_Yv1pnqqFY3gVDBp_g') const jose = derToJose(expected, 'ES384') const actual = joseToDer(jose, 'ES384') @@ -208,7 +208,7 @@ test('ES512 should jose -> der -> jose', t => { }) test('ES512 should der -> jose -> der', t => { - const expected = decodeToBuffer('MIGHAkFgiYpVsYxx6XiQp2OXscRW/PrbEcoime/FftP+B7x4QVa+M3KZzXlfP66zKqjo7O3nwK2s8GbTftW8H4HwojzimwJCAYQNsozTpCo5nwIkBgelcfIQ0y/U/60TbNH1+rlKpFDCFs6Q1ro7R1tjtXoAUb9aPIOVyXGiSQX/+fcmmWs1rkJU') + const expected = decodeToBuffer('MIGHAkFgiYpVsYxx6XiQp2OXscRW_PrbEcoime_FftP-B7x4QVa-M3KZzXlfP66zKqjo7O3nwK2s8GbTftW8H4HwojzimwJCAYQNsozTpCo5nwIkBgelcfIQ0y_U_60TbNH1-rlKpFDCFs6Q1ro7R1tjtXoAUb9aPIOVyXGiSQX_-fcmmWs1rkJU') const jose = derToJose(expected, 'ES512') const actual = joseToDer(jose, 'ES512') diff --git a/test/jwe/sanity.test.js b/test/jwe/sanity.test.js index c66c4c3691..5584840c3d 100644 --- a/test/jwe/sanity.test.js +++ b/test/jwe/sanity.test.js @@ -38,7 +38,7 @@ test('verify key or store argument', t => { ;[{}, new Object(), false, null, Infinity, 0, Buffer.from('foo')].forEach((val) => { // eslint-disable-line no-new-object t.throws(() => { JWE.decrypt('....', val) - }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.importKey or a JWKS.KeyStore' }) + }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.asKey or a JWKS.KeyStore' }) }) }) @@ -288,7 +288,7 @@ test('JWE encrypt rejects non keys', t => { ;[[], false, true, undefined, null, Infinity, 0].forEach((val) => { t.throws(() => { JWE.encrypt('foo', val) - }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.importKey' }) + }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.asKey' }) }) }) diff --git a/test/jwe/smoke.test.js b/test/jwe/smoke.test.js index 852b919e6b..b63c14a83e 100644 --- a/test/jwe/smoke.test.js +++ b/test/jwe/smoke.test.js @@ -3,7 +3,7 @@ const test = require('ava') const { randomBytes } = require('crypto') const { encrypt, decrypt } = require('../../lib/jwe') -const { JWK: { importKey, generateSync }, errors } = require('../..') +const { JWK: { asKey, generateSync }, errors } = require('../..') const PAYLOAD = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' const ENCS = [ @@ -98,8 +98,8 @@ const failure = (t, eKey, dKey, alg, enc) => { } Object.entries(fixtures.PEM).forEach(([type, { private: key, public: pub }]) => { - const eKey = importKey(pub) - const dKey = importKey(key) + const eKey = asKey(pub) + const dKey = asKey(key) ;[...eKey.algorithms('wrapKey'), ...eKey.algorithms('deriveKey')].forEach((alg) => { ENCS.forEach((enc) => { @@ -111,7 +111,7 @@ Object.entries(fixtures.PEM).forEach(([type, { private: key, public: pub }]) => }) ;[16, 24, 32, 48, 64].forEach((len) => { - const sym = importKey(randomBytes(len)) + const sym = asKey(randomBytes(len)) ;[...sym.algorithms('wrapKey'), ...sym.algorithms('deriveKey')].forEach((alg) => { sym.algorithms('encrypt').forEach((enc) => { test(`key ${sym.kty} > alg ${alg} > ${enc}`, success, sym, sym, alg, enc) @@ -122,8 +122,8 @@ Object.entries(fixtures.PEM).forEach(([type, { private: key, public: pub }]) => { const rsa = generateSync('RSA') - const dKey = importKey({ kty: 'RSA', e: rsa.e, n: rsa.n, d: rsa.d }) - const eKey = importKey({ kty: 'RSA', e: rsa.e, n: rsa.n }) + const dKey = asKey({ kty: 'RSA', e: rsa.e, n: rsa.n, d: rsa.d }, { calculateMissingRSAPrimes: true }) + const eKey = asKey({ kty: 'RSA', e: rsa.e, n: rsa.n }) eKey.algorithms('wrapKey').forEach((alg) => { ENCS.forEach((enc) => { if (alg === 'ECDH-ES' && ['A192CBC-HS384', 'A256CBC-HS512'].includes(enc)) return diff --git a/test/jwk/general.test.js b/test/jwk/general.test.js index e3f3c0eca2..a3c75041d0 100644 --- a/test/jwk/general.test.js +++ b/test/jwk/general.test.js @@ -1,6 +1,6 @@ const test = require('ava') -const { JWK: { generateSync, isKey, importKey } } = require('../..') +const { JWK: { generateSync, isKey, asKey } } = require('../..') test('.isKey() only key objects return true', t => { ;[[], false, true, null, Infinity, 0].forEach((val) => { @@ -40,7 +40,7 @@ test('"kid" must be a non-empty string', t => { test('"kid" from JWK is used when available and its different from thumbprint', t => { const { kid: generatedThumbprint, ...jwk } = generateSync('oct').toJWK(true) - const key = importKey({ ...jwk, kid: 'foo' }) + const key = asKey({ ...jwk, kid: 'foo' }) t.is(key.kid, 'foo') t.is(key.thumbprint, generatedThumbprint) }) diff --git a/test/jwk/import.test.js b/test/jwk/import.test.js index ff79d69f3a..d0a8adbf74 100644 --- a/test/jwk/import.test.js +++ b/test/jwk/import.test.js @@ -1,29 +1,29 @@ const test = require('ava') const crypto = require('crypto') -const { JWS, JWE, JWK: { importKey, generate }, errors } = require('../..') +const { JWS, JWE, JWK: { asKey, importKey, generate }, errors } = require('../..') const fixtures = require('../fixtures') test('imports PrivateKeyObject and then its Key instance', t => { - const k = importKey(crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }).privateKey) - t.deepEqual(importKey(k).toJWK(), k.toJWK()) + const k = asKey(crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }).privateKey) + t.deepEqual(asKey(k).toJWK(), k.toJWK()) }) test('imports PublicKeyObject and then its Key instance', t => { - const k = importKey(crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }).publicKey) - t.deepEqual(importKey(k).toJWK(), k.toJWK()) + const k = asKey(crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }).publicKey) + t.deepEqual(asKey(k).toJWK(), k.toJWK()) }) test('imports SecretKeyObject and then its Key instance', t => { - const k = importKey(crypto.createSecretKey(Buffer.from('foo'))) - t.deepEqual(importKey(k).toJWK(), k.toJWK()) + const k = asKey(crypto.createSecretKey(Buffer.from('foo'))) + t.deepEqual(asKey(k).toJWK(), k.toJWK()) }) test('only imports string, object or buffer', t => { ;[Buffer, () => {}, async () => {}, true, Infinity, 1].forEach((val) => { t.throws(() => { - importKey(val) + asKey(val) }, { instanceOf: TypeError, message: 'key argument must be a string, buffer or an object' }) }) }) @@ -31,7 +31,7 @@ test('only imports string, object or buffer', t => { test('parameters must be a plain object', t => { ;[Buffer, () => {}, async () => {}, true, Infinity, 1, [], Buffer.from('foo')].forEach((val) => { t.throws(() => { - importKey('foo', val) + asKey('foo', val) }, { instanceOf: TypeError, message: 'parameters argument must be a plain object when provided' }) }) }) @@ -39,35 +39,35 @@ test('parameters must be a plain object', t => { Object.entries(fixtures.PEM).forEach(([type, { private: priv, public: pub }]) => { test(`fails to import ${type} as invalid string`, t => { t.throws(() => { - importKey(priv.toString('ascii').replace(/\n/g, '')) + asKey(priv.toString('ascii').replace(/\n/g, '')) }, { instanceOf: errors.JWKImportFailed, code: 'ERR_JWK_IMPORT_FAILED' }) }) test(`fails to import ${type} as invalid buffer`, t => { t.throws(() => { - importKey(Buffer.from(priv.toString('ascii').replace(/\n/g, ''))) + asKey(Buffer.from(priv.toString('ascii').replace(/\n/g, ''))) }, { instanceOf: errors.JWKImportFailed, code: 'ERR_JWK_IMPORT_FAILED' }) }) test(`${type} private can be imported as a string`, t => { - const k = importKey(priv.toString('ascii')) + const k = asKey(priv.toString('ascii')) t.true(k.private) }) test(`${type} public can be imported as a string`, t => { - const k = importKey(pub.toString('ascii')) + const k = asKey(pub.toString('ascii')) t.true(k.public) }) test(`${type} private can be imported as a buffer`, t => { - const k = importKey(priv) + const k = asKey(priv) t.true(k.private) }) test(`${type} public can be imported as a buffer`, t => { - const k = importKey(pub) + const k = asKey(pub) t.true(k.public) }) }) test('failed to import throws an error', t => { t.throws(() => { - importKey({ + asKey({ key: fixtures.PEM.RSA.public, format: 'der' }) @@ -81,7 +81,7 @@ test('failed to import throws an error', t => { ].forEach((unsupported, i) => { test(`fails to import unsupported PEM ${i + 1}/4`, t => { t.throws(() => { - importKey(unsupported) + asKey(unsupported) }, { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'only RSA, EC and OKP asymmetric keys are supported' }) }) }) @@ -89,7 +89,8 @@ test('failed to import throws an error', t => { test('minimal RSA test', async t => { const key = await generate('RSA') const { d, e, n } = key.toJWK(true) - const minKey = importKey({ kty: 'RSA', d, e, n }) + const minKey = asKey({ kty: 'RSA', d, e, n }, { calculateMissingRSAPrimes: true }) + importKey({ kty: 'RSA', d, e, n }) // deprecated key.algorithms('sign').forEach((alg) => { JWS.verify(JWS.sign({}, key), minKey, { alg }) JWS.verify(JWS.sign({}, minKey), key, { alg }) @@ -98,7 +99,15 @@ test('minimal RSA test', async t => { JWE.decrypt(JWE.encrypt('foo', key), minKey, { alg }) JWE.decrypt(JWE.encrypt('foo', minKey), key, { alg }) }) - t.pass() + t.throws(() => { + asKey({ kty: 'RSA', d: d.substr(1), e, n }, { calculateMissingRSAPrimes: true }) + }, { instanceOf: errors.JWKImportFailed, code: 'ERR_JWK_IMPORT_FAILED', message: 'failed to calculate missing primes' }) + t.throws(() => { + asKey({ kty: 'RSA', d, e, n }) + }, { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'importing private RSA keys without all other private key parameters is not enabled, see documentation and its advisory on how and when its ok to enable it' }) + t.throws(() => { + asKey({ kty: 'RSA', d: `${d}F`, e, n }, { calculateMissingRSAPrimes: true }) + }, { instanceOf: errors.JWKInvalid, code: 'ERR_JWK_INVALID', message: 'invalid RSA private exponent' }) }) test('fails to import RSA without all optimization parameters', async t => { @@ -106,15 +115,15 @@ test('fails to import RSA without all optimization parameters', async t => { for (const param of ['p', 'q', 'dp', 'dq', 'qi']) { const { [param]: omit, ...jwk } = full t.throws(() => { - importKey(jwk) - }, { instanceOf: errors.JWKImportFailed, code: 'ERR_JWK_IMPORT_FAILED', message: 'all other private key parameters must be present when any one of them is present' }) + asKey(jwk) + }, { instanceOf: errors.JWKInvalid, code: 'ERR_JWK_INVALID', message: 'all other private key parameters must be present when any one of them is present' }) } }) test('fails to import JWK RSA with oth', async t => { const jwk = (await generate('RSA')).toJWK(true) t.throws(() => { - importKey({ + asKey({ ...jwk, oth: [] }) diff --git a/test/jwk/key_ops.test.js b/test/jwk/key_ops.test.js index 94be1ca5aa..5c15935e83 100644 --- a/test/jwk/key_ops.test.js +++ b/test/jwk/key_ops.test.js @@ -2,77 +2,77 @@ const test = require('ava') const crypto = require('crypto') const errors = require('../../lib/errors') -const importKey = require('../../lib/jwk/import') +const asKey = require('../../lib/jwk/import') const { generateSync } = require('../../lib/jwk/generate') -const jwk = importKey('foo').toJWK(true) +const jwk = asKey('foo').toJWK(true) test('key_ops ignores unrecognized values', t => { - importKey({ ...jwk, key_ops: ['sign', 'verify', 'foo'] }) + asKey({ ...jwk, key_ops: ['sign', 'verify', 'foo'] }) t.pass() }) test('key_ops ignores duplicate values', t => { - const k = importKey({ ...jwk, key_ops: ['sign', 'verify', 'sign'] }) + const k = asKey({ ...jwk, key_ops: ['sign', 'verify', 'sign'] }) t.deepEqual(k.key_ops, ['sign', 'verify']) }) test('key_ops can be combined with use if consistent', t => { - importKey({ ...jwk, key_ops: ['sign', 'verify'], use: 'sig' }) + asKey({ ...jwk, key_ops: ['sign', 'verify'], use: 'sig' }) t.pass() }) test('key_ops are part of toJWK', t => { - const k = importKey({ ...jwk, key_ops: ['sign', 'verify'], use: 'sig' }) + const k = asKey({ ...jwk, key_ops: ['sign', 'verify'], use: 'sig' }) t.deepEqual(k.toJWK().key_ops, ['sign', 'verify']) t.deepEqual(k.toJWK(true).key_ops, ['sign', 'verify']) }) test('key_ops must be an array', t => { t.throws(() => { - importKey({ ...jwk, key_ops: 'wrapKey' }) + asKey({ ...jwk, key_ops: 'wrapKey' }) }, { instanceOf: TypeError, message: '`key_ops` must be a non-empty array of strings when provided' }) }) test('key_ops must not be empty', t => { t.throws(() => { - importKey({ ...jwk, key_ops: [] }) + asKey({ ...jwk, key_ops: [] }) }, { instanceOf: TypeError, message: '`key_ops` must be a non-empty array of strings when provided' }) }) test('key_ops must only contain strings', t => { t.throws(() => { - importKey({ ...jwk, key_ops: ['wrapKey', true] }) + asKey({ ...jwk, key_ops: ['wrapKey', true] }) }, { instanceOf: TypeError, message: '`key_ops` must be a non-empty array of strings when provided' }) }) -test('JWK importKey with invalid use / key_ops throws', t => { +test('JWK asKey with invalid use / key_ops throws', t => { t.throws(() => { - importKey({ ...jwk, use: 'sig', key_ops: ['wrapKey'] }) + asKey({ ...jwk, use: 'sig', key_ops: ['wrapKey'] }) }, { instanceOf: errors.JWKInvalid, code: 'ERR_JWK_INVALID' }) }) -test('keyObject importKey with invalid use / key_ops throws 1/2', t => { +test('keyObject asKey with invalid use / key_ops throws 1/2', t => { const { publicKey } = crypto.generateKeyPairSync('ed25519') t.throws(() => { - importKey(publicKey, { use: 'sig', key_ops: ['wrapKey'] }) + asKey(publicKey, { use: 'sig', key_ops: ['wrapKey'] }) }, { instanceOf: errors.JWKInvalid, code: 'ERR_JWK_INVALID' }) }) -test('keyObject importKey with invalid use / key_ops throws 2/2', t => { +test('keyObject asKey with invalid use / key_ops throws 2/2', t => { const { publicKey } = crypto.generateKeyPairSync('ed25519') t.throws(() => { - importKey(publicKey, { use: 'enc', key_ops: ['sign'] }) + asKey(publicKey, { use: 'enc', key_ops: ['sign'] }) }, { instanceOf: errors.JWKInvalid, code: 'ERR_JWK_INVALID' }) }) -test('PEM importKey with invalid use / key_ops throws', t => { +test('PEM asKey with invalid use / key_ops throws', t => { const { publicKey } = crypto.generateKeyPairSync('ed25519') t.throws(() => { - importKey(publicKey.export({ type: 'spki', format: 'pem' }), { use: 'sig', key_ops: ['wrapKey'] }) + asKey(publicKey.export({ type: 'spki', format: 'pem' }), { use: 'sig', key_ops: ['wrapKey'] }) }, { instanceOf: errors.JWKInvalid, code: 'ERR_JWK_INVALID' }) }) diff --git a/test/jwk/oct.test.js b/test/jwk/oct.test.js index 01244fbe0e..430849349f 100644 --- a/test/jwk/oct.test.js +++ b/test/jwk/oct.test.js @@ -4,7 +4,7 @@ const { hasProperty, hasNoProperties } = require('../macros') const errors = require('../../lib/errors') const OctKey = require('../../lib/jwk/key/oct') -const { JWK: { importKey } } = require('../..') +const { JWK: { asKey } } = require('../..') const { generateSync } = require('../../lib/jwk/generate') const keyObject = createSecretKey(Buffer.from('secret')) @@ -113,7 +113,7 @@ test('oct keys may not be generated as public', t => { }) test('they may be imported from', t => { - const key = importKey({ + const key = asKey({ kty: 'oct', kid: '4p9o4_DcKoT6Qg2BI_mSgMP_MsXwFqogKuI26CunKAM' }) @@ -126,7 +126,7 @@ test('they may be imported from', t => { }) test('they may be imported from (no kid)', t => { - const key = importKey({ + const key = asKey({ kty: 'oct' }) @@ -141,7 +141,7 @@ test('they may be imported from (no kid)', t => { test('they may be imported so long as there was no k', t => { t.throws(() => { - importKey({ + asKey({ kty: 'oct', kid: '4p9o4_DcKoT6Qg2BI_mSgMP_MsXwFqogKuI26CunKAM', k: undefined diff --git a/test/jwk/x5c_thumbprints.test.js b/test/jwk/x5c_thumbprints.test.js index 0abc6fe4a8..dd58bb0371 100644 --- a/test/jwk/x5c_thumbprints.test.js +++ b/test/jwk/x5c_thumbprints.test.js @@ -2,7 +2,7 @@ const test = require('ava') const errors = require('../../lib/errors') -const { JWK: { importKey } } = require('../..') +const { JWK: { asKey } } = require('../..') const jwk = { kty: 'RSA', @@ -17,7 +17,7 @@ const jwk = { test('x5c can be imported and have their X.509 cert thumbprints calculated', t => { let key - t.notThrows(() => { key = importKey(jwk) }) + t.notThrows(() => { key = asKey(jwk) }) t.deepEqual(key.x5c, jwk.x5c) const asJWK = key.toJWK() t.deepEqual(asJWK.x5c, jwk.x5c) @@ -30,13 +30,13 @@ test('x5c can be imported and have their X.509 cert thumbprints calculated', t = test('checks that x5c is an array of valid PKIX certificates', t => { ;[[], {}, false, 1].forEach((value) => { t.throws(() => { - importKey({ + asKey({ ...jwk, x5c: value }) }, { instanceOf: TypeError, message: '`x5c` must be an array of one or more PKIX certificates when provided' }) t.throws(() => { - importKey({ + asKey({ ...jwk, x5c: [value] }) @@ -46,7 +46,7 @@ test('checks that x5c is an array of valid PKIX certificates', t => { test('checks that first x5c member must represent the key', t => { t.throws(() => { - importKey({ + asKey({ ...jwk, x5c: [ 'MIIC/zCCAeegAwIBAgIJYdZUZz2rikftMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMTEnBhbnZhLmV1LmF1dGgwLmNvbTAeFw0xNzEwMTgxNTExMjBaFw0zMTA2MjcxNTExMjBaMB0xGzAZBgNVBAMTEnBhbnZhLmV1LmF1dGgwLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKROvB+A+ZlFV1AXl75tVegjaCuBE7CiXHNstVZ/F6fKl6OvIRhAW3YKnJEglzVvHw0q46Nw48yBdbbKjdwGo1jbrI15D2+MYPy8xlMfDzEqNWBjOsgnA1nhFFDXD7wITwFRMtlRKVvKMa19QCmMFrpQ2qcloMne/DzSvxlEnVA6DG1SYqHR/gdK5hoRATJkwHXQ5F/nUxD3BOAyyjsU5RsGJAeVVS4Yf532xmziIbda3iV4LMUiHUb1v8Oy2sDncYF+imq/sbHGgE7dyv5R5AsYHGANgvIPMHJ1QTFSQVU0lxPy+EWnLk9abVOZYzD6O5YRdJ29UWVtQ1q5UcyrF18CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSUcoPFHi/vm7dw1rt/IRmxvLMyowDgYDVR0PAQH/BAQDAgKEMA0GCSqGSIb3DQEBCwUAA4IBAQBcBXXBcbqliVOHkTgxocSYNUajcgIKjgeqG9RKFkbHfPuK/Hn80vQhd6mBKJTIyM7fY7DPh1/PjRsAyDQEwouHWItcM6iJBSdAkPq2DPfCkpUOi7MHrhXSouU1X4IOBvAl94k9Z8oj5k12KWVH8jZn5G03lwkWUgSfkLJ0Dh86+4sF2W4Dz2qZUXZuQbUL5eJcWRpfEZowff+T8xsiRjcIEpgfLz4nWonijtvEWESEa3bYpI9pI5OXLImgVJLGxVaUktsGIexQ6eM1AoxBYE7E+nbN/rwo30XWGbTkYecisySSYuzVn2c0xnC/8ZvW+gJ4SkzRDjlOAbm3R0r5j7b1' @@ -54,7 +54,7 @@ test('checks that first x5c member must represent the key', t => { }) }, { instanceOf: errors.JWKInvalid, code: 'ERR_JWK_INVALID', message: 'The key in the first `x5c` certificate MUST match the public key represented by the JWK' }) t.throws(() => { - importKey({ + asKey({ ...jwk, x5c: [ jwk.x5c[0], diff --git a/test/jwks/keystore.test.js b/test/jwks/keystore.test.js index 88cd22c480..c16e8e1eed 100644 --- a/test/jwks/keystore.test.js +++ b/test/jwks/keystore.test.js @@ -1,7 +1,8 @@ const test = require('ava') -const KeyStore = require('../../lib/jwks/keystore') -const { importKey, generateSync } = require('../../lib/jwk') +const { KeyStore, asKeyStore } = require('../../lib/jwks') +const { asKey, generateSync } = require('../../lib/jwk') +const errors = require('../../lib/errors') const withX5C = { kty: 'RSA', @@ -30,10 +31,10 @@ test('constructor', t => { }) }) -test('constructor only accepts Key instances created through JWK.importKey', t => { +test('constructor only accepts Key instances created through JWK.asKey', t => { t.throws(() => { new KeyStore({}) // eslint-disable-line no-new - }, { instanceOf: TypeError, message: 'all keys must be an instances of a key instantiated by JWK.importKey' }) + }, { instanceOf: TypeError, message: 'all keys must be an instances of a key instantiated by JWK.asKey' }) }) test('.generate()', async t => { @@ -56,7 +57,7 @@ test('.add()', t => { t.is(ks.size, 1) t.throws(() => { ks.add({}) - }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.importKey' }) + }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.asKey' }) }) test('.remove()', t => { @@ -67,7 +68,7 @@ test('.remove()', t => { ks.remove(k) t.throws(() => { ks.remove({}) - }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.importKey' }) + }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.asKey' }) }) test('.all() key_ops must be an array', t => { @@ -158,25 +159,25 @@ test('.all() and .get() kid filter', t => { }) test('.all() and .get() x5t filter and sort', t => { - const k = importKey(withX5C) + const k = asKey(withX5C) const ks = new KeyStore(k) t.deepEqual(ks.all({ x5t: 'baz' }), []) t.deepEqual(ks.all({ x5t: '4pNenEBLv0JpLIdugWxQkOsZcK0' }), [k]) t.is(ks.get({ x5t: 'baz' }), undefined) t.is(ks.get({ x5t: '4pNenEBLv0JpLIdugWxQkOsZcK0' }), k) - const k2 = importKey({ ...withX5C, alg: 'RS256' }) + const k2 = asKey({ ...withX5C, alg: 'RS256' }) ks.add(k2) t.is(ks.get({ x5t: '4pNenEBLv0JpLIdugWxQkOsZcK0', alg: 'RS256' }), k2) }) test('.all() and .get() x5t#S256 filter and sort', t => { - const k = importKey(withX5C) + const k = asKey(withX5C) const ks = new KeyStore(k) t.deepEqual(ks.all({ 'x5t#S256': 'baz' }), []) t.deepEqual(ks.all({ 'x5t#S256': 'pJm2BBpkB8y7tCqrWM0X37WOmQTO8zQw-VpxVgBb21I' }), [k]) t.is(ks.get({ 'x5t#S256': 'baz' }), undefined) t.is(ks.get({ 'x5t#S256': 'pJm2BBpkB8y7tCqrWM0X37WOmQTO8zQw-VpxVgBb21I' }), k) - const k2 = importKey({ ...withX5C, alg: 'RS256' }) + const k2 = asKey({ ...withX5C, alg: 'RS256' }) ks.add(k2) t.is(ks.get({ 'x5t#S256': 'pJm2BBpkB8y7tCqrWM0X37WOmQTO8zQw-VpxVgBb21I', alg: 'RS256' }), k2) }) @@ -210,25 +211,26 @@ test('.all() and .get() alg sort', t => { t.is(ks.get({ alg: 'RS256' }), k2) }) -test('.fromJWKS()', t => { +test('.asKeyStore()', t => { const ks = new KeyStore() ks.generateSync('EC') ks.generateSync('RSA') - const ks2 = KeyStore.fromJWKS(ks.toJWKS()) + const ks2 = asKeyStore(ks.toJWKS()) + t.true(ks2 instanceof KeyStore) t.is(ks2.size, 2) }) -test('.fromJWKS() input validation', t => { +test('.asKeyStore() input validation', t => { [Buffer, 1, false, '', 'foo', {}, { foo: 'bar' }].forEach((val) => { t.throws(() => { - KeyStore.fromJWKS(val) + asKeyStore(val) }, { instanceOf: TypeError, message: 'jwks must be a JSON Web Key Set formatted object' }) t.throws(() => { - KeyStore.fromJWKS({ keys: val }) + asKeyStore({ keys: val }) }, { instanceOf: TypeError, message: 'jwks must be a JSON Web Key Set formatted object' }) t.throws(() => { - KeyStore.fromJWKS({ keys: [val] }) + asKeyStore({ keys: [val] }) }, { instanceOf: TypeError, message: 'jwks must be a JSON Web Key Set formatted object' }) }) }) @@ -242,3 +244,19 @@ test('keystore instance is an iterator', t => { } t.pass() }) + +test('minimal RSA test', async t => { + const key = generateSync('RSA') + const { d, e, n } = key.toJWK(true) + asKeyStore({ keys: [{ kty: 'RSA', d, e, n }] }, { calculateMissingRSAPrimes: true }) + KeyStore.fromJWKS({ keys: [{ kty: 'RSA', d, e, n }] }) // deprecated + t.throws(() => { + asKeyStore({ keys: [{ kty: 'RSA', d: d.substr(1), e, n }] }, { calculateMissingRSAPrimes: true }) + }, { instanceOf: errors.JWKImportFailed, code: 'ERR_JWK_IMPORT_FAILED', message: 'failed to calculate missing primes' }) + t.throws(() => { + asKeyStore({ keys: [{ kty: 'RSA', d, e, n }] }) + }, { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'importing private RSA keys without all other private key parameters is not enabled, see documentation and its advisory on how and when its ok to enable it' }) + t.throws(() => { + asKeyStore({ keys: [{ kty: 'RSA', d: `${d}F`, e, n }] }, { calculateMissingRSAPrimes: true }) + }, { instanceOf: errors.JWKInvalid, code: 'ERR_JWK_INVALID', message: 'invalid RSA private exponent' }) +}) diff --git a/test/jws/b64.test.js b/test/jws/b64.test.js index 10a1c4375c..d66d024f7b 100644 --- a/test/jws/b64.test.js +++ b/test/jws/b64.test.js @@ -2,7 +2,7 @@ const test = require('ava') const { JWK, JWS, errors } = require('../..') -const k = JWK.importKey({ +const k = JWK.asKey({ kty: 'oct', k: 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow' }) diff --git a/test/jws/sanity.test.js b/test/jws/sanity.test.js index fd74c66631..cdb1e8dc23 100644 --- a/test/jws/sanity.test.js +++ b/test/jws/sanity.test.js @@ -38,7 +38,7 @@ test('verify key or store argument', t => { ;[{}, new Object(), false, null, Infinity, 0, Buffer.from('foo')].forEach((val) => { // eslint-disable-line no-new-object t.throws(() => { JWS.verify('..', val) - }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.importKey or a JWKS.KeyStore' }) + }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.asKey or a JWKS.KeyStore' }) }) }) @@ -73,7 +73,7 @@ test('JWS sign rejects non keys', t => { ;[[], false, true, undefined, null, Infinity, 0].forEach((val) => { t.throws(() => { JWS.sign('foo', val) - }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.importKey' }) + }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.asKey' }) }) }) diff --git a/test/jws/smoke.test.js b/test/jws/smoke.test.js index c3d2d28280..17e014f5c0 100644 --- a/test/jws/smoke.test.js +++ b/test/jws/smoke.test.js @@ -1,7 +1,7 @@ const test = require('ava') const { sign, verify } = require('../../lib/jws') -const { JWK: { importKey, generateSync }, errors } = require('../..') +const { JWK: { asKey, generateSync }, errors } = require('../..') const PAYLOAD = {} @@ -61,8 +61,8 @@ const failure = (t, sKey, vKey, alg) => { } Object.entries(fixtures.PEM).forEach(([type, { private: key, public: pub }]) => { - const sKey = importKey(key) - const vKey = importKey(pub) + const sKey = asKey(key) + const vKey = asKey(pub) sKey.algorithms('sign').forEach((alg) => { test(`key ${type} > alg ${alg}`, success, sKey, vKey, alg) @@ -78,8 +78,8 @@ sym.algorithms('sign').forEach((alg) => { { const rsa = generateSync('RSA') - const sKey = importKey({ kty: 'RSA', e: rsa.e, n: rsa.n, d: rsa.d }) - const vKey = importKey({ kty: 'RSA', e: rsa.e, n: rsa.n }) + const sKey = asKey({ kty: 'RSA', e: rsa.e, n: rsa.n, d: rsa.d }, { calculateMissingRSAPrimes: true }) + const vKey = asKey({ kty: 'RSA', e: rsa.e, n: rsa.n }) sKey.algorithms('sign').forEach((alg) => { test(`key RSA (min) > alg ${alg}`, success, sKey, vKey, alg) test(`key RSA (min) > alg ${alg} (negative cases)`, failure, sKey, vKey, alg)