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 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 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 { KeyObject = class KeyObject {
export ({ cipher, passphrase, type, format } = {}) { export ({ cipher, passphrase, type, format } = {}) {
@ -286,7 +306,8 @@ if (!keyObjectSupported) {
const parsed = PublicKeyInfo.decode(key, format, { label }) const parsed = PublicKeyInfo.decode(key, format, { label })
let type, keyObject let type, keyObject
switch (parsed.algorithm.algorithm.join('.')) { const oid = parsed.algorithm.algorithm.join('.')
switch (oid) {
case '1.2.840.10045.2.1': { case '1.2.840.10045.2.1': {
keyObject = new KeyObject() keyObject = new KeyObject()
i(keyObject).asn1 = parsed i(keyObject).asn1 = parsed
@ -302,7 +323,7 @@ if (!keyObjectSupported) {
break break
} }
default: default:
throw new errors.JOSENotSupported(`OID ${parsed.algorithm.algorithm.join('.')} is not supported in your Node.js runtime version`) unsupported(oid)
} }
return keyObject return keyObject
@ -375,7 +396,8 @@ if (!keyObjectSupported) {
const parsed = PrivateKeyInfo.decode(key, format, { label }) const parsed = PrivateKeyInfo.decode(key, format, { label })
let type, keyObject let type, keyObject
switch (parsed.algorithm.algorithm.join('.')) { const oid = parsed.algorithm.algorithm.join('.')
switch (oid) {
case '1.2.840.10045.2.1': { case '1.2.840.10045.2.1': {
const OID = asn1.get('OID') const OID = asn1.get('OID')
type = 'sec1' type = 'sec1'
@ -388,7 +410,7 @@ if (!keyObjectSupported) {
break break
} }
default: 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 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 } 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 { try {
privateKey = createPrivateKey(key) privateKey = createPrivateKey(key)
} catch (err) {} } catch (err) {
if (err instanceof errors.JOSEError) {
throw err
}
}
try { try {
publicKey = createPublicKey(key) publicKey = createPublicKey(key)
@ -95,7 +99,11 @@ const asKey = (key, parameters, { calculateMissingRSAPrimes = false } = {}) => {
x5c: [key.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '')] x5c: [key.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '')]
}) })
} }
} catch (err) {} } catch (err) {
if (err instanceof errors.JOSEError) {
throw err
}
}
try { try {
// this is to filter out invalid PEM keys and certs, i'll rather have them fail import then // 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' }) }, { instanceOf: errors.JOSEInvalidEncoding, code: 'ERR_JOSE_INVALID_ENCODING', message: 'input is not a valid base64url encoded string' })
}) })
if (keyObjectSupported) { const cert = `-----BEGIN CERTIFICATE-----
test('importing a certificate file populates the certificate properties', t => {
const key = asKey(`-----BEGIN CERTIFICATE-----
MIIC4DCCAcgCCQDO8JBSH914NDANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJD MIIC4DCCAcgCCQDO8JBSH914NDANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJD
WjEPMA0GA1UEBwwGUHJhZ3VlMRIwEAYDVQQDDAlwa210bHN0d28wHhcNMTkwNjE4 WjEPMA0GA1UEBwwGUHJhZ3VlMRIwEAYDVQQDDAlwa210bHN0d28wHhcNMTkwNjE4
MTIzMjAxWhcNMjAwNjE3MTIzMjAxWjAyMQswCQYDVQQGEwJDWjEPMA0GA1UEBwwG MTIzMjAxWhcNMjAwNjE3MTIzMjAxWjAyMQswCQYDVQQGEwJDWjEPMA0GA1UEBwwG
@ -184,9 +182,19 @@ KwwOdRu7VJpAxvweA/3woKl6Cjfy20ZupPH9mxr1R78BMKgEtdFsiLwbB7MOdDbT
LsrUcEcupXv+gZek22upQKrAk/XFP067KIqKmCEhDidxhP251SloUaruv9cHEx0a LsrUcEcupXv+gZek22upQKrAk/XFP067KIqKmCEhDidxhP251SloUaruv9cHEx0a
DKol9eR465FAiBLvg2N7qJHCKlWdn99SgN4Y3kINsuFR7Tj4QIJZNubOjV0YeOgn DKol9eR465FAiBLvg2N7qJHCKlWdn99SgN4Y3kINsuFR7Tj4QIJZNubOjV0YeOgn
AWzRJlZD89KZAQgjj4Z215QeLxA= 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.x5c)
t.truthy(key.x5t) t.truthy(key.x5t)
t.truthy(key['x5t#S256']) 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 test = require('ava')
const { keyObjectSupported } = require('../../lib/help/runtime_support') 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 { createPrivateKey, createPublicKey } = require('crypto')
const { hasProperty, hasNoProperties, hasProperties } = require('../macros') const { hasProperty, hasNoProperties, hasProperties } = require('../macros')
const fixtures = require('../fixtures')
const OKPKey = require('../../lib/jwk/key/okp') const OKPKey = require('../../lib/jwk/key/okp')