libsodium-wrappers
libsodium-wrappers provides a JavaScript/TypeScript binding for the highly regarded Sodium cryptographic library, compiled to WebAssembly with a pure JavaScript fallback. It is currently stable at version 0.8.3, wrapping libsodium 1.0.22. The package offers comprehensive, high-performance cryptographic operations for both web browsers (Chrome, Firefox, Edge, Safari, Mobile Safari) and server-side environments like Node.js and Bun. It comes in two variants: a standard version with commonly used functions and a 'sumo' version that includes the full, exhaustive libsodium API. Since version 0.8.1, the library automatically includes TypeScript definitions, simplifying development in typed environments. Users must `await sodium.ready` to ensure the underlying cryptographic engine is fully initialized before using any functions or constants. The library maintains a regular release cadence to incorporate upstream libsodium updates and address issues.
Common errors
-
TypeError: sodium.crypto_secretstream_xchacha20poly1305_keygen is not a function
cause Attempting to use a cryptographic function before the `sodium.ready` promise has resolved.fixEnsure `await sodium.ready;` is executed before any calls to `sodium`'s cryptographic methods. -
ERR_MODULE_NOT_FOUND: Cannot find package 'libsodium-wrappers'
cause Incorrect import path, package not installed, or module resolution issues in the build system (e.g., webpack, Turbopack, or Jest).fixVerify the package is installed (`npm install libsodium-wrappers`). Check import paths, especially when mixing CommonJS and ESM. For bundler issues, review configuration (e.g., Next.js with Turbopack might need specific handling for WASM modules). -
TypeError: Cannot read properties of undefined (reading 'sodium')
cause This error can occur in testing environments (like Jest) where the global `window` object or module loading context is not fully emulated as expected by libsodium-wrappers' internal initialization logic.fixWhen testing, ensure Jest (or similar) is correctly configured for ESM and/or browser-like environments. You might need to mock the library or configure Jest's `moduleNameMapper` or `transformIgnorePatterns` to handle it correctly. Some users have found solutions by ensuring proper WebAssembly loading in their test runner.
Warnings
- breaking All cryptographic functions and constants are only available after the `sodium.ready` promise has resolved. Attempting to call them before initialization will result in a `TypeError`.
- gotcha Cryptographic functions (e.g., `crypto_secretbox_easy`) and constants (e.g., `crypto_secretbox_KEYBYTES`) are not available as named exports in ESM modules. They must be accessed via the default `sodium` object after it has initialized.
- gotcha Older versions of Mobile Safari on iOS (< 8.0) and Safari (< 6) are noted to produce incorrect cryptographic results. Developers targeting these environments should exercise extreme caution or use alternative libraries.
- gotcha The `libsodium-wrappers` package (standard) includes commonly used high-level functions, while `libsodium-wrappers-sumo` provides the full, often low-level and undocumented, libsodium API, including functions like `crypto_pwhash_*`. Using the `sumo` version increases bundle size and exposes potentially misuse-prone functions.
- breaking ESM (ECMAScript Module) support was added in version 0.7.16. Older versions may not correctly resolve `import` statements or use `.mjs` extensions, leading to module resolution errors in modern JavaScript environments.
Install
-
npm install libsodium-wrappers -
yarn add libsodium-wrappers -
pnpm add libsodium-wrappers
Imports
- sodium
import { sodium } from 'libsodium-wrappers';import sodium from 'libsodium-wrappers';
- ready
import { ready } from 'libsodium-wrappers'; console.log(sodium.crypto_secretbox_keygen());import { ready, from_string, to_string } from 'libsodium-wrappers'; - sodium
const sodium = require('libsodium-wrappers'); const key = sodium.crypto_secretbox_keygen();const sodium = require('libsodium-wrappers'); (async () => { await sodium.ready; /* use sodium */ })();
Quickstart
import sodium from 'libsodium-wrappers';
async function encryptDecryptStream() {
await sodium.ready;
// Generate a key for secret stream encryption
const key = sodium.crypto_secretstream_xchacha20poly1305_keygen();
// Initialize a push state and get the header
const { state: state_out, header } =
sodium.crypto_secretstream_xchacha20poly1305_init_push(key);
// Push messages with different tags
const message1 = sodium.from_string('This is the first secret message.');
const c1 = sodium.crypto_secretstream_xchacha20poly1305_push(
state_out, message1, null,
sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
);
const message2 = sodium.from_string('And this is the final message.');
const c2 = sodium.crypto_secretstream_xchacha20poly1305_push(
state_out, message2, null,
sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
);
console.log('Encrypted message 1 (hex):', sodium.to_hex(c1));
console.log('Encrypted message 2 (hex):', sodium.to_hex(c2));
// Initialize a pull state with the header and key
const state_in = sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, key);
// Pull and decrypt the first message
const r1 = sodium.crypto_secretstream_xchacha20poly1305_pull(state_in, c1);
const { message: m1_bytes, tag: tag1 } = r1;
const m1 = sodium.to_string(m1_bytes);
console.log('Decrypted message 1:', m1, '(Tag:', tag1, ')');
// Pull and decrypt the second (final) message
const r2 = sodium.crypto_secretstream_xchacha20poly1305_pull(state_in, c2);
const { message: m2_bytes, tag: tag2 } = r2;
const m2 = sodium.to_string(m2_bytes);
console.log('Decrypted message 2:', m2, '(Tag:', tag2, ')');
// Ensure the tags are correct
if (tag1 === sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE &&
tag2 === sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
console.log('Stream messages successfully encrypted and decrypted with correct tags.');
} else {
console.error('Tag mismatch!');
}
}
encryptDecryptStream();