Skip to content

Commit

Permalink
refactor: backwards compatibility for node < 12
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Nov 27, 2019
1 parent 3a69062 commit d45f845
Show file tree
Hide file tree
Showing 12 changed files with 50 additions and 27 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ jobs:
node-version:
- 10.13.0
- 10
- 11.0.0
- 11
- 12.0.0
- 12
- 13.0.0
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,8 @@ private API and is subject to change between any versions.

#### How do I use it outside of Node.js

It is **only built for ^10.13.0 || >=12.0.0 Node.js** environment - including `jose` in
transpiled browser-environment targeted projects is not supported and may result in unexpected
results.
It is **only built for >=10.13.0 Node.js** environment - including `jose` in transpiled
browser-environment targeted projects is not supported and may result in unexpected results.

#### How is it different from [`jws`](https://github.com/brianloveswords/node-jws), [`jwa`](https://github.com/brianloveswords/node-jwa) or [`jsonwebtoken`](https://github.com/auth0/node-jsonwebtoken)?

Expand Down
23 changes: 19 additions & 4 deletions lib/help/key_object.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
let { createPublicKey, createPrivateKey, createSecretKey, KeyObject } = require('crypto')

if (!createPublicKey || !createPrivateKey || !createSecretKey || !KeyObject) {
const { keyObjectSupported } = require('./runtime_support')

let asInput

if (!keyObjectSupported) {
const { EOL } = require('os')

const errors = require('../errors')
const isObject = require('./is_object')
const asn1 = require('./asn1')
const toInput = Symbol('toInput')

const namedCurve = Symbol('namedCurve')
const map = new WeakMap()
Expand All @@ -17,6 +22,14 @@ if (!createPublicKey || !createPrivateKey || !createSecretKey || !KeyObject) {
return map.get(ctx)
}

asInput = (keyObject, needsPublic) => {
if (keyObject instanceof KeyObject) {
return keyObject[toInput](needsPublic)
}

return createSecretKey(keyObject)[toInput](needsPublic)
}

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}-----`

Expand Down Expand Up @@ -168,7 +181,7 @@ if (!createPublicKey || !createPrivateKey || !createSecretKey || !KeyObject) {
return i(this).symmetricKeySize
}

asInput (needsPublic = false) {
[toInput] (needsPublic) {
switch (i(this).type) {
case 'secret':
return i(this).buffer
Expand All @@ -180,7 +193,7 @@ if (!createPublicKey || !createPrivateKey || !createSecretKey || !KeyObject) {
i(this).pub = createPublicKey(this)
}

return i(this).pub.asInput()
return i(this).pub[toInput](false)
}

return i(this).pem
Expand Down Expand Up @@ -415,6 +428,8 @@ if (!createPublicKey || !createPrivateKey || !createSecretKey || !KeyObject) {
throw new TypeError('type must be one of "pkcs8", "pkcs1" or "sec1"')
}
}
} else {
asInput = (input) => input
}

module.exports = { createPublicKey, createPrivateKey, createSecretKey, KeyObject }
module.exports = { createPublicKey, createPrivateKey, createSecretKey, KeyObject, asInput }
2 changes: 1 addition & 1 deletion lib/help/runtime_support.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ const [major, minor] = process.version.substr(1).split('.').map(x => parseInt(x,

module.exports = {
oaepHashSupported: major > 12 || (major === 12 && minor >= 9),
keyObjectSupported: !!KeyObject,
keyObjectSupported: !!KeyObject && major >= 12,
edDSASupported: !!sign && !!verify
}
5 changes: 3 additions & 2 deletions lib/jwa/aes_gcm.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { createCipheriv, createDecipheriv } = require('crypto')

const { KEYOBJECT } = require('../help/consts')
const { JWEInvalid, JWEDecryptionFailed } = require('../errors')
const { asInput } = require('../help/key_object')

const checkInput = function (size, iv, tag) {
if (iv.length !== 12) {
Expand All @@ -16,7 +17,7 @@ const checkInput = function (size, iv, tag) {
}

const encrypt = (size, { [KEYOBJECT]: keyObject }, cleartext, { iv, aad = Buffer.alloc(0) }) => {
const key = keyObject.asInput ? keyObject.asInput() : keyObject
const key = asInput(keyObject, false)
checkInput(size, iv)

const cipher = createCipheriv(`aes-${size}-gcm`, key, iv)
Expand All @@ -29,7 +30,7 @@ const encrypt = (size, { [KEYOBJECT]: keyObject }, cleartext, { iv, aad = Buffer
}

const decrypt = (size, { [KEYOBJECT]: keyObject }, ciphertext, { iv, tag = Buffer.alloc(0), aad = Buffer.alloc(0) }) => {
const key = keyObject.asInput ? keyObject.asInput() : keyObject
const key = asInput(keyObject, false)
checkInput(size, iv, tag)

try {
Expand Down
5 changes: 3 additions & 2 deletions lib/jwa/aes_kw.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const { createCipheriv, createDecipheriv } = require('crypto')
const uint64be = require('../help/uint64be')
const timingSafeEqual = require('../help/timing_safe_equal')
const { KEYOBJECT } = require('../help/consts')
const { asInput } = require('../help/key_object')

const checkInput = (data) => {
if (data !== undefined && data.length % 8 !== 0) {
Expand Down Expand Up @@ -32,7 +33,7 @@ const split = (input, size) => {
}

const wrapKey = (size, { [KEYOBJECT]: keyObject }, payload) => {
const key = keyObject.asInput ? keyObject.asInput() : keyObject
const key = asInput(keyObject, false)
const iv = Buffer.alloc(16)
let R = split(payload, 8)
let A
Expand All @@ -56,7 +57,7 @@ const wrapKey = (size, { [KEYOBJECT]: keyObject }, payload) => {
}

const unwrapKey = (size, { [KEYOBJECT]: keyObject }, payload) => {
const key = keyObject.asInput ? keyObject.asInput() : keyObject
const key = asInput(keyObject, false)
checkInput(payload)

const iv = Buffer.alloc(16)
Expand Down
9 changes: 5 additions & 4 deletions lib/jwa/ecdsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,32 @@ const { sign: signOneShot, verify: verifyOneShot, createSign, createVerify } = r
const { derToJose, joseToDer } = require('../help/ecdsa_signatures')
const { KEYOBJECT } = require('../help/consts')
const resolveNodeAlg = require('../help/node_alg')
const { asInput } = require('../help/key_object')

let sign, verify

if (signOneShot) {
sign = (jwaAlg, nodeAlg, { [KEYOBJECT]: keyObject }, payload) => {
return derToJose(signOneShot(nodeAlg, payload, keyObject), jwaAlg)
return derToJose(signOneShot(nodeAlg, payload, asInput(keyObject, false)), jwaAlg)
}
} else {
sign = (jwaAlg, nodeAlg, { [KEYOBJECT]: keyObject }, payload) => {
return derToJose(createSign(nodeAlg).update(payload).sign(keyObject.asInput()), jwaAlg)
return derToJose(createSign(nodeAlg).update(payload).sign(asInput(keyObject, false)), jwaAlg)
}
}

if (verifyOneShot) {
verify = (jwaAlg, nodeAlg, { [KEYOBJECT]: keyObject }, payload, signature) => {
try {
return verifyOneShot(nodeAlg, payload, keyObject, joseToDer(signature, jwaAlg))
return verifyOneShot(nodeAlg, payload, asInput(keyObject, true), joseToDer(signature, jwaAlg))
} catch (err) {
return false
}
}
} else {
verify = (jwaAlg, nodeAlg, { [KEYOBJECT]: keyObject }, payload, signature) => {
try {
return createVerify(nodeAlg).update(payload).verify(keyObject.asInput(true), joseToDer(signature, jwaAlg))
return createVerify(nodeAlg).update(payload).verify(asInput(keyObject, true), joseToDer(signature, jwaAlg))
} catch (err) {
return false
}
Expand Down
5 changes: 3 additions & 2 deletions lib/jwa/hmac.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ const { createHmac } = require('crypto')
const { KEYOBJECT } = require('../help/consts')
const timingSafeEqual = require('../help/timing_safe_equal')
const resolveNodeAlg = require('../help/node_alg')
const { asInput } = require('../help/key_object')

const sign = (jwaAlg, hmacAlg, { [KEYOBJECT]: keyObject }, payload) => {
const hmac = createHmac(hmacAlg, keyObject.asInput ? keyObject.asInput() : keyObject)
const hmac = createHmac(hmacAlg, asInput(keyObject, false))
hmac.update(payload)
return hmac.digest()
}

const verify = (jwaAlg, hmacAlg, { [KEYOBJECT]: keyObject }, payload, signature) => {
const hmac = createHmac(hmacAlg, keyObject.asInput ? keyObject.asInput() : keyObject)
const hmac = createHmac(hmacAlg, asInput(keyObject, false))
hmac.update(payload)
const expected = hmac.digest()
const actual = signature
Expand Down
5 changes: 3 additions & 2 deletions lib/jwa/rsaes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { strict: assert } = require('assert')
const { publicEncrypt, privateDecrypt, constants } = require('crypto')

const { KEYOBJECT } = require('../help/consts')
const { asInput } = require('../help/key_object')

const resolvePadding = (alg) => {
switch (alg) {
Expand All @@ -25,12 +26,12 @@ const resolveOaepHash = (alg) => {
}

const wrapKey = (padding, oaepHash, { [KEYOBJECT]: keyObject }, payload) => {
const key = keyObject.asInput ? keyObject.asInput(true) : keyObject
const key = asInput(keyObject, true)
return { wrapped: publicEncrypt({ key, oaepHash, padding }, payload) }
}

const unwrapKey = (padding, oaepHash, { [KEYOBJECT]: keyObject }, payload) => {
const key = keyObject.asInput ? keyObject.asInput(false) : keyObject
const key = asInput(keyObject, false)
return privateDecrypt({ key, oaepHash, padding }, payload)
}

Expand Down
5 changes: 3 additions & 2 deletions lib/jwa/rsassa.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { sign: signOneShot, verify: verifyOneShot, createSign, createVerify } = r

const { KEYOBJECT } = require('../help/consts')
const resolveNodeAlg = require('../help/node_alg')
const { asInput } = require('../help/key_object')

let sign, verify

Expand All @@ -12,7 +13,7 @@ if (signOneShot) {
}
} else {
sign = (nodeAlg, { [KEYOBJECT]: keyObject }, payload) => {
return createSign(nodeAlg).update(payload).sign(keyObject.asInput())
return createSign(nodeAlg).update(payload).sign(asInput(keyObject, false))
}
}

Expand All @@ -23,7 +24,7 @@ if (verifyOneShot) {
} else {
verify = (nodeAlg, { [KEYOBJECT]: keyObject }, payload, signature) => {
try {
return createVerify(nodeAlg).update(payload).verify(keyObject.asInput(true), signature)
return createVerify(nodeAlg).update(payload).verify(asInput(keyObject, true), signature)
} catch (err) {
return false
}
Expand Down
9 changes: 5 additions & 4 deletions lib/jwa/rsassa_pss.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@ const {

const { KEYOBJECT } = require('../help/consts')
const resolveNodeAlg = require('../help/node_alg')
const { asInput } = require('../help/key_object')

let sign, verify

if (signOneShot) {
sign = (nodeAlg, { [KEYOBJECT]: keyObject }, payload) => {
return signOneShot(nodeAlg, payload, {
key: keyObject,
key: asInput(keyObject, false),
padding: constants.RSA_PKCS1_PSS_PADDING,
saltLength: constants.RSA_PSS_SALTLEN_DIGEST
})
}
} else {
sign = (nodeAlg, { [KEYOBJECT]: keyObject }, payload) => {
const key = keyObject.asInput()
const key = asInput(keyObject, false)
return createSign(nodeAlg).update(payload).sign({
key,
padding: constants.RSA_PKCS1_PSS_PADDING,
Expand All @@ -34,14 +35,14 @@ if (signOneShot) {
if (verifyOneShot) {
verify = (nodeAlg, { [KEYOBJECT]: keyObject }, payload, signature) => {
return verifyOneShot(nodeAlg, payload, {
key: keyObject,
key: asInput(keyObject, false),
padding: constants.RSA_PKCS1_PSS_PADDING,
saltLength: constants.RSA_PSS_SALTLEN_DIGEST
}, signature)
}
} else {
verify = (nodeAlg, { [KEYOBJECT]: keyObject }, payload, signature) => {
const key = keyObject.asInput(true)
const key = asInput(keyObject, true)
return createVerify(nodeAlg).update(payload).verify({
key,
padding: constants.RSA_PKCS1_PSS_PADDING,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"standard": "^14.3.1"
},
"engines": {
"node": "^10.13.0 || >=12.0.0"
"node": ">=10.13.0"
},
"ava": {
"babel": false,
Expand Down

0 comments on commit d45f845

Please sign in to comment.