Home Reference Source Test

src/main/generic/utils/crypto/CryptoUtils.js

class CryptoUtils {
    /**
     * @param {Uint8Array} key
     * @param {Uint8Array} data
     * @return {Uint8Array}
     */
    static computeHmacSha512(key, data) {
        if (key.length > CryptoUtils.SHA512_BLOCK_SIZE) {
            key = new SerialBuffer(Hash.computeSha512(key));
        }

        const iKey = new SerialBuffer(CryptoUtils.SHA512_BLOCK_SIZE);
        const oKey = new SerialBuffer(CryptoUtils.SHA512_BLOCK_SIZE);
        for (let i = 0; i < CryptoUtils.SHA512_BLOCK_SIZE; ++i) {
            const byte = key[i] || 0;
            iKey[i] = 0x36 ^ byte;
            oKey[i] = 0x5c ^ byte;
        }

        const innerHash = Hash.computeSha512(BufferUtils.concatTypedArrays(iKey, data));
        return Hash.computeSha512(BufferUtils.concatTypedArrays(oKey, innerHash));
    }

    /**
     * @param {Uint8Array} password
     * @param {Uint8Array} salt
     * @param {number} iterations
     * @param {number} derivedKeyLength
     * @return {Uint8Array}
     */
    static computePBKDF2sha512(password, salt, iterations, derivedKeyLength) {
        // Following https://www.ietf.org/rfc/rfc2898.txt
        const hashLength = Hash.SIZE.get(Hash.Algorithm.SHA512);

        if (derivedKeyLength > (Math.pow(2, 32) - 1) * hashLength) {
            throw new Error('Derived key too long');
        }

        const l = Math.ceil(derivedKeyLength / hashLength);
        const r = derivedKeyLength - (l - 1) * hashLength;

        const derivedKey = new SerialBuffer(derivedKeyLength);
        for (let i = 1; i <= l; i++) {
            let u = new SerialBuffer(salt.length + 4);
            u.write(salt);
            u.writeUint32(i);

            u = CryptoUtils.computeHmacSha512(password, u);
            const t = u;
            for (let j = 1; j < iterations; j++) {
                u = CryptoUtils.computeHmacSha512(password, u);
                for (let k = 0; k < t.length; k++) {
                    t[k] ^= u[k];
                }
            }

            if (i < l) {
                derivedKey.write(t);
            } else {
                derivedKey.write(t.slice(0, r));
            }
        }
        return derivedKey;
    }

    /**
     * @param {Uint8Array} message
     * @param {Uint8Array} key
     * @param {Uint8Array} salt
     * @param {number} iterations
     * @return {Promise.<Uint8Array>}
     * @deprecated
     */
    static async otpKdfLegacy(message, key, salt, iterations) {
        const worker = await CryptoWorker.getInstanceAsync();
        const derivedKey = await worker.kdfLegacy(key, salt, iterations, message.byteLength);
        return BufferUtils.xor(message, derivedKey);
    }

    /**
     * @param {Uint8Array} message
     * @param {Uint8Array} key
     * @param {Uint8Array} salt
     * @param {number} iterations
     * @return {Promise.<Uint8Array>}
     */
    static async otpKdf(message, key, salt, iterations) {
        const worker = await CryptoWorker.getInstanceAsync();
        const derivedKey = await worker.kdf(key, salt, iterations, message.byteLength);
        return BufferUtils.xor(message, derivedKey);
    }

}
CryptoUtils.SHA512_BLOCK_SIZE = 128;

Class.register(CryptoUtils);