From e7ad82cbbcbd95b1971be72aff6f64beabf5649b Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Sat, 27 Jul 2019 19:01:20 +0200 Subject: [PATCH] feat: electron v6.x support --- .travis.yml | 16 +-- README.md | 12 +++ lib/help/consts.js | 9 ++ lib/jwk/key/oct.js | 7 +- lib/jwk/key/rsa.js | 4 +- package.json | 1 + ...ing_additional_authentication_data.test.js | 2 + ....protecting_specific_header_fields.test.js | 2 + .../5_12.protecting_content_only.test.js | 2 + ....encrypting_to_multiple_recipients.test.js | 2 + ...aes-keywrap_with-aes-cbc-hmac-sha2.test.js | 2 + ...dh-es_and_aes-keywrap_with_aes-gcm.test.js | 2 + ...rap_using_aes-keywrap_with_aes-gcm.test.js | 2 + test/cookbook/5_9.compressed_content.test.js | 2 + test/electron/electron.test.js | 25 +++++ test/electron/index.js | 2 + test/help/P-256K.key_utils.test.js | 3 + test/help/key_utils.test.js | 102 +++++++++--------- test/jwe/sanity.test.js | 90 ++++++++++------ test/jwe/smoke.P-256K.test.js | 2 + test/jwe/smoke.test.js | 1 + test/jwk/P-256K.import.test.js | 3 + test/jwk/ec.test.js | 38 ++++--- test/jwk/generate.test.js | 4 + test/jwk/import.test.js | 23 ++-- test/jwk/oct.test.js | 37 +++++-- test/jwk/okp_enc.test.js | 3 + test/jwk/okp_sig.test.js | 1 + test/jwk/rsa.test.js | 82 +++++++++----- test/jws/smoke.P-256K.test.js | 2 + test/jws/smoke.test.js | 1 + 31 files changed, 331 insertions(+), 153 deletions(-) create mode 100644 test/electron/electron.test.js create mode 100644 test/electron/index.js diff --git a/.travis.yml b/.travis.yml index 2b3ac24c64..2f81803c02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,8 @@ matrix: allow_failures: - - name: "Test Suite - Nightly build" + - name: "Test Suite - Node.js Nightly build" - name: "Linter" include: - - name: Linter - language: node_js - node_js: stable - script: npm run lint - name: "Test Suite + coverage - 12.0.0" #min language: node_js node_js: 12.0.0 @@ -22,10 +18,18 @@ matrix: os: windows script: npm test node_js: node - - name: "Test Suite - Nightly build" + - name: "Test Suite - Electron build" + language: node_js + node_js: stable + script: xvfb-run npx electron@beta ./test/electron + - name: "Test Suite - Node.js Nightly build" language: minimal env: NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly/ install: - nvm i node - npm i script: npm test + - name: Linter + language: node_js + node_js: stable + script: npm run lint diff --git a/README.md b/README.md index 82aee28fa3..dcecb0ba27 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,18 @@ key = jose.JWK.asKey(fs.readFileSync('path/to/key/file')) key.crv === 'P-256K' ``` +#### Electron Support + +Electron v6.x runtime is supported to the extent of the crypto engine BoringSSL feature parity with +standard Node.js OpenSSL. The following is disabled in Electron runtime because of its lack of +[support](https://github.com/panva/jose/blob/master/test/electron/electron.test.js). + +- JWE `A128KW`, `A192KW` and `A256KW` algs are not available, this also means that other JWAs + depending on those are not working, those are `ECDH-ES+A128KW`, `ECDH-ES+A192KW`, + `ECDH-ES+A256KW`, `PBES2-HS256+A128KW`, `PBES2-HS384+A192KW`, `PBES2-HS512+A256KW`) +- OKP curves `Ed448`, `X25519` and `X448` are not supported +- EC curve `secp256k1` is not supported + ## FAQ #### Semver? diff --git a/lib/help/consts.js b/lib/help/consts.js index 6a3b0f3daa..68f0891840 100644 --- a/lib/help/consts.js +++ b/lib/help/consts.js @@ -31,3 +31,12 @@ module.exports.KEYLENGTHS = { A192GCM: 192, A256GCM: 256 } + +/* c8 ignore next 7 */ +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/jwk/key/oct.js b/lib/jwk/key/oct.js index b2aeb8269f..3eaa54762d 100644 --- a/lib/jwk/key/oct.js +++ b/lib/jwk/key/oct.js @@ -91,7 +91,7 @@ class OctKey extends Key { switch (operation) { case 'deriveKey': - if (use === 'sig') { + if (use === 'sig' || 'electron' in process.versions) { return new Set() } @@ -116,6 +116,11 @@ class OctKey extends Key { return new Set() } + /* c8 ignore next 3 */ + 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([ diff --git a/lib/jwk/key/rsa.js b/lib/jwk/key/rsa.js index 9d161f7e73..06b16aaa9d 100644 --- a/lib/jwk/key/rsa.js +++ b/lib/jwk/key/rsa.js @@ -139,7 +139,7 @@ class RSAKey extends Key { } static async generate (len = 2048, privat = true) { - if (!Number.isSafeInteger(len) || len < 512 || len % 8 !== 0) { + if (!Number.isSafeInteger(len) || len < 512 || len % 8 !== 0 || (('electron' in process.versions) && len % 128 !== 0)) { throw new TypeError('invalid bit length') } @@ -149,7 +149,7 @@ class RSAKey extends Key { } static generateSync (len = 2048, privat = true) { - if (!Number.isSafeInteger(len) || len < 512 || len % 8 !== 0) { + if (!Number.isSafeInteger(len) || len < 512 || len % 8 !== 0 || (('electron' in process.versions) && len % 128 !== 0)) { throw new TypeError('invalid bit length') } diff --git a/package.json b/package.json index c6c7a4caeb..b824dd7b0e 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "decode", "decrypt", "eddsa", + "electron", "encrypt", "flattened", "general", 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 f566492052..2373b619af 100644 --- a/test/cookbook/5_10.including_additional_authentication_data.test.js +++ b/test/cookbook/5_10.including_additional_authentication_data.test.js @@ -1,5 +1,7 @@ const test = require('ava') +if ('electron' in process.versions) return + const recipe = require('./recipes').get('5.10') const { enc: verifiers } = require('./verifiers') 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 32ddb81e0d..6cbdee96c5 100644 --- a/test/cookbook/5_11.protecting_specific_header_fields.test.js +++ b/test/cookbook/5_11.protecting_specific_header_fields.test.js @@ -1,5 +1,7 @@ const test = require('ava') +if ('electron' in process.versions) return + const recipe = require('./recipes').get('5.11') const { enc: verifiers } = require('./verifiers') diff --git a/test/cookbook/5_12.protecting_content_only.test.js b/test/cookbook/5_12.protecting_content_only.test.js index 95410df8f1..4d067d1c7b 100644 --- a/test/cookbook/5_12.protecting_content_only.test.js +++ b/test/cookbook/5_12.protecting_content_only.test.js @@ -1,5 +1,7 @@ const test = require('ava') +if ('electron' in process.versions) return + const recipe = require('./recipes').get('5.12') const { enc: verifiers } = require('./verifiers') 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 72bb225a8b..e90f38005e 100644 --- a/test/cookbook/5_13.encrypting_to_multiple_recipients.test.js +++ b/test/cookbook/5_13.encrypting_to_multiple_recipients.test.js @@ -1,5 +1,7 @@ const test = require('ava') +if ('electron' in process.versions) return + const recipe = require('./recipes').get('5.13') const { enc: verifiers } = require('./verifiers') 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 e5603fc20d..3bfa31eed1 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 @@ -1,5 +1,7 @@ const test = require('ava') +if ('electron' in process.versions) return + const recipe = require('./recipes').get('5.3') const { enc: verifiers } = require('./verifiers') 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 3c929c8c2d..6454b9448f 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 @@ -1,5 +1,7 @@ const test = require('ava') +if ('electron' in process.versions) return + const recipe = require('./recipes').get('5.4') const { enc: verifiers } = require('./verifiers') 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 dfb5ff50ad..e0b36ffd55 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 @@ -1,5 +1,7 @@ const test = require('ava') +if ('electron' in process.versions) return + const recipe = require('./recipes').get('5.8') const { enc: verifiers } = require('./verifiers') diff --git a/test/cookbook/5_9.compressed_content.test.js b/test/cookbook/5_9.compressed_content.test.js index ed685ad97d..6bcaaa5746 100644 --- a/test/cookbook/5_9.compressed_content.test.js +++ b/test/cookbook/5_9.compressed_content.test.js @@ -1,5 +1,7 @@ const test = require('ava') +if ('electron' in process.versions) return + const recipe = require('./recipes').get('5.9') const { enc: verifiers } = require('./verifiers') diff --git a/test/electron/electron.test.js b/test/electron/electron.test.js new file mode 100644 index 0000000000..9e7cb23055 --- /dev/null +++ b/test/electron/electron.test.js @@ -0,0 +1,25 @@ +const test = require('ava') + +if (!('electron' in process.versions)) return + +const crypto = require('crypto') + +const fixtures = require('../fixtures') + +;['aes128', 'aes192', 'aes256'].forEach((cipher) => { + test(`${cipher} is not supported`, t => { + t.false(crypto.getCiphers().includes(cipher)) + }) +}) + +test('secp256k1 is not supported', t => { + t.false(crypto.getCurves().includes('secp256k1')) +}) + +;['Ed448', 'X25519', 'X448'].forEach((okp) => { + test(`${okp} is not supported`, t => { + t.throws(() => { + crypto.createPrivateKey(fixtures.PEM[okp].private) + }, { instanceOf: Error, code: 'ERR_OSSL_EVP_UNSUPPORTED_ALGORITHM' }) + }) +}) diff --git a/test/electron/index.js b/test/electron/index.js new file mode 100644 index 0000000000..b1bcfa4bf5 --- /dev/null +++ b/test/electron/index.js @@ -0,0 +1,2 @@ +const exit = () => process.exit(process.exitCode) +require('ava/lib/cli').run().then(exit, exit) diff --git a/test/help/P-256K.key_utils.test.js b/test/help/P-256K.key_utils.test.js index 2b646df505..f6e5a27f8a 100644 --- a/test/help/P-256K.key_utils.test.js +++ b/test/help/P-256K.key_utils.test.js @@ -2,6 +2,9 @@ require('../../P-256K') const test = require('ava') + +if ('electron' in process.versions) return + const { createPublicKey, createPrivateKey } = require('crypto') const { keyObjectToJWK, jwkToPem } = require('../../lib/help/key_utils') diff --git a/test/help/key_utils.test.js b/test/help/key_utils.test.js index f8d020b1d7..f448b238c7 100644 --- a/test/help/key_utils.test.js +++ b/test/help/key_utils.test.js @@ -57,56 +57,58 @@ test('Ed25519 Private key', t => { t.deepEqual(actual, expected) }) -test('Ed448 Public key', t => { - const expected = clone(fixtures.Ed448) - delete expected.d - const pem = createPublicKey(jwkToPem(expected)) - const actual = keyObjectToJWK(pem) - - t.deepEqual(actual, expected) -}) - -test('Ed448 Private key', t => { - const expected = fixtures.Ed448 - const pem = createPrivateKey(jwkToPem(expected)) - const actual = keyObjectToJWK(pem) - - t.deepEqual(actual, expected) -}) - -test('X25519 Public key', t => { - const expected = clone(fixtures.X25519) - delete expected.d - const pem = createPublicKey(jwkToPem(expected)) - const actual = keyObjectToJWK(pem) - - t.deepEqual(actual, expected) -}) - -test('X25519 Private key', t => { - const expected = fixtures.X25519 - const pem = createPrivateKey(jwkToPem(expected)) - const actual = keyObjectToJWK(pem) - - t.deepEqual(actual, expected) -}) - -test('X448 Public key', t => { - const expected = clone(fixtures.X448) - delete expected.d - const pem = createPublicKey(jwkToPem(expected)) - const actual = keyObjectToJWK(pem) - - t.deepEqual(actual, expected) -}) - -test('X448 Private key', t => { - const expected = fixtures.X448 - const pem = createPrivateKey(jwkToPem(expected)) - const actual = keyObjectToJWK(pem) - - t.deepEqual(actual, expected) -}) +if (!('electron' in process.versions)) { + test('Ed448 Public key', t => { + const expected = clone(fixtures.Ed448) + delete expected.d + const pem = createPublicKey(jwkToPem(expected)) + const actual = keyObjectToJWK(pem) + + t.deepEqual(actual, expected) + }) + + test('Ed448 Private key', t => { + const expected = fixtures.Ed448 + const pem = createPrivateKey(jwkToPem(expected)) + const actual = keyObjectToJWK(pem) + + t.deepEqual(actual, expected) + }) + + test('X25519 Public key', t => { + const expected = clone(fixtures.X25519) + delete expected.d + const pem = createPublicKey(jwkToPem(expected)) + const actual = keyObjectToJWK(pem) + + t.deepEqual(actual, expected) + }) + + test('X25519 Private key', t => { + const expected = fixtures.X25519 + const pem = createPrivateKey(jwkToPem(expected)) + const actual = keyObjectToJWK(pem) + + t.deepEqual(actual, expected) + }) + + test('X448 Public key', t => { + const expected = clone(fixtures.X448) + delete expected.d + const pem = createPublicKey(jwkToPem(expected)) + const actual = keyObjectToJWK(pem) + + t.deepEqual(actual, expected) + }) + + test('X448 Private key', t => { + const expected = fixtures.X448 + const pem = createPrivateKey(jwkToPem(expected)) + const actual = keyObjectToJWK(pem) + + t.deepEqual(actual, expected) + }) +} test('EC P-256 Public key', t => { const expected = clone(fixtures['P-256']) diff --git a/test/jwe/sanity.test.js b/test/jwe/sanity.test.js index d001868445..d504633454 100644 --- a/test/jwe/sanity.test.js +++ b/test/jwe/sanity.test.js @@ -52,34 +52,53 @@ test('JWE no alg specified but cannot resolve', t => { test('JWE no alg/enc specified (multi recipient)', t => { const encrypt = new JWE.Encrypt('foo') encrypt.recipient(generateSync('RSA')) - encrypt.recipient(generateSync('EC')) encrypt.recipient(generateSync('oct', 256)) + if (!('electron' in process.versions)) { + encrypt.recipient(generateSync('EC')) + } const jwe = encrypt.encrypt('general') t.is(jwe.unprotected, undefined) t.deepEqual(base64url.JSON.decode(jwe.protected), { enc: 'A128CBC-HS256' }) t.deepEqual(jwe.recipients[0].header, { alg: 'RSA-OAEP' }) - const { epk, ...rest } = jwe.recipients[1].header - t.deepEqual(rest, { alg: 'ECDH-ES+A128KW' }) - t.deepEqual(jwe.recipients[2].header, { alg: 'A256KW' }) + if (!('electron' in process.versions)) { + t.deepEqual(jwe.recipients[1].header, { alg: 'A256KW' }) + const { epk, ...rest } = jwe.recipients[2].header + t.deepEqual(rest, { alg: 'ECDH-ES+A128KW' }) + } else { + const { alg, ...rest } = jwe.recipients[1].header + t.is(alg, 'A256GCMKW') + t.true('iv' in rest) + t.true('tag' in rest) + } }) test('JWE no alg/enc specified (multi recipient) with per-recipient headers', t => { const encrypt = new JWE.Encrypt('foo') const k1 = generateSync('RSA', undefined, { kid: 'kid_1' }) encrypt.recipient(k1, { kid: k1.kid }) - const k2 = generateSync('EC', undefined, { kid: 'kid_2' }) + const k2 = generateSync('oct', 256, { kid: 'kid_3' }) encrypt.recipient(k2, { kid: k2.kid }) - const k3 = generateSync('oct', 256, { kid: 'kid_3' }) - encrypt.recipient(k3, { kid: k3.kid }) + if (!('electron' in process.versions)) { + const k3 = generateSync('EC', undefined, { kid: 'kid_2' }) + encrypt.recipient(k3, { kid: k3.kid }) + } const jwe = encrypt.encrypt('general') t.is(jwe.unprotected, undefined) t.deepEqual(base64url.JSON.decode(jwe.protected), { enc: 'A128CBC-HS256' }) t.deepEqual(jwe.recipients[0].header, { alg: 'RSA-OAEP', kid: 'kid_1' }) - const { epk, ...rest } = jwe.recipients[1].header - t.deepEqual(rest, { alg: 'ECDH-ES+A128KW', kid: 'kid_2' }) - t.deepEqual(jwe.recipients[2].header, { alg: 'A256KW', kid: 'kid_3' }) + if (!('electron' in process.versions)) { + t.deepEqual(jwe.recipients[1].header, { alg: 'A256KW', kid: 'kid_3' }) + const { epk, ...rest } = jwe.recipients[2].header + t.deepEqual(rest, { alg: 'ECDH-ES+A128KW', kid: 'kid_2' }) + } else { + const { alg, kid, ...rest } = jwe.recipients[1].header + t.is(alg, 'A256GCMKW') + t.is(kid, 'kid_3') + t.true('iv' in rest) + t.true('tag' in rest) + } }) test('JWE no alg/enc specified (single rsa), no protected header', t => { @@ -132,7 +151,14 @@ test('JWE no alg/enc specified (single oct)', t => { const jwe = encrypt.encrypt('flattened') t.is(jwe.unprotected, undefined) t.is(jwe.header, undefined) - t.deepEqual(base64url.JSON.decode(jwe.protected), { alg: 'A128KW', enc: 'A128CBC-HS256' }) + if (!('electron' in process.versions)) { + t.deepEqual(base64url.JSON.decode(jwe.protected), { alg: 'A128KW', enc: 'A128CBC-HS256' }) + } else { + const { iv, tag, ...rest } = base64url.JSON.decode(jwe.protected) + t.deepEqual(rest, { alg: 'A128GCMKW', enc: 'A128CBC-HS256' }) + t.truthy(iv) + t.truthy(tag) + } }) test('JWE no alg/enc specified (single ec)', t => { @@ -423,28 +449,30 @@ test('JWE prot, unprot and per-recipient headers must be disjoint', t => { }, { instanceOf: errors.JWEInvalid, code: 'ERR_JWE_INVALID', message: 'JWE Shared Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint' }) }) -test('JWE decrypt algorithms whitelist', t => { - const k = generateSync('oct') - const jwe = JWE.encrypt('foo', k, { alg: 'PBES2-HS256+A128KW' }) - JWE.decrypt(jwe, k, { algorithms: ['PBES2-HS256+A128KW', 'PBES2-HS384+A192KW'] }) +if (!('electron' in process.versions)) { + test('JWE decrypt algorithms whitelist', t => { + const k = generateSync('oct') + const jwe = JWE.encrypt('foo', k, { alg: 'PBES2-HS256+A128KW' }) + JWE.decrypt(jwe, k, { algorithms: ['PBES2-HS256+A128KW', 'PBES2-HS384+A192KW'] }) - t.throws(() => { - JWE.decrypt(jwe, k, { algorithms: ['PBES2-HS384+A192KW'] }) - }, { instanceOf: errors.JOSEAlgNotWhitelisted, code: 'ERR_JOSE_ALG_NOT_WHITELISTED', message: 'alg not whitelisted' }) -}) + t.throws(() => { + JWE.decrypt(jwe, k, { algorithms: ['PBES2-HS384+A192KW'] }) + }, { instanceOf: errors.JOSEAlgNotWhitelisted, code: 'ERR_JOSE_ALG_NOT_WHITELISTED', message: 'alg not whitelisted' }) + }) -test('JWE decrypt algorithms whitelist with a keystore', t => { - const k = generateSync('oct') - const k2 = generateSync('oct') - const ks = new JWKS.KeyStore(k, k2) + test('JWE decrypt algorithms whitelist with a keystore', t => { + const k = generateSync('oct') + const k2 = generateSync('oct') + const ks = new JWKS.KeyStore(k, k2) - const jwe = JWE.encrypt('foo', k2, { alg: 'PBES2-HS256+A128KW' }) - JWE.decrypt(jwe, ks, { algorithms: ['PBES2-HS256+A128KW', 'PBES2-HS384+A192KW'] }) + const jwe = JWE.encrypt('foo', k2, { alg: 'PBES2-HS256+A128KW' }) + JWE.decrypt(jwe, ks, { algorithms: ['PBES2-HS256+A128KW', 'PBES2-HS384+A192KW'] }) - t.throws(() => { - JWE.decrypt(jwe, ks, { algorithms: ['PBES2-HS384+A192KW'] }) - }, { instanceOf: errors.JOSEAlgNotWhitelisted, code: 'ERR_JOSE_ALG_NOT_WHITELISTED' }) -}) + t.throws(() => { + JWE.decrypt(jwe, ks, { algorithms: ['PBES2-HS384+A192KW'] }) + }, { instanceOf: errors.JOSEAlgNotWhitelisted, code: 'ERR_JOSE_ALG_NOT_WHITELISTED' }) + }) +} test('JWE decrypt algorithms whitelist with direct encryption', t => { const k = generateSync('oct') @@ -465,7 +493,7 @@ test('JWE decrypt algorithms whitelist (multi-recipient)', t => { encrypt.recipient(k2) const jwe = encrypt.encrypt('general') - JWE.decrypt(jwe, k, { algorithms: ['A256KW'] }) + JWE.decrypt(jwe, k, { algorithms: ['electron' in process.versions ? 'A256GCMKW' : 'A256KW'] }) JWE.decrypt(jwe, k2, { algorithms: ['RSA-OAEP'] }) let err @@ -481,7 +509,7 @@ test('JWE decrypt algorithms whitelist (multi-recipient)', t => { }) err = t.throws(() => { - JWE.decrypt(jwe, k2, { algorithms: ['A256KW'] }) + JWE.decrypt(jwe, k2, { algorithms: ['electron' in process.versions ? 'A256GCMKW' : 'A256KW'] }) }, { instanceOf: errors.JOSEMultiError, code: 'ERR_JOSE_MULTIPLE_ERRORS' }) ;[...err].forEach((e, i) => { if (i === 0) { diff --git a/test/jwe/smoke.P-256K.test.js b/test/jwe/smoke.P-256K.test.js index b8ddf37ac3..c57d5073a8 100644 --- a/test/jwe/smoke.P-256K.test.js +++ b/test/jwe/smoke.P-256K.test.js @@ -3,6 +3,8 @@ require('../../P-256K') const test = require('ava') +if ('electron' in process.versions) return + const { JWK: { asKey } } = require('../..') const ENCS = [ diff --git a/test/jwe/smoke.test.js b/test/jwe/smoke.test.js index 69752a2941..10ac13320a 100644 --- a/test/jwe/smoke.test.js +++ b/test/jwe/smoke.test.js @@ -19,6 +19,7 @@ const { JWE: { success, failure } } = require('../macros') Object.entries(fixtures.PEM).forEach(([type, { private: key, public: pub }]) => { if (type === 'P-256K') return + if ('electron' in process.versions && (type.startsWith('X') || type === 'Ed448' || type === 'secp256k1')) return const eKey = asKey(pub) const dKey = asKey(key) diff --git a/test/jwk/P-256K.import.test.js b/test/jwk/P-256K.import.test.js index da68ce834f..0e85647d5f 100644 --- a/test/jwk/P-256K.import.test.js +++ b/test/jwk/P-256K.import.test.js @@ -2,6 +2,9 @@ require('../../P-256K') const test = require('ava') + +if ('electron' in process.versions) return + const { createPrivateKey, createPublicKey } = require('crypto') const { hasProperty, hasNoProperties, hasProperties } = require('../macros') const fixtures = require('../fixtures') diff --git a/test/jwk/ec.test.js b/test/jwk/ec.test.js index ef94e7f819..a0b9233071 100644 --- a/test/jwk/ec.test.js +++ b/test/jwk/ec.test.js @@ -11,17 +11,19 @@ test(`EC key .algorithms invalid operation`, t => { t.throws(() => key.algorithms('foo'), { instanceOf: TypeError, message: 'invalid key operation' }) }) -test('Unusable with unsupported curves', t => { - const kp = generateKeyPairSync('ec', { namedCurve: 'secp224k1' }) - t.throws( - () => new ECKey(kp.privateKey), - { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'unsupported EC key curve' } - ) - t.throws( - () => new ECKey(kp.publicKey), - { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'unsupported EC key curve' } - ) -}) +if (!('electron' in process.versions)) { + test('Unusable with unsupported curves', t => { + const kp = generateKeyPairSync('ec', { namedCurve: 'secp224k1' }) + t.throws( + () => new ECKey(kp.privateKey), + { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'unsupported EC key curve' } + ) + t.throws( + () => new ECKey(kp.publicKey), + { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'unsupported EC key curve' } + ) + }) +} Object.entries({ 'P-256': ['ES256', 'rDd6H6t9-nJUoz72nTpz8tInvypVWhE2iQoPznj8ZY8'], @@ -29,6 +31,12 @@ Object.entries({ 'P-384': ['ES384', '5gebayAhpztJCs4Pxo-z1hhsN0upoyG2NAoKpiiH2b0'], 'P-521': ['ES512', 'BQtkbSY3xgN4M2ZP3IHMLG7-Rp1L29teCMfNqgJHtTY'] }).forEach(([crv, [alg, kid]]) => { + const ECDH = ['ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW'] + if ('electron' in process.versions) { + if (crv === 'secp256k1') return + ECDH.splice(1, ECDH.length - 1) + } + if (crv === 'secp256k1' && 'electron' in process.versions) return // private ;(() => { const keyObject = createPrivateKey(fixtures.PEM[crv].private) @@ -52,7 +60,7 @@ Object.entries({ test(`${crv} EC Private key algorithms (no operation)`, t => { const result = key.algorithms() t.is(result.constructor, Set) - t.deepEqual([...result], [alg, 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW']) + t.deepEqual([...result], [alg, ...ECDH]) }) test(`${crv} EC Private key algorithms (no operation, w/ alg)`, t => { @@ -137,7 +145,7 @@ Object.entries({ test(`${crv} EC Private key .algorithms("deriveKey")`, t => { const result = key.algorithms('deriveKey') t.is(result.constructor, Set) - t.deepEqual([...result], ['ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW']) + t.deepEqual([...result], ECDH) }) test(`${crv} EC Private key .algorithms("wrapKey") when use is sig`, t => { @@ -184,7 +192,7 @@ Object.entries({ test(`${crv} EC Public key algorithms (no operation)`, t => { const result = key.algorithms() t.is(result.constructor, Set) - t.deepEqual([...result], [alg, 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW']) + t.deepEqual([...result], [alg, ...ECDH]) }) test(`${crv} EC Public key algorithms (no operation, w/ alg)`, t => { @@ -269,7 +277,7 @@ Object.entries({ test(`${crv} EC Public key .algorithms("deriveKey")`, t => { const result = key.algorithms('deriveKey') t.is(result.constructor, Set) - t.deepEqual([...result], ['ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW']) + t.deepEqual([...result], ECDH) }) test(`${crv} EC Public key .algorithms("wrapKey") when use is sig`, t => { diff --git a/test/jwk/generate.test.js b/test/jwk/generate.test.js index 8055295baa..2576886311 100644 --- a/test/jwk/generate.test.js +++ b/test/jwk/generate.test.js @@ -72,6 +72,10 @@ const { JWK: { generate, generateSync }, errors } = require('../..') ['oct', 192, { alg: 'HS256' }], ['oct', 192, { alg: 'A192GCM' }] ].forEach((args) => { + if ('electron' in process.versions) { + const [, crv] = args + if (crv === 'secp256k1' || String(crv).startsWith('X') || crv === 'Ed448') return + } test(`sync generates ${args[0]}(${args[1]}) with options ${JSON.stringify(args[2])}${typeof args[3] === 'boolean' ? ` and private=${args[3]}` : ''}`, t => { const key = generateSync(args[0], args[1], args[2], args[3]) t.truthy(key) diff --git a/test/jwk/import.test.js b/test/jwk/import.test.js index c84b56b7f5..f9381a6452 100644 --- a/test/jwk/import.test.js +++ b/test/jwk/import.test.js @@ -38,6 +38,7 @@ test('parameters must be a plain object', t => { Object.entries(fixtures.PEM).forEach(([type, { private: priv, public: pub }]) => { if (type === 'P-256K') return + if ('electron' in process.versions && (type.startsWith('X') || type === 'Ed448' || type === 'secp256k1')) return test(`fails to import ${type} as invalid string`, t => { t.throws(() => { @@ -76,17 +77,19 @@ test('failed to import throws an error', t => { }, { instanceOf: errors.JWKImportFailed, code: 'ERR_JWK_IMPORT_FAILED' }) }) -;[ - `-----BEGIN PUBLIC KEY-----\nMIIBtjCCASsGByqGSM44BAEwggEeAoGBANuHjLdqQcKozzWf9fUfe/mw4i5NLT8k\nCIA75k+GNYNbBaGZ2lGNeKsrjHzM8w7mE5k6qx5hDB4n88qFoauqCsUZ4knbTybn\nYV08gfWS375l/EGSpt3c/1dezVZuT/FmEeXbMhOIDORf/9f/6PpEMFN3eghszLvN\ng+L/19HVpWAXAhUAnOFG9vvOiZIz/ZxdpR+EVv8o4T8CgYBDk/ChY3fo4DrxzLZT\n7AjsAiJOzO8QnsV07Gh8gSzUCBsb+Hb4GvMs2U6rB5mxOMib3S2HGbs791uBva2a\nA6pzNzRmgV/w6CyOcxhCkZdVL7MwO9y5iq6V65R4GgfkCrIAYi/BW6XdXOyw/7J0\nt/4wB0/wKtsXf541NLfmUprJ+QOBhAACgYBGbXflbrGGg02+w8Xo6RO+tHoekREZ\nlJA0KKBN4jT0S3/OsLQeHtO7k/gkdMMbXD1J1fae9tIxy1SwYVTR6csgydGuvuyG\nB4A/ZtXEb+dumCBbtw8dyred4Okhl44Fdrs79F1rjSWEcwKqJghxS+GsbA0vcTaf\nAHDL6OblN04uzg==\n-----END PUBLIC KEY-----`, - crypto.generateKeyPairSync('dsa', { modulusLength: 1024 }).publicKey, - crypto.generateKeyPairSync('dsa', { modulusLength: 1024 }).privateKey -].forEach((unsupported, i) => { - test(`fails to import unsupported PEM ${i + 1}/4`, t => { - t.throws(() => { - asKey(unsupported) - }, { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'only RSA, EC and OKP asymmetric keys are supported' }) +if (!('electron' in process.versions)) { + ;[ + `-----BEGIN PUBLIC KEY-----\nMIIBtjCCASsGByqGSM44BAEwggEeAoGBANuHjLdqQcKozzWf9fUfe/mw4i5NLT8k\nCIA75k+GNYNbBaGZ2lGNeKsrjHzM8w7mE5k6qx5hDB4n88qFoauqCsUZ4knbTybn\nYV08gfWS375l/EGSpt3c/1dezVZuT/FmEeXbMhOIDORf/9f/6PpEMFN3eghszLvN\ng+L/19HVpWAXAhUAnOFG9vvOiZIz/ZxdpR+EVv8o4T8CgYBDk/ChY3fo4DrxzLZT\n7AjsAiJOzO8QnsV07Gh8gSzUCBsb+Hb4GvMs2U6rB5mxOMib3S2HGbs791uBva2a\nA6pzNzRmgV/w6CyOcxhCkZdVL7MwO9y5iq6V65R4GgfkCrIAYi/BW6XdXOyw/7J0\nt/4wB0/wKtsXf541NLfmUprJ+QOBhAACgYBGbXflbrGGg02+w8Xo6RO+tHoekREZ\nlJA0KKBN4jT0S3/OsLQeHtO7k/gkdMMbXD1J1fae9tIxy1SwYVTR6csgydGuvuyG\nB4A/ZtXEb+dumCBbtw8dyred4Okhl44Fdrs79F1rjSWEcwKqJghxS+GsbA0vcTaf\nAHDL6OblN04uzg==\n-----END PUBLIC KEY-----`, + crypto.generateKeyPairSync('dsa', { modulusLength: 1024 }).publicKey, + crypto.generateKeyPairSync('dsa', { modulusLength: 1024 }).privateKey + ].forEach((unsupported, i) => { + test(`fails to import unsupported PEM ${i + 1}/4`, t => { + t.throws(() => { + asKey(unsupported) + }, { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'only RSA, EC and OKP asymmetric keys are supported' }) + }) }) -}) +} test('minimal RSA test', async t => { const key = await generate('RSA') diff --git a/test/jwk/oct.test.js b/test/jwk/oct.test.js index 430849349f..6c105db024 100644 --- a/test/jwk/oct.test.js +++ b/test/jwk/oct.test.js @@ -83,12 +83,21 @@ test('no verify support when `use` is "enc"', t => { t.deepEqual([...result], []) }) -test(`oct keys (odd bits) deriveKey algorithms only have "PBES2"`, t => { - const key = generateSync('oct', 136) - const result = key.algorithms('deriveKey') - t.is(result.constructor, Set) - t.deepEqual([...result], ['PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW']) -}) +if (!('electron' in process.versions)) { + test(`oct keys (odd bits) deriveKey algorithms only have "PBES2"`, t => { + const key = generateSync('oct', 136) + const result = key.algorithms('deriveKey') + t.is(result.constructor, Set) + t.deepEqual([...result], ['PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW']) + }) +} else { + test(`oct keys (odd bits) deriveKey don't even have "PBES2"`, t => { + const key = generateSync('oct', 136) + const result = key.algorithms('deriveKey') + t.is(result.constructor, Set) + t.deepEqual([...result], []) + }) +} test(`oct keys (odd bits) wrap/unwrap algorithms cant wrap`, t => { const key = generateSync('oct', 136) @@ -98,12 +107,22 @@ test(`oct keys (odd bits) wrap/unwrap algorithms cant wrap`, t => { }) ;[128, 192, 256].forEach((len) => { - test(`oct keys (${len} bits) wrap/unwrap algorithms have "KW / GCMKW"`, t => { + test(`oct keys (${len} bits) wrap/unwrap algorithms have "GCMKW"`, t => { const key = generateSync('oct', len) - - t.true(key.algorithms().has(`A${len}KW`)) t.true(key.algorithms().has(`A${len}GCMKW`)) }) + + if (!('electron' in process.versions)) { + test(`oct keys (${len} bits) wrap/unwrap algorithms have "KW"`, t => { + const key = generateSync('oct', len) + t.true(key.algorithms().has(`A${len}KW`)) + }) + } else { + test(`oct keys (${len} bits) wrap/unwrap algorithms dont have "KW"`, t => { + const key = generateSync('oct', len) + t.false(key.algorithms().has(`A${len}KW`)) + }) + } }) test('oct keys may not be generated as public', t => { diff --git a/test/jwk/okp_enc.test.js b/test/jwk/okp_enc.test.js index e26656360d..bac3ddfc3c 100644 --- a/test/jwk/okp_enc.test.js +++ b/test/jwk/okp_enc.test.js @@ -1,4 +1,7 @@ const test = require('ava') + +if ('electron' in process.versions) return + const { createPrivateKey, createPublicKey } = require('crypto') const { hasProperty, hasNoProperties, hasProperties } = require('../macros') const fixtures = require('../fixtures') diff --git a/test/jwk/okp_sig.test.js b/test/jwk/okp_sig.test.js index 03c999bcf4..7133d441e0 100644 --- a/test/jwk/okp_sig.test.js +++ b/test/jwk/okp_sig.test.js @@ -14,6 +14,7 @@ Object.entries({ Ed25519: 'YeOxXoX_a0317nVDSwtlinj0RuJnSI0lYnxCM6qSC4c', Ed448: 'eaEfshTya3PWdLWK4CfotnZcHKNJbpQviiTOqwOyFfE' }).forEach(([crv, kid]) => { + if ('electron' in process.versions && crv === 'Ed448') return const alg = 'EdDSA' // private diff --git a/test/jwk/rsa.test.js b/test/jwk/rsa.test.js index 8fb32c5f2d..70c045cbc5 100644 --- a/test/jwk/rsa.test.js +++ b/test/jwk/rsa.test.js @@ -274,33 +274,57 @@ test(`RSA key .algorithms invalid operation`, t => { t.deepEqual([...result], ['RS256', 'RSA1_5']) }) - test('RSA key >= 528 bits can do PS256', t => { - const k = generateSync('RSA', 528) - t.true(k.algorithms().has('PS256')) - }) - - test('RSA key >= 592 bits can do RSA-OAEP', t => { - const k = generateSync('RSA', 592) - t.true(k.algorithms().has('RSA-OAEP')) - }) - - test('RSA key >= 624 bits can do RS384', t => { - const k = generateSync('RSA', 624) - t.true(k.algorithms().has('RS384')) - }) - - test('RSA key >= 752 bits can do RS512', t => { - const k = generateSync('RSA', 752) - t.true(k.algorithms().has('RS512')) - }) - - test('RSA key >= 784 bits can do PS384', t => { - const k = generateSync('RSA', 784) - t.true(k.algorithms().has('PS384')) - }) - - test('RSA key >= 1040 bits can do PS512', t => { - const k = generateSync('RSA', 1040) - t.true(k.algorithms().has('PS512')) - }) + if (!('electron' in process.versions)) { + test('RSA key >= 528 bits can do PS256', t => { + const k = generateSync('RSA', 528) + t.true(k.algorithms().has('PS256')) + }) + + test('RSA key >= 592 bits can do RSA-OAEP', t => { + const k = generateSync('RSA', 592) + t.true(k.algorithms().has('RSA-OAEP')) + }) + + test('RSA key >= 624 bits can do RS384', t => { + const k = generateSync('RSA', 624) + t.true(k.algorithms().has('RS384')) + }) + + test('RSA key >= 752 bits can do RS512', t => { + const k = generateSync('RSA', 752) + t.true(k.algorithms().has('RS512')) + }) + + test('RSA key >= 784 bits can do PS384', t => { + const k = generateSync('RSA', 784) + t.true(k.algorithms().has('PS384')) + }) + + test('RSA key >= 1040 bits can do PS512', t => { + const k = generateSync('RSA', 1040) + t.true(k.algorithms().has('PS512')) + }) + } else { + test('RSA key >= 640 bits can do RS384, RSA-OAEP and PS256', t => { + const k = generateSync('RSA', 640) + t.true(k.algorithms().has('RS384')) + t.true(k.algorithms().has('PS256')) + t.true(k.algorithms().has('RSA-OAEP')) + }) + + test('RSA key >= 768 bits can do RS512', t => { + const k = generateSync('RSA', 768) + t.true(k.algorithms().has('RS512')) + }) + + test('RSA key >= 896 bits can do PS384', t => { + const k = generateSync('RSA', 896) + t.true(k.algorithms().has('PS384')) + }) + + test('RSA key >= 1152 bits can do PS512', t => { + const k = generateSync('RSA', 1152) + t.true(k.algorithms().has('PS512')) + }) + } })() diff --git a/test/jws/smoke.P-256K.test.js b/test/jws/smoke.P-256K.test.js index 2a91518fbd..22bad0b35e 100644 --- a/test/jws/smoke.P-256K.test.js +++ b/test/jws/smoke.P-256K.test.js @@ -3,6 +3,8 @@ require('../../P-256K') const test = require('ava') +if ('electron' in process.versions) return + const { JWK: { asKey } } = require('../..') const type = 'P-256K' diff --git a/test/jws/smoke.test.js b/test/jws/smoke.test.js index 27839c29ea..944735896e 100644 --- a/test/jws/smoke.test.js +++ b/test/jws/smoke.test.js @@ -8,6 +8,7 @@ const { JWS: { success, failure } } = require('../macros') Object.entries(fixtures.PEM).forEach(([type, { private: key, public: pub }]) => { if (type === 'P-256K') return + if ('electron' in process.versions && (type.startsWith('X') || type === 'Ed448' || type === 'secp256k1')) return const sKey = asKey(key) const vKey = asKey(pub)