Source: common/crypto.js

'use strict';
/**
 * @module lib/common/crypto
 * @summary Whiteflag API common cryptographic functions
 * @description Module with synchronous cryptographic functions
 * @tutorial modules
 */
module.exports = {
    // Crypto functions
    hkdf,
    hash,
    zeroise
};

// Node.js core modules //
const crypto = require('crypto');

// MAIN MODULE FUNCTIONS //
/**
 * Hash-based Key Derivation Function using SHA-256 i.a.w. RFC 5869
 * @function hkdf
 * @alias module:lib/common/crypto.hkdf
 * @param {buffer} ikm input key material
 * @param {buffer} salt salt
 * @param {buffer} info info
 * @param {buffer} keylen output key length in octets
 * @returns {buffer} generated key
 */
function hkdf(ikm, salt, info, keylen) {
    const HASHALG = 'sha256';
    const HASHLEN = 32;
    const N = Math.ceil(keylen / HASHLEN);

    // Function variables
    let okm = Buffer.alloc(keylen);
    let t = Buffer.alloc(HASHLEN);
    let offset = 0;

    // Step 1. HKDF-Extract(salt, IKM) -> PRK
    let prk = crypto.createHmac(HASHALG, salt).update(ikm).digest();
    zeroise(ikm);

    // Step 2. HKDF-Expand(PRK, info, L) -> OKM
    for (let i = 1; i <= N; i++) {
        // Concatinate previous hash t, info and counter i
        let block = Buffer.alloc(offset + info.length + 1);
        t.copy(block, 0);
        info.copy(block, offset);
        block[offset + info.length] = i;

        // Get hash and add to okm buffer
        let hash = crypto.createHmac(HASHALG, prk).update(block).digest();
        hash.copy(t, 0);
        hash.copy(okm, offset * (i - 1));

        // Block contains t after after first interation
        offset = HASHLEN;
    }
    // Return output key material
    return okm;
}

/**
 * Basic hashing function
 * @function hash
 * @alias module:lib/common/crypto.hash
 * @param {string} data data to hash
 * @param {number} [lentgh] output length in octets
 * @param {string} [algorithm] hash algorithm
 * @returns {string} hexadecimal representation of the hash
 */
function hash(data, length, algorithm = 'sha256') {
    const output = crypto.createHash(algorithm).update(data).digest('hex');
    if (!length) return output;
    return output.substr(0, (length * 2));
}

/**
 * Basic zeroisation function
 * @function zeroise
 * @alias module:lib/common/crypto.zeroise
 * @param {buffer} buffer buffer to zeroise
 * @returns {buffer} the zeroised buffer
 */
function zeroise(buffer) {
    return buffer.fill(0);
}