add node modules

This commit is contained in:
Oliver King
2021-12-07 13:18:08 -05:00
parent 315a1c1f59
commit a00f2d2297
11231 changed files with 2072252 additions and 1 deletions
+74
View File
@@ -0,0 +1,74 @@
const { createCipheriv, createDecipheriv, getCiphers } = require('crypto')
const uint64be = require('../help/uint64be')
const timingSafeEqual = require('../help/timing_safe_equal')
const { KEYOBJECT } = require('../help/consts')
const { JWEInvalid, JWEDecryptionFailed } = require('../errors')
const checkInput = function (size, iv, tag) {
if (iv.length !== 16) {
throw new JWEInvalid('invalid iv')
}
if (arguments.length === 3) {
if (tag.length !== size / 8) {
throw new JWEInvalid('invalid tag')
}
}
}
const encrypt = (size, sign, { [KEYOBJECT]: keyObject }, cleartext, { iv, aad = Buffer.alloc(0) }) => {
const key = keyObject.export()
checkInput(size, iv)
const keySize = size / 8
const encKey = key.slice(keySize)
const cipher = createCipheriv(`aes-${size}-cbc`, encKey, iv)
const ciphertext = Buffer.concat([cipher.update(cleartext), cipher.final()])
const macData = Buffer.concat([aad, iv, ciphertext, uint64be(aad.length * 8)])
const macKey = key.slice(0, keySize)
const tag = sign({ [KEYOBJECT]: macKey }, macData).slice(0, keySize)
return { ciphertext, tag }
}
const decrypt = (size, sign, { [KEYOBJECT]: keyObject }, ciphertext, { iv, tag = Buffer.alloc(0), aad = Buffer.alloc(0) }) => {
checkInput(size, iv, tag)
const keySize = size / 8
const key = keyObject.export()
const encKey = key.slice(keySize)
const macKey = key.slice(0, keySize)
const macData = Buffer.concat([aad, iv, ciphertext, uint64be(aad.length * 8)])
const expectedTag = sign({ [KEYOBJECT]: macKey }, macData, tag).slice(0, keySize)
const macCheckPassed = timingSafeEqual(tag, expectedTag)
if (!macCheckPassed) {
throw new JWEDecryptionFailed()
}
let cleartext
try {
const cipher = createDecipheriv(`aes-${size}-cbc`, encKey, iv)
cleartext = Buffer.concat([cipher.update(ciphertext), cipher.final()])
} catch (err) {}
if (!cleartext) {
throw new JWEDecryptionFailed()
}
return cleartext
}
module.exports = (JWA, JWK) => {
['A128CBC-HS256', 'A192CBC-HS384', 'A256CBC-HS512'].forEach((jwaAlg) => {
const size = parseInt(jwaAlg.substr(1, 3), 10)
const sign = JWA.sign.get(`HS${size * 2}`)
if (getCiphers().includes(`aes-${size}-cbc`)) {
JWA.encrypt.set(jwaAlg, encrypt.bind(undefined, size, sign))
JWA.decrypt.set(jwaAlg, decrypt.bind(undefined, size, sign))
JWK.oct.encrypt[jwaAlg] = JWK.oct.decrypt[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length / 2 === size
}
})
}
+55
View File
@@ -0,0 +1,55 @@
const { createCipheriv, createDecipheriv, getCiphers } = 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) {
throw new JWEInvalid('invalid iv')
}
if (arguments.length === 3) {
if (tag.length !== 16) {
throw new JWEInvalid('invalid tag')
}
}
}
const encrypt = (size, { [KEYOBJECT]: keyObject }, cleartext, { iv, aad = Buffer.alloc(0) }) => {
const key = asInput(keyObject, false)
checkInput(size, iv)
const cipher = createCipheriv(`aes-${size}-gcm`, key, iv, { authTagLength: 16 })
cipher.setAAD(aad)
const ciphertext = Buffer.concat([cipher.update(cleartext), cipher.final()])
const tag = cipher.getAuthTag()
return { ciphertext, tag }
}
const decrypt = (size, { [KEYOBJECT]: keyObject }, ciphertext, { iv, tag = Buffer.alloc(0), aad = Buffer.alloc(0) }) => {
const key = asInput(keyObject, false)
checkInput(size, iv, tag)
try {
const cipher = createDecipheriv(`aes-${size}-gcm`, key, iv, { authTagLength: 16 })
cipher.setAuthTag(tag)
cipher.setAAD(aad)
return Buffer.concat([cipher.update(ciphertext), cipher.final()])
} catch (err) {
throw new JWEDecryptionFailed()
}
}
module.exports = (JWA, JWK) => {
['A128GCM', 'A192GCM', 'A256GCM'].forEach((jwaAlg) => {
const size = parseInt(jwaAlg.substr(1, 3), 10)
if (getCiphers().includes(`aes-${size}-gcm`)) {
JWA.encrypt.set(jwaAlg, encrypt.bind(undefined, size))
JWA.decrypt.set(jwaAlg, decrypt.bind(undefined, size))
JWK.oct.encrypt[jwaAlg] = JWK.oct.decrypt[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length === size
}
})
}
+24
View File
@@ -0,0 +1,24 @@
const generateIV = require('../help/generate_iv')
const base64url = require('../help/base64url')
module.exports = (JWA, JWK) => {
['A128GCMKW', 'A192GCMKW', 'A256GCMKW'].forEach((jwaAlg) => {
const encAlg = jwaAlg.substr(0, 7)
const size = parseInt(jwaAlg.substr(1, 3), 10)
const encrypt = JWA.encrypt.get(encAlg)
const decrypt = JWA.decrypt.get(encAlg)
if (encrypt && decrypt) {
JWA.keyManagementEncrypt.set(jwaAlg, (key, payload) => {
const iv = generateIV(jwaAlg)
const { ciphertext, tag } = encrypt(key, payload, { iv })
return {
wrapped: ciphertext,
header: { tag: base64url.encodeBuffer(tag), iv: base64url.encodeBuffer(iv) }
}
})
JWA.keyManagementDecrypt.set(jwaAlg, decrypt)
JWK.oct.wrapKey[jwaAlg] = JWK.oct.unwrapKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length === size
}
})
}
+37
View File
@@ -0,0 +1,37 @@
const { createCipheriv, createDecipheriv, getCiphers } = require('crypto')
const { KEYOBJECT } = require('../help/consts')
const { asInput } = require('../help/key_object')
const checkInput = (data) => {
if (data !== undefined && data.length % 8 !== 0) {
throw new Error('invalid data length')
}
}
const wrapKey = (alg, { [KEYOBJECT]: keyObject }, payload) => {
const key = asInput(keyObject, false)
const cipher = createCipheriv(alg, key, Buffer.alloc(8, 'a6', 'hex'))
return { wrapped: Buffer.concat([cipher.update(payload), cipher.final()]) }
}
const unwrapKey = (alg, { [KEYOBJECT]: keyObject }, payload) => {
const key = asInput(keyObject, false)
checkInput(payload)
const cipher = createDecipheriv(alg, key, Buffer.alloc(8, 'a6', 'hex'))
return Buffer.concat([cipher.update(payload), cipher.final()])
}
module.exports = (JWA, JWK) => {
['A128KW', 'A192KW', 'A256KW'].forEach((jwaAlg) => {
const size = parseInt(jwaAlg.substr(1, 3), 10)
const alg = `aes${size}-wrap`
if (getCiphers().includes(alg)) {
JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, alg))
JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, alg))
JWK.oct.wrapKey[jwaAlg] = JWK.oct.unwrapKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length === size
}
})
}
+43
View File
@@ -0,0 +1,43 @@
const { improvedDH } = require('../../help/runtime_support')
if (improvedDH) {
const { diffieHellman } = require('crypto')
const { KeyObject } = require('../../help/key_object')
const importKey = require('../../jwk/import')
module.exports = ({ keyObject: privateKey }, publicKey) => {
if (!(publicKey instanceof KeyObject)) {
({ keyObject: publicKey } = importKey(publicKey))
}
return diffieHellman({ privateKey, publicKey })
}
} else {
const { createECDH, constants: { POINT_CONVERSION_UNCOMPRESSED } } = require('crypto')
const base64url = require('../../help/base64url')
const crvToCurve = (crv) => {
switch (crv) {
case 'P-256':
return 'prime256v1'
case 'P-384':
return 'secp384r1'
case 'P-521':
return 'secp521r1'
}
}
const UNCOMPRESSED = Buffer.alloc(1, POINT_CONVERSION_UNCOMPRESSED)
const pubToBuffer = (x, y) => Buffer.concat([UNCOMPRESSED, base64url.decodeToBuffer(x), base64url.decodeToBuffer(y)])
module.exports = ({ crv, d }, { x, y }) => {
const curve = crvToCurve(crv)
const exchange = createECDH(curve)
exchange.setPrivateKey(base64url.decodeToBuffer(d))
return exchange.computeSecret(pubToBuffer(x, y))
}
}
+40
View File
@@ -0,0 +1,40 @@
const { createHash } = require('crypto')
const ecdhComputeSecret = require('./compute_secret')
const concat = (key, length, value) => {
const iterations = Math.ceil(length / 32)
let res
for (let iter = 1; iter <= iterations; iter++) {
const buf = Buffer.allocUnsafe(4 + key.length + value.length)
buf.writeUInt32BE(iter, 0)
key.copy(buf, 4)
value.copy(buf, 4 + key.length)
if (!res) {
res = createHash('sha256').update(buf).digest()
} else {
res = Buffer.concat([res, createHash('sha256').update(buf).digest()])
}
}
return res.slice(0, length)
}
const uint32be = (value, buf = Buffer.allocUnsafe(4)) => {
buf.writeUInt32BE(value)
return buf
}
const lengthAndInput = input => Buffer.concat([uint32be(input.length), input])
module.exports = (alg, keyLen, privKey, pubKey, { apu = Buffer.alloc(0), apv = Buffer.alloc(0) } = {}, computeSecret = ecdhComputeSecret) => {
const value = Buffer.concat([
lengthAndInput(Buffer.from(alg)),
lengthAndInput(apu),
lengthAndInput(apv),
uint32be(keyLen)
])
const sharedSecret = computeSecret(privKey, pubKey)
return concat(sharedSecret, keyLen / 8, value)
}
+31
View File
@@ -0,0 +1,31 @@
const { improvedDH } = require('../../help/runtime_support')
const { KEYLENGTHS } = require('../../registry')
const { generateSync } = require('../../jwk/generate')
const derive = require('./derive')
const wrapKey = (key, payload, { enc }) => {
const epk = generateSync(key.kty, key.crv)
const derivedKey = derive(enc, KEYLENGTHS.get(enc), epk, key)
return {
wrapped: derivedKey,
header: { epk: { kty: key.kty, crv: key.crv, x: epk.x, y: epk.y } }
}
}
const unwrapKey = (key, payload, header) => {
const { enc, epk } = header
return derive(enc, KEYLENGTHS.get(enc), key, epk, header)
}
module.exports = (JWA, JWK) => {
JWA.keyManagementEncrypt.set('ECDH-ES', wrapKey)
JWA.keyManagementDecrypt.set('ECDH-ES', unwrapKey)
JWK.EC.deriveKey['ECDH-ES'] = key => (key.use === 'enc' || key.use === undefined) && key.crv !== 'secp256k1'
if (improvedDH) {
JWK.OKP.deriveKey['ECDH-ES'] = key => (key.use === 'enc' || key.use === undefined) && key.keyObject.asymmetricKeyType.startsWith('x')
}
}
+47
View File
@@ -0,0 +1,47 @@
const { improvedDH } = require('../../help/runtime_support')
const { KEYOBJECT } = require('../../help/consts')
const { generateSync } = require('../../jwk/generate')
const { ECDH_DERIVE_LENGTHS } = require('../../registry')
const derive = require('./derive')
const wrapKey = (wrap, derive, key, payload) => {
const epk = generateSync(key.kty, key.crv)
const derivedKey = derive(epk, key, payload)
const result = wrap({ [KEYOBJECT]: derivedKey }, payload)
result.header = result.header || {}
Object.assign(result.header, { epk: { kty: key.kty, crv: key.crv, x: epk.x, y: epk.y } })
return result
}
const unwrapKey = (unwrap, derive, key, payload, header) => {
const { epk } = header
const derivedKey = derive(key, epk, header)
return unwrap({ [KEYOBJECT]: derivedKey }, payload, header)
}
module.exports = (JWA, JWK) => {
['ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW'].forEach((jwaAlg) => {
const kw = jwaAlg.substr(-6)
const kwWrap = JWA.keyManagementEncrypt.get(kw)
const kwUnwrap = JWA.keyManagementDecrypt.get(kw)
const keylen = parseInt(jwaAlg.substr(9, 3), 10)
ECDH_DERIVE_LENGTHS.set(jwaAlg, keylen)
if (kwWrap && kwUnwrap) {
JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, kwWrap, derive.bind(undefined, jwaAlg, keylen)))
JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, kwUnwrap, derive.bind(undefined, jwaAlg, keylen)))
JWK.EC.deriveKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.crv !== 'secp256k1'
if (improvedDH) {
JWK.OKP.deriveKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.keyObject.asymmetricKeyType.startsWith('x')
}
}
})
}
module.exports.wrapKey = wrapKey
module.exports.unwrapKey = unwrapKey
+77
View File
@@ -0,0 +1,77 @@
const { sign: signOneShot, verify: verifyOneShot, createSign, createVerify, getCurves } = require('crypto')
const { derToJose, joseToDer } = require('../help/ecdsa_signatures')
const { KEYOBJECT } = require('../help/consts')
const resolveNodeAlg = require('../help/node_alg')
const { asInput } = require('../help/key_object')
const { dsaEncodingSupported } = require('../help/runtime_support')
let sign, verify
if (dsaEncodingSupported) {
sign = (jwaAlg, nodeAlg, { [KEYOBJECT]: keyObject }, payload) => {
if (typeof payload === 'string') {
payload = Buffer.from(payload)
}
return signOneShot(nodeAlg, payload, { key: asInput(keyObject, false), dsaEncoding: 'ieee-p1363' })
}
verify = (jwaAlg, nodeAlg, { [KEYOBJECT]: keyObject }, payload, signature) => {
try {
return verifyOneShot(nodeAlg, payload, { key: asInput(keyObject, true), dsaEncoding: 'ieee-p1363' }, signature)
} catch (err) {
return false
}
}
} else {
sign = (jwaAlg, nodeAlg, { [KEYOBJECT]: keyObject }, payload) => {
return derToJose(createSign(nodeAlg).update(payload).sign(asInput(keyObject, false)), jwaAlg)
}
verify = (jwaAlg, nodeAlg, { [KEYOBJECT]: keyObject }, payload, signature) => {
try {
return createVerify(nodeAlg).update(payload).verify(asInput(keyObject, true), joseToDer(signature, jwaAlg))
} catch (err) {
return false
}
}
}
const crvToAlg = (crv) => {
switch (crv) {
case 'P-256':
return 'ES256'
case 'secp256k1':
return 'ES256K'
case 'P-384':
return 'ES384'
case 'P-521':
return 'ES512'
}
}
module.exports = (JWA, JWK) => {
const algs = []
if (getCurves().includes('prime256v1')) {
algs.push('ES256')
}
if (getCurves().includes('secp256k1')) {
algs.push('ES256K')
}
if (getCurves().includes('secp384r1')) {
algs.push('ES384')
}
if (getCurves().includes('secp521r1')) {
algs.push('ES512')
}
algs.forEach((jwaAlg) => {
const nodeAlg = resolveNodeAlg(jwaAlg)
JWA.sign.set(jwaAlg, sign.bind(undefined, jwaAlg, nodeAlg))
JWA.verify.set(jwaAlg, verify.bind(undefined, jwaAlg, nodeAlg))
JWK.EC.sign[jwaAlg] = key => key.private && JWK.EC.verify[jwaAlg](key)
JWK.EC.verify[jwaAlg] = key => (key.use === 'sig' || key.use === undefined) && crvToAlg(key.crv) === jwaAlg
})
}
+24
View File
@@ -0,0 +1,24 @@
const { sign: signOneShot, verify: verifyOneShot } = require('crypto')
const { KEYOBJECT } = require('../help/consts')
const { edDSASupported } = require('../help/runtime_support')
const sign = ({ [KEYOBJECT]: keyObject }, payload) => {
if (typeof payload === 'string') {
payload = Buffer.from(payload)
}
return signOneShot(undefined, payload, keyObject)
}
const verify = ({ [KEYOBJECT]: keyObject }, payload, signature) => {
return verifyOneShot(undefined, payload, keyObject, signature)
}
module.exports = (JWA, JWK) => {
if (edDSASupported) {
JWA.sign.set('EdDSA', sign)
JWA.verify.set('EdDSA', verify)
JWK.OKP.sign.EdDSA = key => key.private && JWK.OKP.verify.EdDSA(key)
JWK.OKP.verify.EdDSA = key => (key.use === 'sig' || key.use === undefined) && key.keyObject.asymmetricKeyType.startsWith('ed')
}
}
+28
View File
@@ -0,0 +1,28 @@
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, asInput(keyObject, false))
hmac.update(payload)
return hmac.digest()
}
const verify = (jwaAlg, hmacAlg, key, payload, signature) => {
const expected = sign(jwaAlg, hmacAlg, key, payload)
const actual = signature
return timingSafeEqual(actual, expected)
}
module.exports = (JWA, JWK) => {
['HS256', 'HS384', 'HS512'].forEach((jwaAlg) => {
const hmacAlg = resolveNodeAlg(jwaAlg)
JWA.sign.set(jwaAlg, sign.bind(undefined, jwaAlg, hmacAlg))
JWA.verify.set(jwaAlg, verify.bind(undefined, jwaAlg, hmacAlg))
JWK.oct.sign[jwaAlg] = JWK.oct.verify[jwaAlg] = key => key.use === 'sig' || key.use === undefined
})
}
+88
View File
@@ -0,0 +1,88 @@
const { JWKKeySupport, JOSENotSupported } = require('../errors')
const { KEY_MANAGEMENT_ENCRYPT, KEY_MANAGEMENT_DECRYPT } = require('../help/consts')
const { JWA, JWK } = require('../registry')
// sign, verify
require('./hmac')(JWA, JWK)
require('./ecdsa')(JWA, JWK)
require('./eddsa')(JWA, JWK)
require('./rsassa_pss')(JWA, JWK)
require('./rsassa')(JWA, JWK)
require('./none')(JWA)
// encrypt, decrypt
require('./aes_cbc_hmac_sha2')(JWA, JWK)
require('./aes_gcm')(JWA, JWK)
// wrapKey, unwrapKey
require('./rsaes')(JWA, JWK)
require('./aes_kw')(JWA, JWK)
require('./aes_gcm_kw')(JWA, JWK)
// deriveKey
require('./pbes2')(JWA, JWK)
require('./ecdh/dir')(JWA, JWK)
require('./ecdh/kw')(JWA, JWK)
const check = (key, op, alg) => {
const cache = `_${op}_${alg}`
let label
let keyOp
if (op === 'keyManagementEncrypt') {
label = 'key management (encryption)'
keyOp = KEY_MANAGEMENT_ENCRYPT
} else if (op === 'keyManagementDecrypt') {
label = 'key management (decryption)'
keyOp = KEY_MANAGEMENT_DECRYPT
}
if (cache in key) {
if (key[cache]) {
return
}
throw new JWKKeySupport(`the key does not support ${alg} ${label || op} algorithm`)
}
let value = true
if (!JWA[op].has(alg)) {
throw new JOSENotSupported(`unsupported ${label || op} alg: ${alg}`)
} else if (!key.algorithms(keyOp).has(alg)) {
value = false
}
Object.defineProperty(key, cache, { value, enumerable: false })
if (!value) {
return check(key, op, alg)
}
}
module.exports = {
check,
sign: (alg, key, payload) => {
check(key, 'sign', alg)
return JWA.sign.get(alg)(key, payload)
},
verify: (alg, key, payload, signature) => {
check(key, 'verify', alg)
return JWA.verify.get(alg)(key, payload, signature)
},
keyManagementEncrypt: (alg, key, payload, opts) => {
check(key, 'keyManagementEncrypt', alg)
return JWA.keyManagementEncrypt.get(alg)(key, payload, opts)
},
keyManagementDecrypt: (alg, key, payload, opts) => {
check(key, 'keyManagementDecrypt', alg)
return JWA.keyManagementDecrypt.get(alg)(key, payload, opts)
},
encrypt: (alg, key, cleartext, opts) => {
check(key, 'encrypt', alg)
return JWA.encrypt.get(alg)(key, cleartext, opts)
},
decrypt: (alg, key, ciphertext, opts) => {
check(key, 'decrypt', alg)
return JWA.decrypt.get(alg)(key, ciphertext, opts)
}
}
+7
View File
@@ -0,0 +1,7 @@
const sign = () => Buffer.from('')
const verify = (key, payload, signature) => !signature.length
module.exports = (JWA, JWK) => {
JWA.sign.set('none', sign)
JWA.verify.set('none', verify)
}
+56
View File
@@ -0,0 +1,56 @@
const { pbkdf2Sync: pbkdf2, randomBytes } = require('crypto')
const { KEYOBJECT } = require('../help/consts')
const base64url = require('../help/base64url')
const SALT_LENGTH = 16
const NULL_BUFFER = Buffer.alloc(1, 0)
const concatSalt = (alg, p2s) => {
return Buffer.concat([
Buffer.from(alg, 'utf8'),
NULL_BUFFER,
p2s
])
}
const wrapKey = (keylen, sha, concat, wrap, { [KEYOBJECT]: keyObject }, payload) => {
// Note that if password-based encryption is used for multiple
// recipients, it is expected that each recipient use different values
// for the PBES2 parameters "p2s" and "p2c".
// here we generate p2c between 2048 and 4096 and random p2s
const p2c = Math.floor((Math.random() * 2049) + 2048)
const p2s = randomBytes(SALT_LENGTH)
const salt = concat(p2s)
const derivedKey = pbkdf2(keyObject.export(), salt, p2c, keylen, sha)
const result = wrap({ [KEYOBJECT]: derivedKey }, payload)
result.header = result.header || {}
Object.assign(result.header, { p2c, p2s: base64url.encodeBuffer(p2s) })
return result
}
const unwrapKey = (keylen, sha, concat, unwrap, { [KEYOBJECT]: keyObject }, payload, header) => {
const { p2s, p2c } = header
const salt = concat(p2s)
const derivedKey = pbkdf2(keyObject.export(), salt, p2c, keylen, sha)
return unwrap({ [KEYOBJECT]: derivedKey }, payload, header)
}
module.exports = (JWA, JWK) => {
['PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW'].forEach((jwaAlg) => {
const kw = jwaAlg.substr(-6)
const kwWrap = JWA.keyManagementEncrypt.get(kw)
const kwUnwrap = JWA.keyManagementDecrypt.get(kw)
const keylen = parseInt(jwaAlg.substr(13, 3), 10) / 8
const sha = `sha${jwaAlg.substr(8, 3)}`
if (kwWrap && kwUnwrap) {
JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, keylen, sha, concatSalt.bind(undefined, jwaAlg), kwWrap))
JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, keylen, sha, concatSalt.bind(undefined, jwaAlg), kwUnwrap))
JWK.oct.deriveKey[jwaAlg] = key => key.use === 'enc' || key.use === undefined
}
})
}
+67
View File
@@ -0,0 +1,67 @@
const { publicEncrypt, privateDecrypt, constants } = require('crypto')
const { oaepHashSupported } = require('../help/runtime_support')
const { KEYOBJECT } = require('../help/consts')
const { asInput } = require('../help/key_object')
const resolvePadding = (alg) => {
switch (alg) {
case 'RSA-OAEP':
case 'RSA-OAEP-256':
case 'RSA-OAEP-384':
case 'RSA-OAEP-512':
return constants.RSA_PKCS1_OAEP_PADDING
case 'RSA1_5':
return constants.RSA_PKCS1_PADDING
}
}
const resolveOaepHash = (alg) => {
switch (alg) {
case 'RSA-OAEP':
return 'sha1'
case 'RSA-OAEP-256':
return 'sha256'
case 'RSA-OAEP-384':
return 'sha384'
case 'RSA-OAEP-512':
return 'sha512'
default:
return undefined
}
}
const wrapKey = (padding, oaepHash, { [KEYOBJECT]: keyObject }, payload) => {
const key = asInput(keyObject, true)
return { wrapped: publicEncrypt({ key, oaepHash, padding }, payload) }
}
const unwrapKey = (padding, oaepHash, { [KEYOBJECT]: keyObject }, payload) => {
const key = asInput(keyObject, false)
return privateDecrypt({ key, oaepHash, padding }, payload)
}
const LENGTHS = {
RSA1_5: 0,
'RSA-OAEP': 592,
'RSA-OAEP-256': 784,
'RSA-OAEP-384': 1040,
'RSA-OAEP-512': 1296
}
module.exports = (JWA, JWK) => {
const algs = ['RSA-OAEP', 'RSA1_5']
if (oaepHashSupported) {
algs.splice(1, 0, 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512')
}
algs.forEach((jwaAlg) => {
const padding = resolvePadding(jwaAlg)
const oaepHash = resolveOaepHash(jwaAlg)
JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, padding, oaepHash))
JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, padding, oaepHash))
JWK.RSA.wrapKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length >= LENGTHS[jwaAlg]
JWK.RSA.unwrapKey[jwaAlg] = key => key.private && (key.use === 'enc' || key.use === undefined) && key.length >= LENGTHS[jwaAlg]
})
}
+29
View File
@@ -0,0 +1,29 @@
const { createSign, createVerify } = require('crypto')
const { KEYOBJECT } = require('../help/consts')
const resolveNodeAlg = require('../help/node_alg')
const { asInput } = require('../help/key_object')
const sign = (nodeAlg, { [KEYOBJECT]: keyObject }, payload) => {
return createSign(nodeAlg).update(payload).sign(asInput(keyObject, false))
}
const verify = (nodeAlg, { [KEYOBJECT]: keyObject }, payload, signature) => {
return createVerify(nodeAlg).update(payload).verify(asInput(keyObject, true), signature)
}
const LENGTHS = {
RS256: 0,
RS384: 624,
RS512: 752
}
module.exports = (JWA, JWK) => {
['RS256', 'RS384', 'RS512'].forEach((jwaAlg) => {
const nodeAlg = resolveNodeAlg(jwaAlg)
JWA.sign.set(jwaAlg, sign.bind(undefined, nodeAlg))
JWA.verify.set(jwaAlg, verify.bind(undefined, nodeAlg))
JWK.RSA.sign[jwaAlg] = key => key.private && JWK.RSA.verify[jwaAlg](key)
JWK.RSA.verify[jwaAlg] = key => (key.use === 'sig' || key.use === undefined) && key.length >= LENGTHS[jwaAlg]
})
}
+43
View File
@@ -0,0 +1,43 @@
const {
createSign,
createVerify,
constants
} = require('crypto')
const { KEYOBJECT } = require('../help/consts')
const resolveNodeAlg = require('../help/node_alg')
const { asInput } = require('../help/key_object')
const sign = (nodeAlg, { [KEYOBJECT]: keyObject }, payload) => {
const key = asInput(keyObject, false)
return createSign(nodeAlg).update(payload).sign({
key,
padding: constants.RSA_PKCS1_PSS_PADDING,
saltLength: constants.RSA_PSS_SALTLEN_DIGEST
})
}
const verify = (nodeAlg, { [KEYOBJECT]: keyObject }, payload, signature) => {
const key = asInput(keyObject, true)
return createVerify(nodeAlg).update(payload).verify({
key,
padding: constants.RSA_PKCS1_PSS_PADDING,
saltLength: constants.RSA_PSS_SALTLEN_DIGEST
}, signature)
}
const LENGTHS = {
PS256: 528,
PS384: 784,
PS512: 1040
}
module.exports = (JWA, JWK) => {
['PS256', 'PS384', 'PS512'].forEach((jwaAlg) => {
const nodeAlg = resolveNodeAlg(jwaAlg)
JWA.sign.set(jwaAlg, sign.bind(undefined, nodeAlg))
JWA.verify.set(jwaAlg, verify.bind(undefined, nodeAlg))
JWK.RSA.sign[jwaAlg] = key => key.private && JWK.RSA.verify[jwaAlg](key)
JWK.RSA.verify[jwaAlg] = key => (key.use === 'sig' || key.use === undefined) && key.length >= LENGTHS[jwaAlg]
})
}