Low level bindings for libsodium cryptographic library
—
Key derivation functions for generating multiple keys from a single master key with proper domain separation and context binding.
Generate a cryptographically secure master key for key derivation.
/**
* Generate random master key for key derivation
* @param key - Output buffer for master key (must be KEYBYTES long)
*/
function crypto_kdf_keygen(key: Buffer): void;Usage Example:
const sodium = require('sodium-native');
// Generate master key
const masterKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
sodium.crypto_kdf_keygen(masterKey);Derive subkeys from a master key with context and subkey ID for domain separation.
/**
* Derive subkey from master key with context and ID
* @param subkey - Output buffer for derived subkey (size determines key length)
* @param subkeyId - Subkey identifier (numeric ID for key differentiation)
* @param ctx - Context buffer for domain separation (must be CONTEXTBYTES long)
* @param key - Master key buffer (must be KEYBYTES long)
* @throws Error if buffer sizes incorrect or derivation fails
*/
function crypto_kdf_derive_from_key(
subkey: Buffer,
subkeyId: number,
ctx: Buffer,
key: Buffer
): void;Usage Example:
const sodium = require('sodium-native');
// Generate master key
const masterKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
sodium.crypto_kdf_keygen(masterKey);
// Define context for domain separation
const context = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
Buffer.from('MyApp v1').copy(context); // Pad to exact size
// Derive different types of keys
const encryptionKey = Buffer.alloc(32); // 256-bit encryption key
const authKey = Buffer.alloc(32); // 256-bit authentication key
const signingKey = Buffer.alloc(64); // 512-bit signing key
sodium.crypto_kdf_derive_from_key(encryptionKey, 1, context, masterKey);
sodium.crypto_kdf_derive_from_key(authKey, 2, context, masterKey);
sodium.crypto_kdf_derive_from_key(signingKey, 3, context, masterKey);
console.log('Derived encryption key length:', encryptionKey.length);
console.log('Derived auth key length:', authKey.length);
console.log('Derived signing key length:', signingKey.length);// Minimum derived key size in bytes
const crypto_kdf_BYTES_MIN: number;
// Maximum derived key size in bytes
const crypto_kdf_BYTES_MAX: number;
// Context size in bytes (for domain separation)
const crypto_kdf_CONTEXTBYTES: number;
// Master key size in bytes
const crypto_kdf_KEYBYTES: number;const sodium = require('sodium-native');
class ApplicationKeyManager {
constructor(appName, version) {
// Generate or load master key
this.masterKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
sodium.crypto_kdf_keygen(this.masterKey);
// Create context from app name and version
this.context = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
const contextString = `${appName} ${version}`.padEnd(sodium.crypto_kdf_CONTEXTBYTES, '\0');
Buffer.from(contextString).copy(this.context, 0, 0, sodium.crypto_kdf_CONTEXTBYTES);
}
// Key types with assigned IDs
static KeyTypes = {
DATABASE_ENCRYPTION: 1,
SESSION_SIGNING: 2,
API_AUTHENTICATION: 3,
FILE_ENCRYPTION: 4,
LOG_INTEGRITY: 5,
BACKUP_ENCRYPTION: 6
};
deriveKey(keyType, keySize = 32) {
if (!ApplicationKeyManager.KeyTypes[keyType]) {
throw new Error(`Unknown key type: ${keyType}`);
}
if (keySize < sodium.crypto_kdf_BYTES_MIN || keySize > sodium.crypto_kdf_BYTES_MAX) {
throw new Error(`Key size must be between ${sodium.crypto_kdf_BYTES_MIN} and ${sodium.crypto_kdf_BYTES_MAX} bytes`);
}
const subkey = Buffer.alloc(keySize);
const keyId = ApplicationKeyManager.KeyTypes[keyType];
sodium.crypto_kdf_derive_from_key(subkey, keyId, this.context, this.masterKey);
return subkey;
}
// Derive keys for common cryptographic operations
getEncryptionKey() {
return this.deriveKey('DATABASE_ENCRYPTION', 32);
}
getSigningKey() {
return this.deriveKey('SESSION_SIGNING', 64);
}
getAuthKey() {
return this.deriveKey('API_AUTHENTICATION', 32);
}
// Export master key for backup (encrypt before storing)
exportMasterKey() {
return Buffer.from(this.masterKey);
}
// Import master key from backup
importMasterKey(masterKeyData) {
Buffer.from(masterKeyData).copy(this.masterKey);
}
}
// Usage
const keyManager = new ApplicationKeyManager('SecureApp', 'v2.1');
const dbKey = keyManager.getEncryptionKey();
const apiKey = keyManager.getAuthKey();
const sessionKey = keyManager.getSigningKey();const sodium = require('sodium-native');
class UserKeyDerivation {
constructor(systemMasterKey) {
this.systemMasterKey = Buffer.from(systemMasterKey);
}
// Derive user-specific master key
deriveUserMasterKey(userId) {
const userContext = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
Buffer.from('UserKeys').copy(userContext);
const userMasterKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
sodium.crypto_kdf_derive_from_key(
userMasterKey,
userId,
userContext,
this.systemMasterKey
);
return userMasterKey;
}
// Derive specific keys for a user
deriveUserKeys(userId) {
const userMasterKey = this.deriveUserMasterKey(userId);
const userContext = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
Buffer.from('UserData').copy(userContext);
// Derive different keys for user data
const fileKey = Buffer.alloc(32);
const profileKey = Buffer.alloc(32);
const settingsKey = Buffer.alloc(32);
sodium.crypto_kdf_derive_from_key(fileKey, 1, userContext, userMasterKey);
sodium.crypto_kdf_derive_from_key(profileKey, 2, userContext, userMasterKey);
sodium.crypto_kdf_derive_from_key(settingsKey, 3, userContext, userMasterKey);
// Clean up user master key from memory
sodium.sodium_memzero(userMasterKey);
return {
fileEncryption: fileKey,
profileEncryption: profileKey,
settingsEncryption: settingsKey
};
}
}
// Usage
const systemKey = 'system-master-key-32-bytes-long!!!';
const userKdf = new UserKeyDerivation(systemKey);
const user123Keys = userKdf.deriveUserKeys(123);
const user456Keys = userKdf.deriveUserKeys(456);const sodium = require('sodium-native');
class HierarchicalKeyDerivation {
constructor() {
// Root master key
this.rootKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
sodium.crypto_kdf_keygen(this.rootKey);
}
// Derive domain-specific master keys
deriveDomainKey(domain) {
const domainContext = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
Buffer.from('Domains').copy(domainContext);
const domainKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
const domainId = this.hashStringToId(domain);
sodium.crypto_kdf_derive_from_key(
domainKey,
domainId,
domainContext,
this.rootKey
);
return domainKey;
}
// Derive service keys within a domain
deriveServiceKey(domain, service) {
const domainKey = this.deriveDomainKey(domain);
const serviceContext = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
Buffer.from(`${domain}`).copy(serviceContext);
const serviceKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
const serviceId = this.hashStringToId(service);
sodium.crypto_kdf_derive_from_key(
serviceKey,
serviceId,
serviceContext,
domainKey
);
// Clean up domain key
sodium.sodium_memzero(domainKey);
return serviceKey;
}
// Derive operational keys for specific purposes
deriveOperationalKey(domain, service, operation, keySize = 32) {
const serviceKey = this.deriveServiceKey(domain, service);
const opContext = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
Buffer.from(`${service}`).copy(opContext);
const operationalKey = Buffer.alloc(keySize);
const operationId = this.hashStringToId(operation);
sodium.crypto_kdf_derive_from_key(
operationalKey,
operationId,
opContext,
serviceKey
);
// Clean up service key
sodium.sodium_memzero(serviceKey);
return operationalKey;
}
// Helper to convert string to numeric ID
hashStringToId(str) {
const hash = Buffer.alloc(4);
sodium.crypto_generichash(hash, Buffer.from(str));
return hash.readUInt32LE(0);
}
}
// Usage
const hkdf = new HierarchicalKeyDerivation();
// Derive keys at different levels
const authServiceKey = hkdf.deriveServiceKey('production', 'auth-service');
const dbEncryptionKey = hkdf.deriveOperationalKey('production', 'database', 'encryption');
const logSigningKey = hkdf.deriveOperationalKey('production', 'logging', 'signing', 64);
// Same operations will always produce same keys
const dbEncryptionKey2 = hkdf.deriveOperationalKey('production', 'database', 'encryption');
console.log('Keys match:', dbEncryptionKey.equals(dbEncryptionKey2));const sodium = require('sodium-native');
class VersionedKeyManager {
constructor() {
this.masterKeys = new Map(); // version -> master key
this.currentVersion = 1;
// Initialize with first version
this.generateNewVersion();
}
generateNewVersion() {
const masterKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
sodium.crypto_kdf_keygen(masterKey);
this.masterKeys.set(this.currentVersion, masterKey);
return this.currentVersion;
}
rotateKeys() {
this.currentVersion++;
return this.generateNewVersion();
}
deriveKey(keyPurpose, version = null, keySize = 32) {
const useVersion = version || this.currentVersion;
const masterKey = this.masterKeys.get(useVersion);
if (!masterKey) {
throw new Error(`No master key for version ${useVersion}`);
}
const context = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
Buffer.from(`v${useVersion}`).copy(context);
const purposeId = this.hashStringToId(keyPurpose);
const derivedKey = Buffer.alloc(keySize);
sodium.crypto_kdf_derive_from_key(
derivedKey,
purposeId,
context,
masterKey
);
return {
key: derivedKey,
version: useVersion,
purpose: keyPurpose
};
}
// Clean up old versions (keep only recent versions)
cleanupOldVersions(keepCount = 3) {
const versions = Array.from(this.masterKeys.keys()).sort((a, b) => b - a);
for (let i = keepCount; i < versions.length; i++) {
const oldVersion = versions[i];
const oldKey = this.masterKeys.get(oldVersion);
if (oldKey) {
sodium.sodium_memzero(oldKey);
this.masterKeys.delete(oldVersion);
}
}
}
hashStringToId(str) {
const hash = Buffer.alloc(4);
sodium.crypto_generichash(hash, Buffer.from(str));
return hash.readUInt32LE(0);
}
}
// Usage
const versionedKeys = new VersionedKeyManager();
// Get current keys
const currentDbKey = versionedKeys.deriveKey('database-encryption');
const currentApiKey = versionedKeys.deriveKey('api-signing');
console.log(`Database key version: ${currentDbKey.version}`);
// Rotate keys
const newVersion = versionedKeys.rotateKeys();
console.log(`Rotated to version: ${newVersion}`);
// Get new keys
const newDbKey = versionedKeys.deriveKey('database-encryption');
console.log(`New database key version: ${newDbKey.version}`);
// Still can access old keys for decryption
const oldDbKey = versionedKeys.deriveKey('database-encryption', currentDbKey.version);
console.log('Old key still accessible:', oldDbKey.key.equals(currentDbKey.key));Install with Tessl CLI
npx tessl i tessl/npm-sodium-native