fix: expose JOSENotSupported key import errors on unsupported runtimes

This commit is contained in:
Filip Skokan 2020-01-07 23:10:14 +01:00
parent bb58c9ce52
commit bc81e5dec2
4 changed files with 79 additions and 12 deletions

View file

@ -32,6 +32,26 @@ if (!keyObjectSupported) {
const pemToDer = pem => Buffer.from(pem.replace(/(?:-----(?:BEGIN|END)(?: (?:RSA|EC))? (?:PRIVATE|PUBLIC) KEY-----|\s)/g, ''), 'base64')
const derToPem = (der, label) => `-----BEGIN ${label}-----${EOL}${der.toString('base64').match(/.{1,64}/g).join(EOL)}${EOL}-----END ${label}-----`
const unsupported = (label) => {
switch (label) {
case '1.3.101.110':
label = 'X25519'
break
case '1.3.101.111':
label = 'X448'
break
case '1.3.101.112':
label = 'Ed25519'
break
case '1.3.101.113':
label = 'Ed448'
break
default:
label = `OID ${label}`
}
throw new errors.JOSENotSupported(`${label} is not supported in your Node.js runtime version`)
}
KeyObject = class KeyObject {
export ({ cipher, passphrase, type, format } = {}) {
@ -286,7 +306,8 @@ if (!keyObjectSupported) {
const parsed = PublicKeyInfo.decode(key, format, { label })
let type, keyObject
switch (parsed.algorithm.algorithm.join('.')) {
const oid = parsed.algorithm.algorithm.join('.')
switch (oid) {
case '1.2.840.10045.2.1': {
keyObject = new KeyObject()
i(keyObject).asn1 = parsed
@ -302,7 +323,7 @@ if (!keyObjectSupported) {
break
}
default:
throw new errors.JOSENotSupported(`OID ${parsed.algorithm.algorithm.join('.')} is not supported in your Node.js runtime version`)
unsupported(oid)
}
return keyObject
@ -375,7 +396,8 @@ if (!keyObjectSupported) {
const parsed = PrivateKeyInfo.decode(key, format, { label })
let type, keyObject
switch (parsed.algorithm.algorithm.join('.')) {
const oid = parsed.algorithm.algorithm.join('.')
switch (oid) {
case '1.2.840.10045.2.1': {
const OID = asn1.get('OID')
type = 'sec1'
@ -388,7 +410,7 @@ if (!keyObjectSupported) {
break
}
default:
throw new errors.JOSENotSupported(`OID ${parsed.algorithm.algorithm.join('.')} is not supported in your Node.js runtime version`)
unsupported(oid)
}
i(keyObject).pkcs8 = key

View file

@ -86,7 +86,11 @@ const asKey = (key, parameters, { calculateMissingRSAPrimes = false } = {}) => {
} else if (key && (typeof key === 'object' || typeof key === 'string')) { // <Object> | <string> | <Buffer> passed to crypto.createPrivateKey or crypto.createPublicKey or <Buffer> passed to crypto.createSecretKey
try {
privateKey = createPrivateKey(key)
} catch (err) {}
} catch (err) {
if (err instanceof errors.JOSEError) {
throw err
}
}
try {
publicKey = createPublicKey(key)
@ -95,7 +99,11 @@ const asKey = (key, parameters, { calculateMissingRSAPrimes = false } = {}) => {
x5c: [key.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '')]
})
}
} catch (err) {}
} catch (err) {
if (err instanceof errors.JOSEError) {
throw err
}
}
try {
// this is to filter out invalid PEM keys and certs, i'll rather have them fail import then

View file

@ -165,9 +165,7 @@ test('invalid encoded oct jwk import', async t => {
}, { instanceOf: errors.JOSEInvalidEncoding, code: 'ERR_JOSE_INVALID_ENCODING', message: 'input is not a valid base64url encoded string' })
})
if (keyObjectSupported) {
test('importing a certificate file populates the certificate properties', t => {
const key = asKey(`-----BEGIN CERTIFICATE-----
const cert = `-----BEGIN CERTIFICATE-----
MIIC4DCCAcgCCQDO8JBSH914NDANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJD
WjEPMA0GA1UEBwwGUHJhZ3VlMRIwEAYDVQQDDAlwa210bHN0d28wHhcNMTkwNjE4
MTIzMjAxWhcNMjAwNjE3MTIzMjAxWjAyMQswCQYDVQQGEwJDWjEPMA0GA1UEBwwG
@ -184,9 +182,19 @@ KwwOdRu7VJpAxvweA/3woKl6Cjfy20ZupPH9mxr1R78BMKgEtdFsiLwbB7MOdDbT
LsrUcEcupXv+gZek22upQKrAk/XFP067KIqKmCEhDidxhP251SloUaruv9cHEx0a
DKol9eR465FAiBLvg2N7qJHCKlWdn99SgN4Y3kINsuFR7Tj4QIJZNubOjV0YeOgn
AWzRJlZD89KZAQgjj4Z215QeLxA=
-----END CERTIFICATE-----`)
-----END CERTIFICATE-----`
if (keyObjectSupported) {
test('importing a certificate file populates the certificate properties', t => {
const key = asKey(cert)
t.truthy(key.x5c)
t.truthy(key.x5t)
t.truthy(key['x5t#S256'])
})
} else {
test('cannot import a certificate', t => {
t.throws(() => {
asKey(cert)
}, { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'X.509 certificates are not supported in your Node.js runtime version' })
})
}

View file

@ -1,12 +1,41 @@
const test = require('ava')
const { keyObjectSupported } = require('../../lib/help/runtime_support')
const errors = require('../../lib/errors')
if (!keyObjectSupported) return
const fixtures = require('../fixtures')
if (!keyObjectSupported) {
const JWK = require('../../lib/jwk')
;[
[fixtures.PEM.Ed25519.public, 'Ed25519'],
[fixtures.PEM.Ed25519.private, 'Ed25519'],
[fixtures.PEM.Ed448.public, 'Ed448'],
[fixtures.PEM.Ed448.private, 'Ed448'],
[fixtures.PEM.X25519.public, 'X25519'],
[fixtures.PEM.X25519.private, 'X25519'],
[fixtures.PEM.X448.public, 'X448'],
[fixtures.PEM.X448.private, 'X448'],
[fixtures.JWK.Ed25519, 'Ed25519'],
[fixtures.JWK.Ed448, 'Ed448'],
[fixtures.JWK.X25519, 'X25519'],
[fixtures.JWK.X448, 'X448'],
[{ ...fixtures.JWK.Ed25519, d: undefined }, 'Ed25519'],
[{ ...fixtures.JWK.Ed448, d: undefined }, 'Ed448'],
[{ ...fixtures.JWK.X25519, d: undefined }, 'X25519'],
[{ ...fixtures.JWK.X448, d: undefined }, 'X448']
].forEach(([input, label], i, { length }) => {
test(`OKP ${i + 1} / ${length}`, t => {
t.throws(() => {
JWK.asKey(input)
}, { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: `${label} is not supported in your Node.js runtime version` })
})
})
return
}
const { createPrivateKey, createPublicKey } = require('crypto')
const { hasProperty, hasNoProperties, hasProperties } = require('../macros')
const fixtures = require('../fixtures')
const OKPKey = require('../../lib/jwk/key/okp')