0
# Key Derivation Functions
1
2
Key derivation functions for generating multiple keys from a single master key with proper domain separation and context binding.
3
4
## Capabilities
5
6
### Master Key Generation
7
8
Generate a cryptographically secure master key for key derivation.
9
10
```javascript { .api }
11
/**
12
* Generate random master key for key derivation
13
* @param key - Output buffer for master key (must be KEYBYTES long)
14
*/
15
function crypto_kdf_keygen(key: Buffer): void;
16
```
17
18
**Usage Example:**
19
20
```javascript
21
const sodium = require('sodium-native');
22
23
// Generate master key
24
const masterKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
25
sodium.crypto_kdf_keygen(masterKey);
26
```
27
28
### Key Derivation
29
30
Derive subkeys from a master key with context and subkey ID for domain separation.
31
32
```javascript { .api }
33
/**
34
* Derive subkey from master key with context and ID
35
* @param subkey - Output buffer for derived subkey (size determines key length)
36
* @param subkeyId - Subkey identifier (numeric ID for key differentiation)
37
* @param ctx - Context buffer for domain separation (must be CONTEXTBYTES long)
38
* @param key - Master key buffer (must be KEYBYTES long)
39
* @throws Error if buffer sizes incorrect or derivation fails
40
*/
41
function crypto_kdf_derive_from_key(
42
subkey: Buffer,
43
subkeyId: number,
44
ctx: Buffer,
45
key: Buffer
46
): void;
47
```
48
49
**Usage Example:**
50
51
```javascript
52
const sodium = require('sodium-native');
53
54
// Generate master key
55
const masterKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
56
sodium.crypto_kdf_keygen(masterKey);
57
58
// Define context for domain separation
59
const context = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
60
Buffer.from('MyApp v1').copy(context); // Pad to exact size
61
62
// Derive different types of keys
63
const encryptionKey = Buffer.alloc(32); // 256-bit encryption key
64
const authKey = Buffer.alloc(32); // 256-bit authentication key
65
const signingKey = Buffer.alloc(64); // 512-bit signing key
66
67
sodium.crypto_kdf_derive_from_key(encryptionKey, 1, context, masterKey);
68
sodium.crypto_kdf_derive_from_key(authKey, 2, context, masterKey);
69
sodium.crypto_kdf_derive_from_key(signingKey, 3, context, masterKey);
70
71
console.log('Derived encryption key length:', encryptionKey.length);
72
console.log('Derived auth key length:', authKey.length);
73
console.log('Derived signing key length:', signingKey.length);
74
```
75
76
## Constants
77
78
```javascript { .api }
79
// Minimum derived key size in bytes
80
const crypto_kdf_BYTES_MIN: number;
81
82
// Maximum derived key size in bytes
83
const crypto_kdf_BYTES_MAX: number;
84
85
// Context size in bytes (for domain separation)
86
const crypto_kdf_CONTEXTBYTES: number;
87
88
// Master key size in bytes
89
const crypto_kdf_KEYBYTES: number;
90
```
91
92
## Security Considerations
93
94
- **Master Key Security**: Protect the master key as it can derive all subkeys.
95
- **Context Uniqueness**: Use unique contexts for different applications or purposes.
96
- **Subkey ID Management**: Use systematic subkey ID assignment to avoid conflicts.
97
- **Key Rotation**: Consider rotating master keys periodically for long-term security.
98
99
## Common Patterns
100
101
### Application Key Management
102
103
```javascript
104
const sodium = require('sodium-native');
105
106
class ApplicationKeyManager {
107
constructor(appName, version) {
108
// Generate or load master key
109
this.masterKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
110
sodium.crypto_kdf_keygen(this.masterKey);
111
112
// Create context from app name and version
113
this.context = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
114
const contextString = `${appName} ${version}`.padEnd(sodium.crypto_kdf_CONTEXTBYTES, '\0');
115
Buffer.from(contextString).copy(this.context, 0, 0, sodium.crypto_kdf_CONTEXTBYTES);
116
}
117
118
// Key types with assigned IDs
119
static KeyTypes = {
120
DATABASE_ENCRYPTION: 1,
121
SESSION_SIGNING: 2,
122
API_AUTHENTICATION: 3,
123
FILE_ENCRYPTION: 4,
124
LOG_INTEGRITY: 5,
125
BACKUP_ENCRYPTION: 6
126
};
127
128
deriveKey(keyType, keySize = 32) {
129
if (!ApplicationKeyManager.KeyTypes[keyType]) {
130
throw new Error(`Unknown key type: ${keyType}`);
131
}
132
133
if (keySize < sodium.crypto_kdf_BYTES_MIN || keySize > sodium.crypto_kdf_BYTES_MAX) {
134
throw new Error(`Key size must be between ${sodium.crypto_kdf_BYTES_MIN} and ${sodium.crypto_kdf_BYTES_MAX} bytes`);
135
}
136
137
const subkey = Buffer.alloc(keySize);
138
const keyId = ApplicationKeyManager.KeyTypes[keyType];
139
140
sodium.crypto_kdf_derive_from_key(subkey, keyId, this.context, this.masterKey);
141
return subkey;
142
}
143
144
// Derive keys for common cryptographic operations
145
getEncryptionKey() {
146
return this.deriveKey('DATABASE_ENCRYPTION', 32);
147
}
148
149
getSigningKey() {
150
return this.deriveKey('SESSION_SIGNING', 64);
151
}
152
153
getAuthKey() {
154
return this.deriveKey('API_AUTHENTICATION', 32);
155
}
156
157
// Export master key for backup (encrypt before storing)
158
exportMasterKey() {
159
return Buffer.from(this.masterKey);
160
}
161
162
// Import master key from backup
163
importMasterKey(masterKeyData) {
164
Buffer.from(masterKeyData).copy(this.masterKey);
165
}
166
}
167
168
// Usage
169
const keyManager = new ApplicationKeyManager('SecureApp', 'v2.1');
170
171
const dbKey = keyManager.getEncryptionKey();
172
const apiKey = keyManager.getAuthKey();
173
const sessionKey = keyManager.getSigningKey();
174
```
175
176
### User-specific Key Derivation
177
178
```javascript
179
const sodium = require('sodium-native');
180
181
class UserKeyDerivation {
182
constructor(systemMasterKey) {
183
this.systemMasterKey = Buffer.from(systemMasterKey);
184
}
185
186
// Derive user-specific master key
187
deriveUserMasterKey(userId) {
188
const userContext = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
189
Buffer.from('UserKeys').copy(userContext);
190
191
const userMasterKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
192
sodium.crypto_kdf_derive_from_key(
193
userMasterKey,
194
userId,
195
userContext,
196
this.systemMasterKey
197
);
198
199
return userMasterKey;
200
}
201
202
// Derive specific keys for a user
203
deriveUserKeys(userId) {
204
const userMasterKey = this.deriveUserMasterKey(userId);
205
206
const userContext = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
207
Buffer.from('UserData').copy(userContext);
208
209
// Derive different keys for user data
210
const fileKey = Buffer.alloc(32);
211
const profileKey = Buffer.alloc(32);
212
const settingsKey = Buffer.alloc(32);
213
214
sodium.crypto_kdf_derive_from_key(fileKey, 1, userContext, userMasterKey);
215
sodium.crypto_kdf_derive_from_key(profileKey, 2, userContext, userMasterKey);
216
sodium.crypto_kdf_derive_from_key(settingsKey, 3, userContext, userMasterKey);
217
218
// Clean up user master key from memory
219
sodium.sodium_memzero(userMasterKey);
220
221
return {
222
fileEncryption: fileKey,
223
profileEncryption: profileKey,
224
settingsEncryption: settingsKey
225
};
226
}
227
}
228
229
// Usage
230
const systemKey = 'system-master-key-32-bytes-long!!!';
231
const userKdf = new UserKeyDerivation(systemKey);
232
233
const user123Keys = userKdf.deriveUserKeys(123);
234
const user456Keys = userKdf.deriveUserKeys(456);
235
```
236
237
### Hierarchical Key Derivation
238
239
```javascript
240
const sodium = require('sodium-native');
241
242
class HierarchicalKeyDerivation {
243
constructor() {
244
// Root master key
245
this.rootKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
246
sodium.crypto_kdf_keygen(this.rootKey);
247
}
248
249
// Derive domain-specific master keys
250
deriveDomainKey(domain) {
251
const domainContext = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
252
Buffer.from('Domains').copy(domainContext);
253
254
const domainKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
255
const domainId = this.hashStringToId(domain);
256
257
sodium.crypto_kdf_derive_from_key(
258
domainKey,
259
domainId,
260
domainContext,
261
this.rootKey
262
);
263
264
return domainKey;
265
}
266
267
// Derive service keys within a domain
268
deriveServiceKey(domain, service) {
269
const domainKey = this.deriveDomainKey(domain);
270
271
const serviceContext = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
272
Buffer.from(`${domain}`).copy(serviceContext);
273
274
const serviceKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
275
const serviceId = this.hashStringToId(service);
276
277
sodium.crypto_kdf_derive_from_key(
278
serviceKey,
279
serviceId,
280
serviceContext,
281
domainKey
282
);
283
284
// Clean up domain key
285
sodium.sodium_memzero(domainKey);
286
287
return serviceKey;
288
}
289
290
// Derive operational keys for specific purposes
291
deriveOperationalKey(domain, service, operation, keySize = 32) {
292
const serviceKey = this.deriveServiceKey(domain, service);
293
294
const opContext = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
295
Buffer.from(`${service}`).copy(opContext);
296
297
const operationalKey = Buffer.alloc(keySize);
298
const operationId = this.hashStringToId(operation);
299
300
sodium.crypto_kdf_derive_from_key(
301
operationalKey,
302
operationId,
303
opContext,
304
serviceKey
305
);
306
307
// Clean up service key
308
sodium.sodium_memzero(serviceKey);
309
310
return operationalKey;
311
}
312
313
// Helper to convert string to numeric ID
314
hashStringToId(str) {
315
const hash = Buffer.alloc(4);
316
sodium.crypto_generichash(hash, Buffer.from(str));
317
return hash.readUInt32LE(0);
318
}
319
}
320
321
// Usage
322
const hkdf = new HierarchicalKeyDerivation();
323
324
// Derive keys at different levels
325
const authServiceKey = hkdf.deriveServiceKey('production', 'auth-service');
326
const dbEncryptionKey = hkdf.deriveOperationalKey('production', 'database', 'encryption');
327
const logSigningKey = hkdf.deriveOperationalKey('production', 'logging', 'signing', 64);
328
329
// Same operations will always produce same keys
330
const dbEncryptionKey2 = hkdf.deriveOperationalKey('production', 'database', 'encryption');
331
console.log('Keys match:', dbEncryptionKey.equals(dbEncryptionKey2));
332
```
333
334
### Key Versioning System
335
336
```javascript
337
const sodium = require('sodium-native');
338
339
class VersionedKeyManager {
340
constructor() {
341
this.masterKeys = new Map(); // version -> master key
342
this.currentVersion = 1;
343
344
// Initialize with first version
345
this.generateNewVersion();
346
}
347
348
generateNewVersion() {
349
const masterKey = Buffer.alloc(sodium.crypto_kdf_KEYBYTES);
350
sodium.crypto_kdf_keygen(masterKey);
351
352
this.masterKeys.set(this.currentVersion, masterKey);
353
return this.currentVersion;
354
}
355
356
rotateKeys() {
357
this.currentVersion++;
358
return this.generateNewVersion();
359
}
360
361
deriveKey(keyPurpose, version = null, keySize = 32) {
362
const useVersion = version || this.currentVersion;
363
const masterKey = this.masterKeys.get(useVersion);
364
365
if (!masterKey) {
366
throw new Error(`No master key for version ${useVersion}`);
367
}
368
369
const context = Buffer.alloc(sodium.crypto_kdf_CONTEXTBYTES);
370
Buffer.from(`v${useVersion}`).copy(context);
371
372
const purposeId = this.hashStringToId(keyPurpose);
373
const derivedKey = Buffer.alloc(keySize);
374
375
sodium.crypto_kdf_derive_from_key(
376
derivedKey,
377
purposeId,
378
context,
379
masterKey
380
);
381
382
return {
383
key: derivedKey,
384
version: useVersion,
385
purpose: keyPurpose
386
};
387
}
388
389
// Clean up old versions (keep only recent versions)
390
cleanupOldVersions(keepCount = 3) {
391
const versions = Array.from(this.masterKeys.keys()).sort((a, b) => b - a);
392
393
for (let i = keepCount; i < versions.length; i++) {
394
const oldVersion = versions[i];
395
const oldKey = this.masterKeys.get(oldVersion);
396
397
if (oldKey) {
398
sodium.sodium_memzero(oldKey);
399
this.masterKeys.delete(oldVersion);
400
}
401
}
402
}
403
404
hashStringToId(str) {
405
const hash = Buffer.alloc(4);
406
sodium.crypto_generichash(hash, Buffer.from(str));
407
return hash.readUInt32LE(0);
408
}
409
}
410
411
// Usage
412
const versionedKeys = new VersionedKeyManager();
413
414
// Get current keys
415
const currentDbKey = versionedKeys.deriveKey('database-encryption');
416
const currentApiKey = versionedKeys.deriveKey('api-signing');
417
418
console.log(`Database key version: ${currentDbKey.version}`);
419
420
// Rotate keys
421
const newVersion = versionedKeys.rotateKeys();
422
console.log(`Rotated to version: ${newVersion}`);
423
424
// Get new keys
425
const newDbKey = versionedKeys.deriveKey('database-encryption');
426
console.log(`New database key version: ${newDbKey.version}`);
427
428
// Still can access old keys for decryption
429
const oldDbKey = versionedKeys.deriveKey('database-encryption', currentDbKey.version);
430
console.log('Old key still accessible:', oldDbKey.key.equals(currentDbKey.key));
431
```