UNPKG

@hpke/common

Version:

A Hybrid Public Key Encryption (HPKE) internal-use common module for @hpke family modules.

130 lines (129 loc) 4.29 kB
// deno-lint-ignore-file no-explicit-any /** * This file is based on noble-hashes (https://github.com/paulmillr/noble-hashes). * * noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) * * The original file is located at: * https://github.com/paulmillr/noble-hashes/blob/2e0c00e1aa134082ba1380bf3afb8b1641f60fed/src/hmac.ts */ /** * HMAC: RFC2104 message authentication code. * @module */ import { abytes, aexists, clean } from "../utils/noble.js"; import { ahash } from "./hash.js"; export class _HMAC { constructor(hash, key) { Object.defineProperty(this, "oHash", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "iHash", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "blockLen", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "outputLen", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "finished", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "destroyed", { enumerable: true, configurable: true, writable: true, value: false }); ahash(hash); abytes(key, undefined, "key"); this.iHash = hash.create(); if (typeof this.iHash.update !== "function") { throw new Error("Expected instance of class which extends utils.Hash"); } this.blockLen = this.iHash.blockLen; this.outputLen = this.iHash.outputLen; const blockLen = this.blockLen; const pad = new Uint8Array(blockLen); // blockLen can be bigger than outputLen pad.set(key.length > blockLen ? hash.create().update(key).digest() : key); for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36; this.iHash.update(pad); // By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone this.oHash = hash.create(); // Undo internal XOR && apply outer XOR for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c; this.oHash.update(pad); clean(pad); } update(buf) { aexists(this); this.iHash.update(buf); return this; } digestInto(out) { aexists(this); abytes(out, this.outputLen, "output"); this.finished = true; this.iHash.digestInto(out); this.oHash.update(out); this.oHash.digestInto(out); this.destroy(); } digest() { const out = new Uint8Array(this.oHash.outputLen); this.digestInto(out); return out; } _cloneInto(to) { // Create new instance without calling constructor since key already in state and we don't know it. to ||= Object.create(Object.getPrototypeOf(this), {}); const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; to = to; to.finished = finished; to.destroyed = destroyed; to.blockLen = blockLen; to.outputLen = outputLen; to.oHash = oHash._cloneInto(to.oHash); to.iHash = iHash._cloneInto(to.iHash); return to; } clone() { return this._cloneInto(); } destroy() { this.destroyed = true; this.oHash.destroy(); this.iHash.destroy(); } } /** * HMAC: RFC2104 message authentication code. * @param hash - function that would be used e.g. sha256 * @param key - message key * @param message - message data * @example * import { hmac } from '@noble/hashes/hmac'; * import { sha256 } from '@noble/hashes/sha2'; * const mac1 = hmac(sha256, 'key', 'message'); */ export const hmac = (hash, key, message) => new _HMAC(hash, key).update(message).digest(); hmac.create = (hash, key) => new _HMAC(hash, key);