The Sodium cryptographic library compiled to pure JavaScript (wrappers, sumo variant)
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Cryptographic hash functions provide secure message digests for data integrity, fingerprinting, and key derivation. The library includes BLAKE2b (generic hash), SHA-256, and SHA-512 implementations with both one-shot and streaming operations.
BLAKE2b is a high-performance cryptographic hash function that's faster than SHA-3 and more secure than SHA-2. It supports keyed hashing and variable output lengths.
/**
* Generate a random key for BLAKE2b keyed hashing
* @returns Uint8Array - Random key (up to 64 bytes)
*/
function crypto_generichash_keygen(): Uint8Array;/**
* Compute BLAKE2b hash with optional key and custom length
* @param hash_length - Desired output length (1-64 bytes, default 32)
* @param message - Data to hash
* @param key - Optional key for keyed hashing (null for unkeyed)
* @returns Uint8Array - Hash digest
*/
function crypto_generichash(
hash_length: number,
message: Uint8Array,
key?: Uint8Array | null
): Uint8Array;/**
* Initialize BLAKE2b streaming state
* @param key - Optional key for keyed hashing
* @param hash_length - Output length (1-64 bytes)
* @returns Uint8Array - State object for streaming
*/
function crypto_generichash_init(
key: Uint8Array | null,
hash_length: number
): Uint8Array;
/**
* Update BLAKE2b state with data chunk
* @param state_address - State from init function
* @param message_chunk - Data chunk to hash
*/
function crypto_generichash_update(
state_address: any,
message_chunk: Uint8Array
): void;
/**
* Finalize BLAKE2b and return hash digest
* @param state_address - State from init/update functions
* @param hash_length - Output length (must match init)
* @returns Uint8Array - Final hash digest
*/
function crypto_generichash_final(
state_address: any,
hash_length: number
): Uint8Array;const crypto_generichash_BYTES: number; // 32 (default output size)
const crypto_generichash_BYTES_MIN: number; // 16 (minimum output size)
const crypto_generichash_BYTES_MAX: number; // 64 (maximum output size)
const crypto_generichash_KEYBYTES: number; // 32 (default key size)
const crypto_generichash_KEYBYTES_MIN: number; // 16 (minimum key size)
const crypto_generichash_KEYBYTES_MAX: number; // 64 (maximum key size)
// BLAKE2b specific constants
const crypto_generichash_blake2b_BYTES: number; // 32
const crypto_generichash_blake2b_BYTES_MIN: number; // 16
const crypto_generichash_blake2b_BYTES_MAX: number; // 64
const crypto_generichash_blake2b_KEYBYTES: number; // 32
const crypto_generichash_blake2b_KEYBYTES_MIN: number; // 16
const crypto_generichash_blake2b_KEYBYTES_MAX: number; // 64
const crypto_generichash_blake2b_SALTBYTES: number; // 16
const crypto_generichash_blake2b_PERSONALBYTES: number; // 16/**
* BLAKE2b with salt and personalization parameters
* @param subkey_len - Output length
* @param key - Optional key
* @param id - Salt parameter (16 bytes or null)
* @param ctx - Personalization parameter (16 bytes or null)
* @returns Uint8Array - Hash digest
*/
function crypto_generichash_blake2b_salt_personal(
subkey_len: number,
key: Uint8Array | null,
id: Uint8Array | null,
ctx: Uint8Array | null
): Uint8Array;Secure Hash Algorithm 256-bit, widely used and standardized hash function.
/**
* Compute SHA-256 hash
* @param message - Data to hash
* @returns Uint8Array - 32-byte hash digest
*/
function crypto_hash_sha256(message: Uint8Array): Uint8Array;/**
* Initialize SHA-256 streaming state
* @returns Uint8Array - State object for streaming
*/
function crypto_hash_sha256_init(): Uint8Array;
/**
* Update SHA-256 state with data chunk
* @param state_address - State from init function
* @param message_chunk - Data chunk to hash
*/
function crypto_hash_sha256_update(
state_address: any,
message_chunk: Uint8Array
): void;
/**
* Finalize SHA-256 and return hash digest
* @param state_address - State from init/update functions
* @returns Uint8Array - 32-byte hash digest
*/
function crypto_hash_sha256_final(state_address: any): Uint8Array;const crypto_hash_sha256_BYTES: number; // 32 (output size)Secure Hash Algorithm 512-bit, provides higher security margin than SHA-256.
/**
* Compute SHA-512 hash
* @param message - Data to hash
* @returns Uint8Array - 64-byte hash digest
*/
function crypto_hash_sha512(message: Uint8Array): Uint8Array;/**
* Initialize SHA-512 streaming state
* @returns Uint8Array - State object for streaming
*/
function crypto_hash_sha512_init(): Uint8Array;
/**
* Update SHA-512 state with data chunk
* @param state_address - State from init function
* @param message_chunk - Data chunk to hash
*/
function crypto_hash_sha512_update(
state_address: any,
message_chunk: Uint8Array
): void;
/**
* Finalize SHA-512 and return hash digest
* @param state_address - State from init/update functions
* @returns Uint8Array - 64-byte hash digest
*/
function crypto_hash_sha512_final(state_address: any): Uint8Array;const crypto_hash_sha512_BYTES: number; // 64 (output size)The generic hash function defaults to SHA-512.
/**
* Compute generic hash (SHA-512)
* @param message - Data to hash
* @returns Uint8Array - 64-byte hash digest
*/
function crypto_hash(message: Uint8Array): Uint8Array;
const crypto_hash_BYTES: number; // 64 (output size)import _sodium from 'libsodium-wrappers-sumo';
await _sodium.ready;
const sodium = _sodium;
// Basic BLAKE2b hashing
const message = sodium.from_string('Hello, World!');
// Default 32-byte BLAKE2b hash
const hash = sodium.crypto_generichash(32, message);
console.log('BLAKE2b hash:', sodium.to_hex(hash));
// SHA-256 hash
const sha256 = sodium.crypto_hash_sha256(message);
console.log('SHA-256 hash:', sodium.to_hex(sha256));
// SHA-512 hash
const sha512 = sodium.crypto_hash_sha512(message);
console.log('SHA-512 hash:', sodium.to_hex(sha512));
// Generic hash (SHA-512)
const genericHash = sodium.crypto_hash(message);
console.log('Generic hash equals SHA-512:',
sodium.memcmp(sha512, genericHash)); // true// Generate key for keyed hashing
const key = sodium.crypto_generichash_keygen();
const message = sodium.from_string('Keyed message');
// Keyed BLAKE2b hash
const keyedHash = sodium.crypto_generichash(32, message, key);
console.log('Keyed hash:', sodium.to_hex(keyedHash));
// Same message with different key produces different hash
const differentKey = sodium.crypto_generichash_keygen();
const differentHash = sodium.crypto_generichash(32, message, differentKey);
console.log('Same message, different key produces different hash:',
!sodium.memcmp(keyedHash, differentHash)); // trueconst message = sodium.from_string('Variable length hashing');
// Different output lengths
const hash16 = sodium.crypto_generichash(16, message); // 16 bytes
const hash32 = sodium.crypto_generichash(32, message); // 32 bytes
const hash64 = sodium.crypto_generichash(64, message); // 64 bytes
console.log('16-byte hash:', sodium.to_hex(hash16));
console.log('32-byte hash:', sodium.to_hex(hash32));
console.log('64-byte hash:', sodium.to_hex(hash64));function hashLargeData(chunks, algorithm = 'blake2b') {
let state;
// Initialize based on algorithm
switch (algorithm) {
case 'blake2b':
state = sodium.crypto_generichash_init(null, 32);
break;
case 'sha256':
state = sodium.crypto_hash_sha256_init();
break;
case 'sha512':
state = sodium.crypto_hash_sha512_init();
break;
default:
throw new Error('Unsupported algorithm');
}
// Process chunks
for (const chunk of chunks) {
switch (algorithm) {
case 'blake2b':
sodium.crypto_generichash_update(state, chunk);
break;
case 'sha256':
sodium.crypto_hash_sha256_update(state, chunk);
break;
case 'sha512':
sodium.crypto_hash_sha512_update(state, chunk);
break;
}
}
// Finalize
switch (algorithm) {
case 'blake2b':
return sodium.crypto_generichash_final(state, 32);
case 'sha256':
return sodium.crypto_hash_sha256_final(state);
case 'sha512':
return sodium.crypto_hash_sha512_final(state);
}
}
// Create large data set
const largeData = new Uint8Array(10 * 1024 * 1024); // 10MB
sodium.randombytes_buf_into(largeData);
// Split into chunks
const chunkSize = 64 * 1024; // 64KB chunks
const chunks = [];
for (let i = 0; i < largeData.length; i += chunkSize) {
chunks.push(largeData.subarray(i, i + chunkSize));
}
// Hash with different algorithms
console.time('BLAKE2b streaming');
const blake2bHash = hashLargeData(chunks, 'blake2b');
console.timeEnd('BLAKE2b streaming');
console.time('SHA-256 streaming');
const sha256Hash = hashLargeData(chunks, 'sha256');
console.timeEnd('SHA-256 streaming');
console.time('SHA-512 streaming');
const sha512Hash = hashLargeData(chunks, 'sha512');
console.timeEnd('SHA-512 streaming');
console.log('BLAKE2b result:', sodium.to_hex(blake2bHash));
console.log('SHA-256 result:', sodium.to_hex(sha256Hash));class FileIntegrity {
constructor() {
this.checksums = new Map();
}
// Store file hash
addFile(filename, data) {
const hash = sodium.crypto_generichash(32, data);
this.checksums.set(filename, {
hash: sodium.to_hex(hash),
size: data.length,
timestamp: new Date().toISOString()
});
return sodium.to_hex(hash);
}
// Verify file integrity
verifyFile(filename, data) {
const stored = this.checksums.get(filename);
if (!stored) {
return { valid: false, error: 'File not in registry' };
}
if (data.length !== stored.size) {
return { valid: false, error: 'Size mismatch' };
}
const currentHash = sodium.crypto_generichash(32, data);
const currentHashHex = sodium.to_hex(currentHash);
return {
valid: currentHashHex === stored.hash,
storedHash: stored.hash,
currentHash: currentHashHex,
timestamp: stored.timestamp
};
}
// Export registry
exportRegistry() {
return JSON.stringify(Object.fromEntries(this.checksums));
}
// Import registry
importRegistry(json) {
const data = JSON.parse(json);
this.checksums = new Map(Object.entries(data));
}
}
// Usage
const integrity = new FileIntegrity();
const fileData = sodium.from_string('Important file content');
// Add file to registry
const hash = integrity.addFile('document.txt', fileData);
console.log('File hash:', hash);
// Verify file later
const verification = integrity.verifyFile('document.txt', fileData);
console.log('File is valid:', verification.valid); // true
// Test with modified file
const modifiedData = sodium.from_string('Modified file content');
const modifiedVerification = integrity.verifyFile('document.txt', modifiedData);
console.log('Modified file is valid:', modifiedVerification.valid); // falseSecure password hashing using Argon2i and Argon2id algorithms for storing user passwords and deriving keys from passwords. Never use regular hash functions for passwords.
/**
* Derive a key from a password using Argon2
* @param keyLength - Length of derived key (16-4294967295)
* @param password - Password to derive from
* @param salt - Random salt (crypto_pwhash_SALTBYTES)
* @param opsLimit - CPU/time cost parameter
* @param memLimit - Memory cost parameter (bytes)
* @param algorithm - Algorithm (ALG_ARGON2I13 or ALG_ARGON2ID13)
* @returns Uint8Array - Derived key
*/
function crypto_pwhash(
keyLength: number,
password: Uint8Array,
salt: Uint8Array,
opsLimit: number,
memLimit: number,
algorithm: number
): Uint8Array;/**
* Hash password to string format for storage
* @param password - Password to hash
* @param opsLimit - CPU/time cost parameter
* @param memLimit - Memory cost parameter (bytes)
* @returns string - Formatted hash string for storage
*/
function crypto_pwhash_str(
password: Uint8Array,
opsLimit: number,
memLimit: number
): string;
/**
* Verify password against stored hash string
* @param hashedPassword - Previously computed hash string
* @param password - Password to verify
* @returns boolean - True if password matches
*/
function crypto_pwhash_str_verify(
hashedPassword: string,
password: Uint8Array
): boolean;
/**
* Check if stored hash needs rehashing (parameters changed)
* @param hashedPassword - Previously computed hash string
* @param opsLimit - Current CPU cost parameter
* @param memLimit - Current memory cost parameter
* @returns boolean - True if rehashing recommended
*/
function crypto_pwhash_str_needs_rehash(
hashedPassword: string,
opsLimit: number,
memLimit: number
): boolean;/**
* Legacy scrypt-based password hashing
* @param keyLength - Length of derived key
* @param password - Password to derive from
* @param salt - Random salt (32 bytes)
* @param opsLimit - CPU/time cost parameter
* @param memLimit - Memory cost parameter
* @returns Uint8Array - Derived key
*/
function crypto_pwhash_scryptsalsa208sha256(
keyLength: number,
password: Uint8Array,
salt: Uint8Array,
opsLimit: number,
memLimit: number
): Uint8Array;
/**
* Legacy scrypt string hashing
* @param password - Password to hash
* @param opsLimit - CPU cost parameter
* @param memLimit - Memory cost parameter
* @returns string - Formatted hash string
*/
function crypto_pwhash_scryptsalsa208sha256_str(
password: Uint8Array,
opsLimit: number,
memLimit: number
): string;
/**
* Verify password against scrypt hash string
* @param hashedPassword - Previously computed scrypt hash
* @param password - Password to verify
* @returns boolean - True if password matches
*/
function crypto_pwhash_scryptsalsa208sha256_str_verify(
hashedPassword: string,
password: Uint8Array
): boolean;// Argon2 algorithm identifiers
const crypto_pwhash_ALG_ARGON2I13: number; // 1 (Argon2i)
const crypto_pwhash_ALG_ARGON2ID13: number; // 2 (Argon2id - recommended)
const crypto_pwhash_ALG_DEFAULT: number; // Same as ALG_ARGON2ID13
// Salt and output size limits
const crypto_pwhash_SALTBYTES: number; // 32 (salt size)
const crypto_pwhash_STRBYTES: number; // 102 (string hash length)
const crypto_pwhash_STRPREFIX: string; // "$argon2id$" (string prefix)
const crypto_pwhash_BYTES_MIN: number; // 16 (min key length)
const crypto_pwhash_BYTES_MAX: number; // 4294967295 (max key length)
const crypto_pwhash_PASSWD_MIN: number; // 0 (min password length)
const crypto_pwhash_PASSWD_MAX: number; // 4294967295 (max password length)
// Computational limits - Argon2
const crypto_pwhash_OPSLIMIT_INTERACTIVE: number; // 4 (fast login)
const crypto_pwhash_OPSLIMIT_MODERATE: number; // 6 (moderate security)
const crypto_pwhash_OPSLIMIT_SENSITIVE: number; // 8 (high security)
const crypto_pwhash_MEMLIMIT_INTERACTIVE: number; // 67108864 (64MB)
const crypto_pwhash_MEMLIMIT_MODERATE: number; // 268435456 (256MB)
const crypto_pwhash_MEMLIMIT_SENSITIVE: number; // 1073741824 (1GB)
// Computational limits - Scrypt legacy
const crypto_pwhash_scryptsalsa208sha256_SALTBYTES: number; // 32
const crypto_pwhash_scryptsalsa208sha256_STRBYTES: number; // 102
const crypto_pwhash_scryptsalsa208sha256_STRPREFIX: string; // "$7$"
const crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE: number; // 32768
const crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE: number; // 33554432
const crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE: number; // 16777216
const crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE: number; // 1073741824// User registration - hash password for storage
const password = sodium.from_string('user_password123');
// String-based hashing (recommended for most use cases)
const hashedPassword = sodium.crypto_pwhash_str(
password,
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE
);
// Store hashedPassword in database
console.log('Store this hash:', hashedPassword);
// User login - verify password
const loginPassword = sodium.from_string('user_password123');
const isValid = sodium.crypto_pwhash_str_verify(hashedPassword, loginPassword);
console.log('Password is valid:', isValid); // true
// Key derivation from password (for encryption keys)
const salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);
const encryptionKey = sodium.crypto_pwhash(
32, // 32-byte key for ChaCha20
password,
salt,
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
sodium.crypto_pwhash_ALG_ARGON2ID13
);
// Use encryptionKey for symmetric encryption
// Store salt alongside encrypted data for key recovery// BLAKE2b with salt and personalization for domain separation
const message = sodium.from_string('Domain-specific message');
const salt = sodium.from_string('app_salt_v1.0'); // 16 bytes max
const personal = sodium.from_string('user_profile_'); // 16 bytes max
// Pad to required lengths
const saltPadded = new Uint8Array(16);
const personalPadded = new Uint8Array(16);
saltPadded.set(salt.subarray(0, Math.min(salt.length, 16)));
personalPadded.set(personal.subarray(0, Math.min(personal.length, 16)));
const domainHash = sodium.crypto_generichash_blake2b_salt_personal(
32, null, saltPadded, personalPadded
);
console.log('Domain-separated hash:', sodium.to_hex(domainHash));function benchmarkHashing() {
const message = new Uint8Array(1024 * 1024); // 1MB
sodium.randombytes_buf_into(message);
const iterations = 10;
// BLAKE2b
console.time(`BLAKE2b ${iterations}x1MB`);
for (let i = 0; i < iterations; i++) {
sodium.crypto_generichash(32, message);
}
console.timeEnd(`BLAKE2b ${iterations}x1MB`);
// SHA-256
console.time(`SHA-256 ${iterations}x1MB`);
for (let i = 0; i < iterations; i++) {
sodium.crypto_hash_sha256(message);
}
console.timeEnd(`SHA-256 ${iterations}x1MB`);
// SHA-512
console.time(`SHA-512 ${iterations}x1MB`);
for (let i = 0; i < iterations; i++) {
sodium.crypto_hash_sha512(message);
}
console.timeEnd(`SHA-512 ${iterations}x1MB`);
}
benchmarkHashing();Fast non-cryptographic hash functions for hash tables, checksums, and other applications where speed is more important than cryptographic security.
/**
* Generate random key for short hash functions
* @returns Uint8Array - 16-byte key for SipHash
*/
function crypto_shorthash_keygen(): Uint8Array;/**
* Compute SipHash-2-4 (default short hash)
* @param message - Data to hash
* @param key - 16-byte secret key
* @returns Uint8Array - 8-byte hash output
*/
function crypto_shorthash(
message: Uint8Array,
key: Uint8Array
): Uint8Array;
/**
* Compute SipHash-X-2-4 (128-bit output variant)
* @param message - Data to hash
* @param key - 16-byte secret key
* @returns Uint8Array - 16-byte hash output
*/
function crypto_shorthash_siphashx24(
message: Uint8Array,
key: Uint8Array
): Uint8Array;const crypto_shorthash_BYTES: number; // 8 (SipHash-2-4 output)
const crypto_shorthash_KEYBYTES: number; // 16 (key size)
const crypto_shorthash_siphashx24_BYTES: number; // 16 (SipHashX-2-4 output)
const crypto_shorthash_siphashx24_KEYBYTES: number; // 16 (key size)// Generate key for short hash operations
const shortHashKey = sodium.crypto_shorthash_keygen();
// Hash table usage example
class FastHashMap {
constructor() {
this.buckets = new Array(1024).fill(null).map(() => []);
this.key = sodium.crypto_shorthash_keygen();
}
getBucket(key) {
const keyBytes = sodium.from_string(key);
const hash = sodium.crypto_shorthash(keyBytes, this.key);
// Convert first 4 bytes to bucket index
const bucket = new DataView(hash.buffer).getUint32(0, true) % this.buckets.length;
return this.buckets[bucket];
}
set(key, value) {
const bucket = this.getBucket(key);
const existing = bucket.find(item => item.key === key);
if (existing) {
existing.value = value;
} else {
bucket.push({ key, value });
}
}
get(key) {
const bucket = this.getBucket(key);
const item = bucket.find(item => item.key === key);
return item ? item.value : undefined;
}
}
// Usage
const hashMap = new FastHashMap();
hashMap.set('user:123', { name: 'Alice', email: 'alice@example.com' });
hashMap.set('user:456', { name: 'Bob', email: 'bob@example.com' });
console.log(hashMap.get('user:123')); // { name: 'Alice', email: 'alice@example.com' }
// Checksums for data integrity (non-cryptographic)
const data1 = sodium.from_string('Version 1.0 data');
const data2 = sodium.from_string('Version 1.1 data');
const checksum1 = sodium.crypto_shorthash(data1, shortHashKey);
const checksum2 = sodium.crypto_shorthash(data2, shortHashKey);
console.log('Data1 checksum:', sodium.to_hex(checksum1));
console.log('Data2 checksum:', sodium.to_hex(checksum2));
console.log('Checksums match:', sodium.memcmp(checksum1, checksum2)); // falseBLAKE2b is generally recommended for new applications due to its superior performance and security properties.