ara-crypto
Version:
Cryptographic functions used in Ara modules
178 lines (147 loc) • 3.85 kB
JavaScript
const isBuffer = require('is-buffer')
/* eslint-disable camelcase */
const {
crypto_kdf_CONTEXTBYTES,
crypto_kdf_KEYBYTES,
crypto_kdf_derive_from_key,
crypto_kdf_keygen,
} = require('./sodium')
/**
* Generates a master key.
*
* @public
* @param {?(Buffer)} [key]
* @returns {Buffer}
* @throws TypeError
*/
function keygen(key) {
if (key && false === isBuffer(key)) {
throw new TypeError('kdf.keygen: Expecting key to be a buffer.')
}
if (undefined === key) {
key = Buffer.allocUnsafe(crypto_kdf_KEYBYTES)
}
if (crypto_kdf_KEYBYTES !== key.length) {
throw new TypeError(`kdf.keygen: Invalid key length: ${key.length}`)
}
crypto_kdf_keygen(key)
return key
}
/**
* Initializes key derivation.
*
* @public
* @param {Buffer} key
* @param {?(Buffer)} [buffer]
* @return {Object}
* @throws TypeError
*/
function init(key, buffer) {
if (key && false === isBuffer(key)) {
throw new TypeError('kdf.init: Expecting key to be a buffer.')
}
if (undefined === key) {
throw new TypeError('kdf.init: Expecting key to be defined.')
}
if (buffer) {
if (false === isBuffer(buffer)) {
throw new TypeError('kdf.init: Expecting context to be a buffer.')
}
if (crypto_kdf_CONTEXTBYTES !== buffer.length) {
throw new TypeError(`kdf.init: Invalid context length: ${buffer.length}`)
}
}
return {
buffer: buffer || Buffer.alloc(crypto_kdf_CONTEXTBYTES),
subkey: null,
key
}
}
/**
* Updates the subkey in the context object.
*
* @public
* @param {Object} ctx
* @param {Number} id
* @return {Buffer}
* @throws TypeError
*/
function update(ctx, id) {
if ('object' !== typeof ctx) {
throw new TypeError('kdf.update: Expecting ctx to be an object.')
}
if (ctx.subkey && !isBuffer(ctx.subkey)) {
throw new TypeError('kdf.update: Expecting ctx.subkey to be a buffer.')
}
if (!isBuffer(ctx.buffer)) {
throw new TypeError('kdf.update: Expecting ctx.buffer to be a buffer.')
}
if (crypto_kdf_CONTEXTBYTES !== ctx.buffer.length) {
throw new TypeError(`kdf.update: Invalid buffer length: ${ctx.buffer.length}.`)
}
if (!isBuffer(ctx.key)) {
throw new TypeError('kdf.update: Expecting ctx.key to be a buffer.')
}
if ('number' !== typeof id) {
throw new TypeError('kdf.update: Expecting id to be a number.')
}
if (id < 0 || id > (2 ** 64) - 1) {
throw new TypeError('kdf.update: Expecting id to be between 0 and (2^64)-1.')
}
ctx.subkey = ctx.subkey || Buffer.allocUnsafe(crypto_kdf_KEYBYTES)
crypto_kdf_derive_from_key(
ctx.subkey,
id, ctx.buffer,
ctx.key.slice(0, crypto_kdf_KEYBYTES)
)
return ctx.subkey
}
/**
* Final step to null the original context subkey.
*
* @public
* @param {Object} ctx
* @return {Buffer}
* @throws TypeError
*/
function final(ctx) {
if ('object' !== typeof ctx) {
throw new TypeError('kdf.final: Expecting ctx to be an object.')
}
const { subkey } = ctx
ctx.subkey = null
return subkey
}
/**
* Derives a subkey using the master key and context.
*
* @public
* @param {Buffer} key
* @param {Number} iterations
* @param {?(Buffer)} [buffer]
* @return {Buffer}
* @throws TypeError
*/
function derive(key, iterations, buffer) {
const ctx = init(key, buffer)
if (iterations && 'number' !== typeof iterations) {
throw new TypeError('kdf.derive: Expecting subkeyId to be a number.')
}
if (iterations < 1 || iterations > (2 ** 64) - 1) {
throw new TypeError('kdf.derive: Expecting iterations to be between 1 and (2^64)-1.')
}
if (undefined === iterations) {
throw new TypeError('kdf.derive: Expecting iterations to be defined.')
}
for (let i = 0; i < iterations; ++i) {
update(ctx, i + 1)
}
return final(ctx)
}
module.exports = {
derive,
keygen,
update,
final,
init,
}