Skip to content

Commit

Permalink
perf: faster V1 encrypt/decrypt by grouping the crypto worker operations
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Apr 21, 2020
1 parent f113782 commit dbbdfa6
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 63 deletions.
70 changes: 62 additions & 8 deletions lib/help/crypto_worker.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
const { parentPort, Worker, isMainThread } = require('worker_threads')
const crypto = require('crypto')

if (isMainThread) {
const crypto = require('crypto')

const tasks = new Map()
const exportArgs = {
public: [{ format: 'der', type: 'spki' }],
Expand Down Expand Up @@ -62,22 +61,77 @@ if (isMainThread) {
module.exports = {
sign: a.bind(undefined, 'sign'),
verify: a.bind(undefined, 'verify'),
encrypt: a.bind(undefined, 'encrypt'),
decrypt: a.bind(undefined, 'decrypt'),
hmac: a.bind(undefined, 'hmac'),
'aes-256-ctr-hmac-sha-384-encrypt': a.bind(undefined, 'aes-256-ctr-hmac-sha-384-encrypt'),
'aes-256-ctr-hmac-sha-384-decrypt': a.bind(undefined, 'aes-256-ctr-hmac-sha-384-decrypt'),
'xchacha20-poly1305-encrypt': a.bind(undefined, 'xchacha20-poly1305-encrypt'),
'xchacha20-poly1305-decrypt': a.bind(undefined, 'xchacha20-poly1305-decrypt')
}
/* c8 ignore next 58 */
} else {
const crypto = require('crypto')
const sodium = require('libsodium-wrappers')

const pae = require('./pae')
const hkdf = (key, length, salt, info) => {
const prk = methods.hmac('sha384', key, salt)

const u = Buffer.from(info)

let t = Buffer.from('')
let lb = Buffer.from('')
let i

for (let bi = 1; Buffer.byteLength(t) < length; ++i) {
i = Buffer.from(String.fromCharCode(bi))
const inp = Buffer.concat([lb, u, i])

lb = methods.hmac('sha384', inp, prk)
t = Buffer.concat([t, lb])
}

const orm = Buffer.from(t).slice(0, length)
return orm
}

const pack = require('./pack')
const timingSafeEqual = require('./timing_safe_equal')

const methods = {
hmac (alg, payload, ab) {
const key = Buffer.from(ab)
'aes-256-ctr-hmac-sha-384-encrypt' (m, f, k, nonce) {
let n = methods.hmac('sha384', m, nonce)
n = n.slice(0, 32)
f = Buffer.from(f)

const salt = n.slice(0, 16)
const ek = hkdf(k, 32, salt, 'paseto-encryption-key')
const ak = hkdf(k, 32, salt, 'paseto-auth-key-for-aead')

const c = methods.encrypt('aes-256-ctr', m, ek, n.slice(16))
const preAuth = pae('v1.local.', n, c, f)
const t = methods.hmac('sha384', preAuth, ak)

return pack('v1.local.', [n, c, t], f)
},
'aes-256-ctr-hmac-sha-384-decrypt' (raw, f, k) {
const n = raw.slice(0, 32)
const t = raw.slice(-48)
const c = raw.slice(32, -48)

const salt = n.slice(0, 16)
const ek = hkdf(k, 32, salt, 'paseto-encryption-key')
const ak = hkdf(k, 32, salt, 'paseto-auth-key-for-aead')

const preAuth = pae('v1.local.', n, c, f)

const t2 = methods.hmac('sha384', preAuth, ak)
const payload = methods.decrypt('aes-256-ctr', c, ek, n.slice(16))

if (!timingSafeEqual(t, t2) || !payload) {
return false
}

return payload
},
hmac (alg, payload, key) {
const hmac = crypto.createHmac(alg, key)
hmac.update(payload)
return hmac.digest()
Expand Down
22 changes: 0 additions & 22 deletions lib/help/hkdf.js

This file was deleted.

19 changes: 3 additions & 16 deletions lib/v1/decrypt.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
const { decode } = require('../help/base64url')
const { hmac, decrypt } = require('../help/crypto_worker')
const { 'aes-256-ctr-hmac-sha-384-decrypt': decrypt } = require('../help/crypto_worker')
const { PasetoDecryptionFailed, PasetoInvalid } = require('../errors')
const assertPayload = require('../help/assert_payload')
const checkKey = require('../help/symmetric_key_check').bind(undefined, 'v1.local')
const hkdf = require('../help/hkdf')
const pae = require('../help/pae')
const parse = require('../help/parse_paseto_payload')
const timingSafeEqual = require('../help/timing_safe_equal')

const h = 'v1.local.'

Expand All @@ -28,20 +25,10 @@ module.exports = async function v1Decrypt (token, key, { complete = false, ...op

const f = decode(b64f)
const raw = decode(b64)
const n = raw.slice(0, 32)
const t = raw.slice(-48)
const c = raw.slice(32, -48)

const k = key.export()
const salt = n.slice(0, 16)
const ek = await hkdf(k, 32, salt, 'paseto-encryption-key')
const ak = await hkdf(k, 32, salt, 'paseto-auth-key-for-aead')

const preAuth = pae(h, n, c, f)
const t2 = await hmac('sha384', preAuth, ak)

let payload = await decrypt('aes-256-ctr', c, ek, n.slice(16))
if (!timingSafeEqual(t, t2) || !payload) {
let payload = await decrypt(raw, f, k)
if (!payload) {
throw new PasetoDecryptionFailed('decryption failed')
}

Expand Down
19 changes: 2 additions & 17 deletions lib/v1/encrypt.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ const applyOptions = require('../help/apply_options')
const checkFooter = require('../help/check_footer')
const checkKey = require('../help/symmetric_key_check').bind(undefined, 'v1.local')
const checkPayload = require('../help/check_payload')
const hkdf = require('../help/hkdf')
const pack = require('../help/pack')
const pae = require('../help/pae')
const randomBytes = require('../help/random_bytes')
const { hmac, encrypt } = require('../help/crypto_worker')
const { 'aes-256-ctr-hmac-sha-384-encrypt': encrypt } = require('../help/crypto_worker')

module.exports = async function v1Encrypt (payload, key, { footer, nonce, ...options } = {}) {
payload = checkPayload(payload)
Expand All @@ -15,23 +12,11 @@ module.exports = async function v1Encrypt (payload, key, { footer, nonce, ...opt
payload = applyOptions(options, payload)

const m = Buffer.from(JSON.stringify(payload), 'utf8')
const h = 'v1.local.'
const k = key.export()

if ((nonce && process.env.NODE_ENV !== 'test') || !nonce) {
nonce = await randomBytes(32)
}

let n = await hmac('sha384', m, nonce)
n = n.slice(0, 32)

const salt = n.slice(0, 16)
const ek = await hkdf(k, 32, salt, 'paseto-encryption-key')
const ak = await hkdf(k, 32, salt, 'paseto-auth-key-for-aead')

const c = await encrypt('aes-256-ctr', m, ek, n.slice(16))
const preAuth = pae(h, n, c, f)
const t = await hmac('sha384', preAuth, ak)

return pack(h, [n, c, t], f)
return encrypt(m, f, k, nonce)
}

0 comments on commit dbbdfa6

Please sign in to comment.