Sodium HMAC Utility
sodium-hmac is a JavaScript utility library for creating Hash-based Message Authentication Codes (HMAC). Currently at stable version 2.1.0, this package provides both a streaming API for processing data in chunks and a simplified one-shot API for common SHA256 and SHA512 HMAC operations. Its key differentiator is the flexibility to integrate custom hash functions, provided they adhere to a specific interface (init, update, final, BYTES, STATEBYTES), allowing users to leverage various cryptographic primitives like Blake2b via external libraries such as `sodium`. Maintained by the Holepunch ecosystem, it focuses on reliable cryptographic primitives for secure data integrity and authentication. The library does not enforce specific external dependencies for its hash functions, making it adaptable to different environments and cryptographic backends.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'init')
cause The `HMAC` constructor was provided with a `hash` object that is missing the `init` method, or the object structure is incorrect.fixVerify that the custom hash object passed to `new HMAC(hash)` includes all required methods: `init`, `update`, `final`, and properties: `BYTES`, `STATEBYTES`. Use `sha256` or `sha512` exports as a reference. -
Error: HMAC not initialized
cause An `HMAC` instance's `update()` method was called before `init()` was invoked to set the secret key.fixCall `hmac.init(Buffer.from('your-key'))` immediately after creating the `HMAC` instance and before calling `hmac.update(data)`. -
TypeError: Data must be a Buffer or Uint8Array
cause A non-Buffer/Uint8Array value was passed to `hmac.update()` or a static HMAC method (`HMAC.sha256`, `HMAC.sha512`).fixEnsure all input data, including the key and message parts, are converted to `Buffer` or `Uint8Array` instances, for example, using `Buffer.from('string')` or `b4a.from('string')`.
Warnings
- gotcha When using the streaming HMAC API (HMAC class instance), `hmac.init(key)` must always be called before any calls to `hmac.update(data)`. Failing to initialize will result in runtime errors.
- gotcha The `HMAC` constructor expects the `hash` argument to be an object conforming to a specific interface (`init`, `update`, `final`, `BYTES`, `STATEBYTES`). Providing an object that lacks any of these properties will lead to a `TypeError`.
- gotcha The `final()` method returns the HMAC digest. If you intend to use the HMAC result, you must capture the return value of `hmac.final()`.
- gotcha All data inputs (`key`, `data` in `update`, `data` in static methods) are expected to be `Buffer` instances or `Uint8Array`s. While Node.js `Buffer` works, in browser environments, `Uint8Array` or a universal buffer polyfill like `b4a` should be used.
Install
-
npm install sodium-hmac -
yarn add sodium-hmac -
pnpm add sodium-hmac
Imports
- HMAC
const HMAC = require('sodium-hmac').HMACimport { HMAC } from 'sodium-hmac' - sha256
const sha256 = require('sodium-hmac').sha256import { sha256 } from 'sodium-hmac' - HMAC.sha256 (static method)
const result = require('sodium-hmac').HMAC.sha256(data, key)import { HMAC } from 'sodium-hmac'; const result = HMAC.sha256(data, key);
Quickstart
import { HMAC, sha256, sha512 } from 'sodium-hmac';
import b4a from 'b4a'; // A common Buffer-compatible utility for universal environments
const key = b4a.from('a-very-secret-key-of-at-least-32-bytes');
const dataPart1 = b4a.from('This is the first part ');
const dataPart2 = b4a.from('and this is the second part of the message.');
// 1. Using the streaming API with SHA256
const hmacSha256 = new HMAC(sha256);
hmacSha256.init(key);
hmacSha256.update(dataPart1);
hmacSha256.update(dataPart2);
const outputSha256 = hmacSha256.final();
console.log('HMAC-SHA256 (streaming):', outputSha256.toString('hex'));
// 2. Using the simple one-shot API with SHA512
const fullData = b4a.concat([dataPart1, dataPart2]);
const outputSha512 = HMAC.sha512(fullData, key);
console.log('HMAC-SHA512 (one-shot):', outputSha512.toString('hex'));
// 3. Demonstrating custom hash function integration (conceptual, requires 'sodium' or similar)
// Assuming 'sodium' is installed and provides a compatible blake2b hash interface.
/*
import sodium from 'sodium-native'; // or 'libsodium-wrappers'
const blake2b = {
init: (state, key) => sodium.crypto_generichash_init(state, key, 64), // 64 bytes for BLAKE2b
update: sodium.crypto_generichash_update,
final: (state, out) => sodium.crypto_generichash_final(state, out, 64),
BYTES: 64,
STATEBYTES: sodium.crypto_generichash_STATEBYTES
};
const hmacBlake2b = new HMAC(blake2b);
hmacBlake2b.init(key);
hmacBlake2b.update(fullData);
const outputBlake2b = hmacBlake2b.final(b4a.alloc(blake2b.BYTES));
console.log('HMAC-BLAKE2b (custom hash):', outputBlake2b.toString('hex'));
*/