mirror of
https://github.com/Azure/k8s-set-context.git
synced 2026-06-26 16:59:27 +08:00
add node modules
This commit is contained in:
+74
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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]
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user