Skip to content

Commit

Permalink
fix: use native openssl AES Key Wrap 🤦
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Apr 15, 2020
1 parent 447f2be commit dcf8d75
Showing 1 changed file with 5 additions and 66 deletions.
71 changes: 5 additions & 66 deletions lib/jwa/aes_kw.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
const { createCipheriv, createDecipheriv, getCiphers } = 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')

Expand All @@ -13,84 +11,25 @@ const checkInput = (data) => {

const IV = Buffer.alloc(8, 'a6', 'hex')

const xor = (a, b) => {
const len = Math.max(a.length, b.length)
const result = Buffer.alloc(len)
for (let idx = 0; len > idx; idx++) {
result[idx] = (a[idx] || 0) ^ (b[idx] || 0)
}

return result
}

const split = (input, size) => {
const output = []
for (let idx = 0; input.length > idx; idx += size) {
output.push(input.slice(idx, idx + size))
}
return output
}

const wrapKey = (size, { [KEYOBJECT]: keyObject }, payload) => {
const key = asInput(keyObject, false)
const iv = Buffer.alloc(16)
let R = split(payload, 8)
let A
let B
let count
A = IV
for (let jdx = 0; jdx < 6; jdx++) {
for (let idx = 0; R.length > idx; idx++) {
count = (R.length * jdx) + idx + 1
const cipher = createCipheriv(`aes${size}`, key, iv)
B = Buffer.concat([A, R[idx]])
B = cipher.update(B)
const cipher = createCipheriv(`aes${size}-wrap`, key, IV)

A = xor(B.slice(0, 8), uint64be(count))
R[idx] = B.slice(8, 16)
}
}
R = [A].concat(R)

return { wrapped: Buffer.concat(R) }
return { wrapped: Buffer.concat([cipher.update(payload), cipher.final()]) }
}

const unwrapKey = (size, { [KEYOBJECT]: keyObject }, payload) => {
const key = asInput(keyObject, false)
checkInput(payload)
const cipher = createDecipheriv(`aes${size}-wrap`, key, IV)

const iv = Buffer.alloc(16)

let R = split(payload, 8)
let A
let B
let count
A = R[0]
R = R.slice(1)
for (let jdx = 5; jdx >= 0; --jdx) {
for (let idx = R.length - 1; idx >= 0; --idx) {
count = (R.length * jdx) + idx + 1
B = xor(A, uint64be(count))
B = Buffer.concat([B, R[idx], iv])
const cipher = createDecipheriv(`aes${size}`, key, iv)
B = cipher.update(B)

A = B.slice(0, 8)
R[idx] = B.slice(8, 16)
}
}

if (!timingSafeEqual(IV, A)) {
throw new Error('unwrap failed')
}

return Buffer.concat(R)
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)
if (getCiphers().includes(`aes${size}`)) {
if (getCiphers().includes(`aes${size}-wrap`)) {
JWA.keyManagementEncrypt.set(jwaAlg, wrapKey.bind(undefined, size))
JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, size))
JWK.oct.wrapKey[jwaAlg] = JWK.oct.unwrapKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.length === size
Expand Down

0 comments on commit dcf8d75

Please sign in to comment.