Skip to content

Commit

Permalink
refactor: get rid of multiple asn1 OID mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Apr 7, 2020
1 parent 75e331d commit 27b77b9
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 106 deletions.
6 changes: 4 additions & 2 deletions lib/help/asn1/algorithm_identifier.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const oids = require('./oids')

module.exports = function () {
this.seq().obj(
this.key('algorithm').objid(),
this.key('parameters').optional().any()
this.key('algorithm').objid(oids),
this.key('parameters').optional().choice({ namedCurve: this.objid(oids), null: this.null_() })
)
}
4 changes: 3 additions & 1 deletion lib/help/asn1/ec_private_key.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const oids = require('./oids')

module.exports = function () {
this.seq().obj(
this.key('version').int(),
this.key('privateKey').octstr(),
this.key('parameters').explicit(0).optional().choice({ namedCurve: this.objid() }),
this.key('parameters').explicit(0).optional().choice({ namedCurve: this.objid(oids) }),
this.key('publicKey').explicit(1).optional().bitstr()
)
}
5 changes: 0 additions & 5 deletions lib/help/asn1/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,4 @@ types.set('RSAPrivateKey', RSAPrivateKey)
const RSAPublicKey = asn1.define('RSAPublicKey', require('./rsa_public_key'))
types.set('RSAPublicKey', RSAPublicKey)

const OID = asn1.define('OID', function () {
return this.objid()
})
types.set('OID', OID)

module.exports = types
16 changes: 16 additions & 0 deletions lib/help/asn1/oids.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { name: secp256k1 } = require('../../jwk/key/secp256k1_crv')

const oids = {
'1 2 840 10045 3 1 7': 'P-256',
'1 3 132 0 10': secp256k1,
'1 3 132 0 34': 'P-384',
'1 3 132 0 35': 'P-521',
'1 2 840 10045 2 1': 'ecPublicKey',
'1 2 840 113549 1 1 1': 'rsaEncryption',
'1 3 101 110': 'X25519',
'1 3 101 111': 'X448',
'1 3 101 112': 'Ed25519',
'1 3 101 113': 'Ed448'
}

module.exports = oids
59 changes: 19 additions & 40 deletions lib/help/key_object.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,8 @@ 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}`
}

const unsupported = (input) => {
const label = typeof input === 'string' ? input : `OID ${input.join('.')}`
throw new errors.JOSENotSupported(`${label} is not supported in your Node.js runtime version`)
}

Expand All @@ -71,8 +55,8 @@ if (keyObjectSupported) {
const PublicKeyInfo = asn1.get('PublicKeyInfo')
const pem = PublicKeyInfo.encode({
algorithm: {
algorithm: '1.2.840.113549.1.1.1'.split('.'),
parameters: Buffer.from('BQA=', 'base64')
algorithm: 'rsaEncryption',
parameters: { type: 'null' }
},
publicKey: {
unused: 0,
Expand Down Expand Up @@ -127,8 +111,8 @@ if (keyObjectSupported) {
version: 0,
privateKey,
algorithm: {
algorithm: '1.2.840.113549.1.1.1'.split('.'),
parameters: Buffer.from('BQA=', 'base64')
algorithm: 'rsaEncryption',
parameters: { type: 'null' }
}
})

Expand All @@ -146,13 +130,12 @@ if (keyObjectSupported) {
publicKey: parsed.publicKey
})
const PrivateKeyInfo = asn1.get('PrivateKeyInfo')
const OID = asn1.get('OID')
const pkcs8 = PrivateKeyInfo.encode({
version: 0,
privateKey,
algorithm: {
algorithm: '1.2.840.10045.2.1'.split('.'),
parameters: OID.encode(this._asn1.parameters.value)
algorithm: 'ecPublicKey',
parameters: this._asn1.parameters
}
})

Expand Down Expand Up @@ -234,11 +217,10 @@ if (keyObjectSupported) {
switch (input.asymmetricKeyType) {
case 'ec': {
const PublicKeyInfo = asn1.get('PublicKeyInfo')
const OID = asn1.get('OID')
const key = PublicKeyInfo.encode({
algorithm: {
algorithm: '1.2.840.10045.2.1'.split('.'),
parameters: OID.encode(input._asn1.parameters.value)
algorithm: 'ecPublicKey',
parameters: input._asn1.parameters
},
publicKey: input._asn1.publicKey
})
Expand Down Expand Up @@ -301,9 +283,8 @@ if (keyObjectSupported) {
const parsed = PublicKeyInfo.decode(key, format, { label })

let type, keyObject
const oid = parsed.algorithm.algorithm.join('.')
switch (oid) {
case '1.2.840.10045.2.1': {
switch (parsed.algorithm.algorithm) {
case 'ecPublicKey': {
keyObject = new KeyObject()
keyObject._asn1 = parsed
keyObject._asymmetricKeyType = 'ec'
Expand All @@ -312,13 +293,13 @@ if (keyObjectSupported) {

break
}
case '1.2.840.113549.1.1.1': {
case 'rsaEncryption': {
type = 'pkcs1'
keyObject = createPublicKey({ type, key: parsed.publicKey.data, format: 'der' })
break
}
default:
unsupported(oid)
unsupported(parsed.algorithm.algorithm)
}

return keyObject
Expand Down Expand Up @@ -399,21 +380,19 @@ if (keyObjectSupported) {
const parsed = PrivateKeyInfo.decode(key, format, { label })

let type, keyObject
const oid = parsed.algorithm.algorithm.join('.')
switch (oid) {
case '1.2.840.10045.2.1': {
const OID = asn1.get('OID')
switch (parsed.algorithm.algorithm) {
case 'ecPublicKey': {
type = 'sec1'
keyObject = createPrivateKey({ type, key: parsed.privateKey, format: 'der' }, { [namedCurve]: OID.decode(parsed.algorithm.parameters) })
keyObject = createPrivateKey({ type, key: parsed.privateKey, format: 'der' }, { [namedCurve]: parsed.algorithm.parameters.value })
break
}
case '1.2.840.113549.1.1.1': {
case 'rsaEncryption': {
type = 'pkcs1'
keyObject = createPrivateKey({ type, key: parsed.privateKey, format: 'der' })
break
}
default:
unsupported(oid)
unsupported(parsed.algorithm.algorithm)
}

keyObject._pkcs8 = key
Expand Down
53 changes: 7 additions & 46 deletions lib/help/key_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

const { EOL } = require('os')

const { name: secp256k1 } = require('../jwk/key/secp256k1_crv')
const errors = require('../errors')

const { createPublicKey } = require('./key_object')
Expand All @@ -11,26 +10,6 @@ const asn1 = require('./asn1')
const computePrimes = require('./rsa_primes')
const { OKP_CURVES, EC_CURVES } = require('../registry')

const oidHexToCurve = new Map([
['06082a8648ce3d030107', 'P-256'],
['06052b8104000a', secp256k1],
['06052b81040022', 'P-384'],
['06052b81040023', 'P-521']
])
const EC_KEY_OID = '1.2.840.10045.2.1'.split('.')
const crvToOid = new Map([
['P-256', '1.2.840.10045.3.1.7'.split('.')],
[secp256k1, '1.3.132.0.10'.split('.')],
['P-384', '1.3.132.0.34'.split('.')],
['P-521', '1.3.132.0.35'.split('.')]
])
const crvToOidBuf = new Map([
['P-256', Buffer.from('06082a8648ce3d030107', 'hex')],
[secp256k1, Buffer.from('06052b8104000a', 'hex')],
['P-384', Buffer.from('06052b81040022', 'hex')],
['P-521', Buffer.from('06052b81040023', 'hex')]
])

const formatPem = (base64pem, descriptor) => `-----BEGIN ${descriptor} KEY-----${EOL}${base64pem.match(/.{1,64}/g).join(EOL)}${EOL}-----END ${descriptor} KEY-----`

const okpToJWK = {
Expand Down Expand Up @@ -109,8 +88,7 @@ const keyObjectToJWK = {
const PrivateKeyInfo = asn1.get('PrivateKeyInfo')
const ECPrivateKey = asn1.get('ECPrivateKey')

const { privateKey, algorithm: { parameters: curveOid } } = PrivateKeyInfo.decode(der)
const crv = oidHexToCurve.get(curveOid.toString('hex'))
const { privateKey, algorithm: { parameters: { value: crv } } } = PrivateKeyInfo.decode(der)
const { privateKey: d, publicKey: { data: publicKey } } = ECPrivateKey.decode(privateKey)

const x = publicKey.slice(1, ((publicKey.length - 1) / 2) + 1)
Expand All @@ -129,8 +107,7 @@ const keyObjectToJWK = {

const PublicKeyInfo = asn1.get('PublicKeyInfo')

const { publicKey: { data: publicKey }, algorithm: { parameters: curveOid } } = PublicKeyInfo.decode(der)
const crv = oidHexToCurve.get(curveOid.toString('hex'))
const { publicKey: { data: publicKey }, algorithm: { parameters: { value: crv } } } = PublicKeyInfo.decode(der)

const x = publicKey.slice(1, ((publicKey.length - 1) / 2) + 1)
const y = publicKey.slice(((publicKey.length - 1) / 2) + 1)
Expand Down Expand Up @@ -194,19 +171,6 @@ const concatEcPublicKey = (x, y) => ({
])
})

const okpCrvToOid = (crv) => {
switch (crv) {
case 'X25519':
return '1.3.101.110'.split('.')
case 'X448':
return '1.3.101.111'.split('.')
case 'Ed25519':
return '1.3.101.112'.split('.')
case 'Ed448':
return '1.3.101.113'.split('.')
}
}

const jwkToPem = {
RSA: {
private (jwk, { calculateMissingRSAPrimes }) {
Expand Down Expand Up @@ -255,10 +219,7 @@ const jwkToPem = {
return ECPrivateKey.encode({
version: 1,
privateKey: base64url.decodeToBuffer(jwk.d),
parameters: {
type: 'namedCurve',
value: crvToOid.get(jwk.crv)
},
parameters: { type: 'namedCurve', value: jwk.crv },
publicKey: concatEcPublicKey(jwk.x, jwk.y)
}, 'pem', { label: 'EC PRIVATE KEY' })
},
Expand All @@ -267,8 +228,8 @@ const jwkToPem = {

return PublicKeyInfo.encode({
algorithm: {
algorithm: EC_KEY_OID,
parameters: crvToOidBuf.get(jwk.crv)
algorithm: 'ecPublicKey',
parameters: { type: 'namedCurve', value: jwk.crv }
},
publicKey: concatEcPublicKey(jwk.x, jwk.y)
}, 'pem', { label: 'PUBLIC KEY' })
Expand All @@ -281,7 +242,7 @@ const jwkToPem = {
const b64 = OneAsymmetricKey.encode({
version: 0,
privateKey: { privateKey: base64url.decodeToBuffer(jwk.d) },
algorithm: { algorithm: okpCrvToOid(jwk.crv) }
algorithm: { algorithm: jwk.crv }
}, 'der')

// TODO: WHYYY? https://github.com/indutny/asn1.js/issues/110
Expand All @@ -293,7 +254,7 @@ const jwkToPem = {
const PublicKeyInfo = asn1.get('PublicKeyInfo')

return PublicKeyInfo.encode({
algorithm: { algorithm: okpCrvToOid(jwk.crv) },
algorithm: { algorithm: jwk.crv },
publicKey: {
unused: 0,
data: base64url.decodeToBuffer(jwk.x)
Expand Down
7 changes: 2 additions & 5 deletions lib/jwk/key/ec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ class ECKey extends Key {
constructor (...args) {
super(...args)
this[JWK_MEMBERS]()
Object.defineProperty(this, 'kty', {
value: 'EC',
enumerable: true
})
if (!this.crv) {
Object.defineProperty(this, 'kty', { value: 'EC', enumerable: true })
if (!this.crv || typeof this.crv !== 'string') {
throw new errors.JOSENotSupported('unsupported EC key curve')
}
}
Expand Down
11 changes: 4 additions & 7 deletions lib/jwk/key/okp.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,11 @@ Object.freeze(OKP_PRIVATE)
class OKPKey extends Key {
constructor (...args) {
super(...args)

Object.defineProperties(this, {
kty: {
value: 'OKP',
enumerable: true
}
})
this[JWK_MEMBERS]()
Object.defineProperty(this, 'kty', { value: 'OKP', enumerable: true })
if (!this.crv || typeof this.crv !== 'string') {
throw new errors.JOSENotSupported('unsupported OKP key curve')
}
}

static get [PUBLIC_MEMBERS] () {
Expand Down

0 comments on commit 27b77b9

Please sign in to comment.