diff --git a/lib/jwk/generate.js b/lib/jwk/generate.js index 4a9d886c..e4ae6544 100644 --- a/lib/jwk/generate.js +++ b/lib/jwk/generate.js @@ -2,7 +2,7 @@ const RSAKey = require('./key/rsa') const ECKey = require('./key/ec') const OctKey = require('./key/oct') -const generate = (kty, ...args) => { +const generate = async (kty, ...args) => { switch (kty) { case 'rsa': case 'RSA': diff --git a/lib/jwks/keystore.js b/lib/jwks/keystore.js index 98a3854b..95e23fcb 100644 --- a/lib/jwks/keystore.js +++ b/lib/jwks/keystore.js @@ -44,6 +44,10 @@ class KeyStore { candidate = false } + if (candidate && kty !== undefined && key.kty !== kty) { + candidate = false + } + if (candidate && use !== undefined && (key.use !== undefined && key.use !== use)) { candidate = false } @@ -88,6 +92,10 @@ class KeyStore { generateSync (...args) { this[KEYS].add(generateSync(...args)) } + + get size () { + return this[KEYS].size + } } module.exports = KeyStore diff --git a/package.json b/package.json index e7eff123..7e6e4a23 100644 --- a/package.json +++ b/package.json @@ -29,16 +29,6 @@ "test": "ava", "watch": "ava --watch" }, - "husky": { - "hooks": { - "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" - } - }, - "commitlint": { - "extends": [ - "@commitlint/config-conventional" - ] - }, "dependencies": { "@trust/keyto": "^0.3.5" }, @@ -60,6 +50,16 @@ "test/**/*.test.js" ] }, + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] + }, + "husky": { + "hooks": { + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" + } + }, "nyc": { "reporter": [ "lcov", diff --git a/test/jwk/generate.test.js b/test/jwk/generate.test.js index 4a0eb591..36191cba 100644 --- a/test/jwk/generate.test.js +++ b/test/jwk/generate.test.js @@ -109,6 +109,18 @@ const { JWK: { generate, generateSync } } = require('../..'); }) }) +test('fails to generateSync unsupported kty', t => { + t.throws(() => { + generateSync('OKP') + }, { instanceOf: TypeError, message: 'invalid key type' }) +}) + +test('fails to generate unsupported kty', async t => { + await t.throwsAsync(() => { + return generate('OKP') + }, { instanceOf: TypeError, message: 'invalid key type' }) +}) + test('fails to generateSync RSA with invalid bit lengths', t => { t.throws(() => { generateSync('rsa', 2048 + 1) diff --git a/test/jwks/keystore.test.js b/test/jwks/keystore.test.js new file mode 100644 index 00000000..53b08f47 --- /dev/null +++ b/test/jwks/keystore.test.js @@ -0,0 +1,115 @@ +const test = require('ava') + +const KeyStore = require('../../lib/jwks/keystore') +const { generateSync } = require('../../lib/jwk') + +test('constructor', t => { + t.notThrows(() => { + new KeyStore() // eslint-disable-line no-new + }) + t.notThrows(() => { + new KeyStore(generateSync('ec')) // eslint-disable-line no-new + }) + t.notThrows(() => { + new KeyStore(generateSync('ec'), generateSync('ec')) // eslint-disable-line no-new + }) +}) + +test('constructor only accepts Key instances created through JWK.importKey', 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' }) +}) + +test('.generate()', async t => { + const ks = new KeyStore() + await ks.generate('ec') + t.is(ks.size, 1) +}) + +test('.generateSync()', t => { + const ks = new KeyStore() + ks.generateSync('ec') + t.is(ks.size, 1) +}) + +test('.add()', t => { + const ks = new KeyStore() + const k = generateSync('ec') + ks.add(k) + t.is(ks.size, 1) + t.throws(() => { + ks.add(k) + }, { code: 'ERR_TODO' }) + t.throws(() => { + ks.add({}) + }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.importKey' }) +}) + +test('.remove()', t => { + const k = generateSync('ec') + const ks = new KeyStore(k) + ks.remove(k) + t.is(ks.size, 0) + t.throws(() => { + ks.remove(k) + }, { code: 'ERR_TODO' }) + t.throws(() => { + ks.remove({}) + }, { instanceOf: TypeError, message: 'key must be an instance of a key instantiated by JWK.importKey' }) +}) + +test('.all() and .get() use filter', t => { + const k = generateSync('rsa', undefined, { use: 'sig' }) + const ks = new KeyStore(k) + t.deepEqual(ks.all({ use: 'enc' }), []) + t.deepEqual(ks.all({ use: 'sig' }), [k]) + t.is(ks.get({ use: 'enc' }), undefined) + t.is(ks.get({ use: 'sig' }), k) +}) + +test('.all() and .get() use sort', t => { + const k = generateSync('rsa') + const k2 = generateSync('rsa', undefined, { use: 'sig' }) + const ks = new KeyStore(k, k2) + t.deepEqual(ks.all({ use: 'sig' }), [k2, k]) + t.is(ks.get({ use: 'sig' }), k2) +}) + +test('.all() and .get() kid filter', t => { + const k = generateSync('rsa', undefined, { kid: 'foobar' }) + const ks = new KeyStore(k) + t.deepEqual(ks.all({ kid: 'baz' }), []) + t.deepEqual(ks.all({ kid: 'foobar' }), [k]) + t.is(ks.get({ kid: 'baz' }), undefined) + t.is(ks.get({ kid: 'foobar' }), k) +}) + +test('.all() and .get() kty filter', t => { + const ks = new KeyStore() + ks.generateSync('rsa') + ks.generateSync('ec') + ks.generateSync('oct') + t.is(ks.all({ kty: 'oct' }).length, 1) + t.is(ks.all({ kty: 'RSA' }).length, 1) + t.is(ks.all({ kty: 'EC' }).length, 1) +}) + +test('.all() and .get() alg filter', t => { + const k = generateSync('rsa') + const ks = new KeyStore(k) + t.deepEqual(ks.all({ alg: 'HS256' }), []) + t.deepEqual(ks.all({ alg: 'RS256' }), [k]) + t.is(ks.get({ alg: 'HS256' }), undefined) + t.is(ks.get({ alg: 'RS256' }), k) +}) + +test('.all() and .get() alg sort', t => { + const k = generateSync('rsa') + const k2 = generateSync('rsa', undefined, { alg: 'RS256' }) + const ks = new KeyStore(k, k2) + t.deepEqual(ks.all({ alg: 'HS256' }), []) + t.deepEqual(ks.all({ alg: 'RS256' }), [k2, k]) + t.is(ks.get({ alg: 'HS256' }), undefined) + t.is(ks.get({ alg: 'RS256' }), k2) +})