From 9af295bdb27db1df73e2bd93027ad93ebef448ca Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Sun, 17 Mar 2019 17:44:36 +0100 Subject: [PATCH] fix: restrict RS key algorithms by the key's bit size --- lib/jwk/key/rsa.js | 49 ++++++++++++++++++++++++++++---------------- test/jwk/rsa.test.js | 45 ++++++++++++++++++++++++++++++++-------- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/lib/jwk/key/rsa.js b/lib/jwk/key/rsa.js index 42d53c90ef..79e510aab5 100644 --- a/lib/jwk/key/rsa.js +++ b/lib/jwk/key/rsa.js @@ -6,14 +6,6 @@ const { THUMBPRINT_MATERIAL, PUBLIC_MEMBERS, PRIVATE_MEMBERS, JWK_MEMBERS } = re const Key = require('./base') const generateKeyPair = promisify(async) -const SIG_ALGS = new Set([ - 'PS256', - 'RS256', - 'PS384', - 'RS384', - 'PS512', - 'RS512' -]) const WRAP_ALGS = new Set([ 'RSA-OAEP', @@ -25,6 +17,31 @@ Object.freeze(RSA_PUBLIC) const RSA_PRIVATE = new Set([...RSA_PUBLIC, 'd', 'p', 'q', 'dp', 'dq', 'qi']) Object.freeze(RSA_PRIVATE) +const sigAlgsAvailableFor = (length) => { + switch (true) { + case length >= 1040: + return new Set(['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512']) + case length >= 784: + return new Set(['PS256', 'RS256', 'PS384', 'RS384', 'RS512']) + case length >= 752: + return new Set(['PS256', 'RS256', 'RS384', 'RS512']) + case length >= 624: + return new Set(['PS256', 'RS256', 'RS384']) + case length >= 528: + return new Set(['PS256', 'RS256']) + default: + return new Set(['RS256']) + } +} + +const wrapAlgsAvailableFor = (length) => { + if (length >= 592) { + return new Set(WRAP_ALGS) + } + + return new Set(['RSA1_5']) +} + // RSA Key Type class RSAKey extends Key { constructor (...args) { @@ -62,10 +79,6 @@ class RSAKey extends Key { } algorithms (operation, { use = this.use, alg = this.alg } = {}) { - if (this.length < 2048) { - return new Set() - } - if (alg) { return new Set(this.algorithms(operation, { alg: null, use }).has(alg) ? [alg] : undefined) } @@ -79,25 +92,25 @@ class RSAKey extends Key { return new Set() } - return new Set(SIG_ALGS) + return sigAlgsAvailableFor(this.length) case 'verify': if (use === 'enc') { return new Set() } - return new Set(SIG_ALGS) + return sigAlgsAvailableFor(this.length) case 'wrapKey': if (use === 'sig') { return new Set() } - return new Set(WRAP_ALGS) + return wrapAlgsAvailableFor(this.length) case 'unwrapKey': if (this.public || use === 'sig') { return new Set() } - return new Set(WRAP_ALGS) + return wrapAlgsAvailableFor(this.length) case undefined: // just the ops needed to return all algs regardless of its use return new Set([ @@ -110,7 +123,7 @@ class RSAKey extends Key { } static async generate (len = 2048, opts, privat = true) { - if (!Number.isSafeInteger(len) || len < 2048 || len % 8 !== 0) { + if (!Number.isSafeInteger(len) || len < 512 || len % 8 !== 0) { throw new TypeError('invalid bit length') } @@ -120,7 +133,7 @@ class RSAKey extends Key { } static generateSync (len = 2048, opts, privat = true) { - if (!Number.isSafeInteger(len) || len < 2048 || len % 8 !== 0) { + if (!Number.isSafeInteger(len) || len < 512 || len % 8 !== 0) { throw new TypeError('invalid bit length') } diff --git a/test/jwk/rsa.test.js b/test/jwk/rsa.test.js index 0a074f5e48..29b2ca556c 100644 --- a/test/jwk/rsa.test.js +++ b/test/jwk/rsa.test.js @@ -34,14 +34,6 @@ test(`RSA key .algorithms invalid operation`, t => { t.deepEqual([...result], ['PS256', 'RS256', 'PS384', 'RS384', 'PS512', 'RS512', 'RSA-OAEP', 'RSA1_5']) }) - test('RSA < 2048 bits does not support any algorithms', t => { - const keyObject = createPublicKey(fixtures.RSA_512) - const key = new RSAKey(keyObject) - const result = key.algorithms() - t.is(result.constructor, Set) - t.deepEqual([...result], []) - }) - test('RSA Private key algorithms (no operation, w/ alg)', t => { const key = new RSAKey(keyObject, { alg: 'RS256' }) const result = key.algorithms() @@ -257,4 +249,41 @@ test(`RSA key .algorithms invalid operation`, t => { t.is(result.constructor, Set) t.deepEqual([...result], []) }) + + test('any RSA key can do RS256 and RSA1_5', t => { + const k = RSAKey.generateSync(512) + const result = k.algorithms() + t.is(result.constructor, Set) + t.deepEqual([...result], ['RS256', 'RSA1_5']) + }) + + test('RSA key >= 528 bits can do PS256', t => { + const k = RSAKey.generateSync(528) + t.true(k.algorithms().has('PS256')) + }) + + test('RSA key >= 592 bits can do RSA-OAEP', t => { + const k = RSAKey.generateSync(592) + t.true(k.algorithms().has('RSA-OAEP')) + }) + + test('RSA key >= 624 bits can do RS384', t => { + const k = RSAKey.generateSync(624) + t.true(k.algorithms().has('RS384')) + }) + + test('RSA key >= 752 bits can do RS512', t => { + const k = RSAKey.generateSync(752) + t.true(k.algorithms().has('RS512')) + }) + + test('RSA key >= 784 bits can do PS384', t => { + const k = RSAKey.generateSync(784) + t.true(k.algorithms().has('PS384')) + }) + + test('RSA key >= 1040 bits can do PS512', t => { + const k = RSAKey.generateSync(1040) + t.true(k.algorithms().has('PS512')) + }) })()