{"id":15237,"library":"sodium-hmac","title":"Sodium HMAC Utility","description":"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.","status":"active","version":"2.1.0","language":"javascript","source_language":"en","source_url":"https://github.com/holepunchto/sodium-hmac","tags":["javascript"],"install":[{"cmd":"npm install sodium-hmac","lang":"bash","label":"npm"},{"cmd":"yarn add sodium-hmac","lang":"bash","label":"yarn"},{"cmd":"pnpm add sodium-hmac","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"While CommonJS `require` is shown in examples and is functional, prefer named ESM imports for modern JavaScript environments.","wrong":"const HMAC = require('sodium-hmac').HMAC","symbol":"HMAC","correct":"import { HMAC } from 'sodium-hmac'"},{"note":"The `sha256` hash function is exported directly for use with the `HMAC` class or as a standalone utility.","wrong":"const sha256 = require('sodium-hmac').sha256","symbol":"sha256","correct":"import { sha256 } from 'sodium-hmac'"},{"note":"This provides a convenient one-shot API for HMAC-SHA256, abstracting the streaming interface.","wrong":"const result = require('sodium-hmac').HMAC.sha256(data, key)","symbol":"HMAC.sha256 (static method)","correct":"import { HMAC } from 'sodium-hmac'; const result = HMAC.sha256(data, key);"}],"quickstart":{"code":"import { HMAC, sha256, sha512 } from 'sodium-hmac';\nimport b4a from 'b4a'; // A common Buffer-compatible utility for universal environments\n\nconst key = b4a.from('a-very-secret-key-of-at-least-32-bytes');\nconst dataPart1 = b4a.from('This is the first part ');\nconst dataPart2 = b4a.from('and this is the second part of the message.');\n\n// 1. Using the streaming API with SHA256\nconst hmacSha256 = new HMAC(sha256);\nhmacSha256.init(key);\nhmacSha256.update(dataPart1);\nhmacSha256.update(dataPart2);\nconst outputSha256 = hmacSha256.final();\nconsole.log('HMAC-SHA256 (streaming):', outputSha256.toString('hex'));\n\n// 2. Using the simple one-shot API with SHA512\nconst fullData = b4a.concat([dataPart1, dataPart2]);\nconst outputSha512 = HMAC.sha512(fullData, key);\nconsole.log('HMAC-SHA512 (one-shot):', outputSha512.toString('hex'));\n\n// 3. Demonstrating custom hash function integration (conceptual, requires 'sodium' or similar)\n// Assuming 'sodium' is installed and provides a compatible blake2b hash interface.\n/*\nimport sodium from 'sodium-native'; // or 'libsodium-wrappers'\nconst blake2b = {\n  init: (state, key) => sodium.crypto_generichash_init(state, key, 64), // 64 bytes for BLAKE2b\n  update: sodium.crypto_generichash_update,\n  final: (state, out) => sodium.crypto_generichash_final(state, out, 64),\n  BYTES: 64,\n  STATEBYTES: sodium.crypto_generichash_STATEBYTES\n};\n\nconst hmacBlake2b = new HMAC(blake2b);\nhmacBlake2b.init(key);\nhmacBlake2b.update(fullData);\nconst outputBlake2b = hmacBlake2b.final(b4a.alloc(blake2b.BYTES));\nconsole.log('HMAC-BLAKE2b (custom hash):', outputBlake2b.toString('hex'));\n*/\n","lang":"typescript","description":"This quickstart demonstrates both the streaming and one-shot HMAC APIs, using built-in SHA256/SHA512 and conceptually showing how to integrate a custom hash function."},"warnings":[{"fix":"Ensure `hmac.init(key)` is invoked once after creating the HMAC instance and before processing any data chunks.","message":"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.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"When supplying a custom hash function (e.g., from `libsodium` or other crypto libraries), ensure it exposes all required methods and properties. Refer to the `sha256` or `sha512` exports as examples.","message":"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`.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Always assign the result of `hmac.final()` to a variable, e.g., `const output = hmac.final();`.","message":"The `final()` method returns the HMAC digest. If you intend to use the HMAC result, you must capture the return value of `hmac.final()`.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Convert all string or other data types to `Buffer.from('your-data')` or `b4a.from('your-data')` before passing them to HMAC functions.","message":"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.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"Verify 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.","cause":"The `HMAC` constructor was provided with a `hash` object that is missing the `init` method, or the object structure is incorrect.","error":"TypeError: Cannot read properties of undefined (reading 'init')"},{"fix":"Call `hmac.init(Buffer.from('your-key'))` immediately after creating the `HMAC` instance and before calling `hmac.update(data)`.","cause":"An `HMAC` instance's `update()` method was called before `init()` was invoked to set the secret key.","error":"Error: HMAC not initialized"},{"fix":"Ensure 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')`.","cause":"A non-Buffer/Uint8Array value was passed to `hmac.update()` or a static HMAC method (`HMAC.sha256`, `HMAC.sha512`).","error":"TypeError: Data must be a Buffer or Uint8Array"}],"ecosystem":"npm"}