CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-sodium-native

Low level bindings for libsodium cryptographic library

Pending
Overview
Eval results
Files

kdf.mddocs/

Key Derivation Functions

Key derivation functions for generating multiple keys from a single master key with proper domain separation and context binding.

Capabilities

Master Key Generation

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);

Key Derivation

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);

Constants

// 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;

Security Considerations

  • Master Key Security: Protect the master key as it can derive all subkeys.
  • Context Uniqueness: Use unique contexts for different applications or purposes.
  • Subkey ID Management: Use systematic subkey ID assignment to avoid conflicts.
  • Key Rotation: Consider rotating master keys periodically for long-term security.

Common Patterns

Application Key Management

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();

User-specific Key Derivation

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);

Hierarchical Key Derivation

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));

Key Versioning System

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

docs

aead.md

auth.md

box.md

ed25519.md

hash.md

index.md

kdf.md

kx.md

memory.md

pwhash.md

random.md

secretbox.md

secretstream.md

shorthash.md

sign.md

stream.md

tile.json