0
# Key Derivation
1
2
SJCL provides multiple key derivation functions (KDFs) for converting passwords and other input material into cryptographic keys. These functions include PBKDF2, scrypt, and HKDF, each suited for different security requirements.
3
4
## Capabilities
5
6
### PBKDF2 (Password-Based Key Derivation Function 2)
7
8
The most commonly used password-based key derivation function, applying HMAC iteratively to derive keys from passwords.
9
10
```javascript { .api }
11
/**
12
* PBKDF2 key derivation function
13
* @param {string|BitArray} password - Password or key material
14
* @param {BitArray} salt - Random salt (recommended: at least 8 bytes)
15
* @param {number} count - Number of iterations (recommended: ≥10,000)
16
* @param {number} length - Desired key length in bits
17
* @param {Function} [Prff] - Pseudorandom function (default: HMAC-SHA256)
18
* @returns {BitArray} Derived key as bit array
19
* @throws {sjcl.exception.invalid} If parameters are invalid
20
*/
21
sjcl.misc.pbkdf2(password, salt, count, length, Prff);
22
```
23
24
**Usage Examples:**
25
26
```javascript
27
const sjcl = require('sjcl');
28
29
// Basic PBKDF2 key derivation
30
const password = "userPassword123";
31
const salt = sjcl.random.randomWords(4); // 16 bytes
32
const iterations = 100000; // High iteration count for security
33
const keyLength = 256; // 256-bit key
34
35
const derivedKey = sjcl.misc.pbkdf2(password, salt, iterations, keyLength);
36
const aes = new sjcl.cipher.aes(derivedKey);
37
38
// Using different hash functions
39
const derivedKeySHA1 = sjcl.misc.pbkdf2(
40
password,
41
salt,
42
iterations,
43
keyLength,
44
sjcl.misc.hmac.prototype.encrypt // Uses HMAC-SHA1
45
);
46
47
// Derive multiple keys from same password
48
function deriveKeys(password, salt, iterations) {
49
return {
50
encryptionKey: sjcl.misc.pbkdf2(password, salt, iterations, 256),
51
authKey: sjcl.misc.pbkdf2(password + "auth", salt, iterations, 256),
52
iv: sjcl.misc.pbkdf2(password + "iv", salt, iterations, 128)
53
};
54
}
55
56
const keys = deriveKeys("masterPassword", salt, 100000);
57
```
58
59
### scrypt
60
61
Memory-hard key derivation function designed to be resistant to hardware-based attacks by requiring significant memory usage.
62
63
```javascript { .api }
64
/**
65
* scrypt key derivation function
66
* @param {string|BitArray} password - Password or key material
67
* @param {BitArray} salt - Random salt
68
* @param {number} N - CPU/memory cost parameter (power of 2)
69
* @param {number} r - Block size parameter
70
* @param {number} p - Parallelization parameter
71
* @param {number} length - Desired key length in bits
72
* @param {Function} [Prff] - Pseudorandom function (default: HMAC-SHA256)
73
* @returns {BitArray} Derived key as bit array
74
* @throws {sjcl.exception.invalid} If parameters are invalid
75
*/
76
sjcl.misc.scrypt(password, salt, N, r, p, length, Prff);
77
```
78
79
**Usage Examples:**
80
81
```javascript
82
const sjcl = require('sjcl');
83
84
// Basic scrypt usage
85
const password = "strongPassword";
86
const salt = sjcl.random.randomWords(4);
87
const N = 16384; // CPU/memory cost (2^14)
88
const r = 8; // Block size
89
const p = 1; // Parallelization
90
const keyLength = 256;
91
92
const scryptKey = sjcl.misc.scrypt(password, salt, N, r, p, keyLength);
93
94
// Higher security settings (more memory/time intensive)
95
const secureKey = sjcl.misc.scrypt(
96
password,
97
salt,
98
32768, // N = 2^15 (more memory)
99
8, // r = 8
100
1, // p = 1
101
256 // 256-bit key
102
);
103
104
// Lighter settings for mobile/resource-constrained environments
105
const mobileKey = sjcl.misc.scrypt(
106
password,
107
salt,
108
4096, // N = 2^12 (less memory)
109
8, // r = 8
110
1, // p = 1
111
256 // 256-bit key
112
);
113
```
114
115
### HKDF (HMAC-based Key Derivation Function)
116
117
Key derivation function for expanding existing key material into multiple cryptographic keys.
118
119
```javascript { .api }
120
/**
121
* HKDF key derivation function
122
* @param {BitArray} ikm - Input key material (should have good entropy)
123
* @param {number} keyBitLength - Desired output key length in bits
124
* @param {BitArray} [salt] - Optional salt (if not provided, zero salt is used)
125
* @param {BitArray} [info] - Optional context/application info
126
* @param {Function} [Hash] - Hash function to use (default: SHA-256)
127
* @returns {BitArray} Derived key as bit array
128
* @throws {sjcl.exception.invalid} If parameters are invalid
129
*/
130
sjcl.misc.hkdf(ikm, keyBitLength, salt, info, Hash);
131
```
132
133
**Usage Examples:**
134
135
```javascript
136
const sjcl = require('sjcl');
137
138
// Basic HKDF usage
139
const inputKeyMaterial = sjcl.random.randomWords(8); // 256 bits of random data
140
const salt = sjcl.random.randomWords(4); // Optional salt
141
const info = sjcl.codec.utf8String.toBits("MyApp-EncryptionKey-v1");
142
143
const derivedKey = sjcl.misc.hkdf(inputKeyMaterial, 256, salt, info);
144
145
// Derive multiple keys from shared secret
146
function deriveMultipleKeys(sharedSecret) {
147
const salt = sjcl.random.randomWords(4);
148
149
return {
150
encKey: sjcl.misc.hkdf(
151
sharedSecret,
152
256,
153
salt,
154
sjcl.codec.utf8String.toBits("encryption")
155
),
156
macKey: sjcl.misc.hkdf(
157
sharedSecret,
158
256,
159
salt,
160
sjcl.codec.utf8String.toBits("authentication")
161
),
162
kdfKey: sjcl.misc.hkdf(
163
sharedSecret,
164
256,
165
salt,
166
sjcl.codec.utf8String.toBits("key-derivation")
167
)
168
};
169
}
170
171
// HKDF with different hash functions
172
const hkdfSHA512 = sjcl.misc.hkdf(
173
inputKeyMaterial,
174
512,
175
salt,
176
info,
177
sjcl.hash.sha512
178
);
179
```
180
181
## Cached PBKDF2
182
183
Optimized PBKDF2 implementation with automatic salt generation and caching capabilities.
184
185
```javascript { .api }
186
/**
187
* Cached PBKDF2 with automatic salt generation
188
* @param {string} password - Password for key derivation
189
* @param {Object} obj - Object containing salt and other parameters
190
* @returns {BitArray} Derived key as bit array
191
*/
192
sjcl.misc.cachedPbkdf2(password, obj);
193
```
194
195
**Usage Examples:**
196
197
```javascript
198
const sjcl = require('sjcl');
199
200
// Using cached PBKDF2
201
const password = "userPassword";
202
const params = {
203
salt: sjcl.random.randomWords(4),
204
iter: 100000
205
};
206
207
const cachedKey = sjcl.misc.cachedPbkdf2(password, params);
208
209
// The params object will be modified with additional metadata
210
console.log(params); // Contains salt, iter, and derived key info
211
```
212
213
## Security Recommendations
214
215
### PBKDF2
216
- **Minimum iterations**: 10,000 (preferably 100,000+)
217
- **Salt length**: At least 128 bits (4 words)
218
- **Unique salts**: Generate new salt for each password
219
- **Memory considerations**: PBKDF2 is not memory-hard
220
221
### scrypt
222
- **Parameter selection**: Balance security vs. performance
223
- **N parameter**: Should be power of 2, start with 16384
224
- **Memory usage**: N * r * 128 bytes
225
- **Mobile devices**: Use lower N values (4096-8192)
226
227
### HKDF
228
- **Input entropy**: Ensure IKM has sufficient entropy
229
- **Context separation**: Use different info for different purposes
230
- **Salt usage**: Optional but recommended for security
231
- **Key expansion**: Can generate multiple keys from one input
232
233
## Common Usage Patterns
234
235
### Password-to-Key Conversion
236
237
```javascript
238
const sjcl = require('sjcl');
239
240
function passwordToKey(password, options = {}) {
241
const salt = options.salt || sjcl.random.randomWords(4);
242
const iterations = options.iterations || 100000;
243
const keySize = options.keySize || 256;
244
245
// Use scrypt for higher security, PBKDF2 for compatibility
246
const useScrypt = options.useScrypt !== false;
247
248
let key;
249
if (useScrypt) {
250
const N = options.scryptN || 16384;
251
key = sjcl.misc.scrypt(password, salt, N, 8, 1, keySize);
252
} else {
253
key = sjcl.misc.pbkdf2(password, salt, iterations, keySize);
254
}
255
256
return {
257
key: key,
258
salt: salt,
259
iterations: iterations,
260
algorithm: useScrypt ? 'scrypt' : 'pbkdf2'
261
};
262
}
263
264
// Usage
265
const keyInfo = passwordToKey("userPassword", {
266
keySize: 256,
267
useScrypt: true,
268
scryptN: 32768
269
});
270
```
271
272
### Key Stretching for Weak Keys
273
274
```javascript
275
const sjcl = require('sjcl');
276
277
function stretchWeakKey(weakKey, targetLength = 256) {
278
const salt = sjcl.random.randomWords(4);
279
280
// Convert weak key to bit array if it's a string
281
const keyBits = typeof weakKey === 'string'
282
? sjcl.codec.utf8String.toBits(weakKey)
283
: weakKey;
284
285
// Use HKDF to expand the weak key
286
return sjcl.misc.hkdf(
287
keyBits,
288
targetLength,
289
salt,
290
sjcl.codec.utf8String.toBits("key-stretching")
291
);
292
}
293
294
// Usage
295
const weakKey = "short"; // Weak key material
296
const strongKey = stretchWeakKey(weakKey, 256);
297
```
298
299
## Performance Considerations
300
301
- **PBKDF2**: Fast, CPU-intensive only
302
- **scrypt**: Memory-hard, slower but more secure against hardware attacks
303
- **HKDF**: Very fast, for expanding existing good key material
304
- **Iteration counts**: Higher is more secure but slower
305
- **Memory usage**: scrypt requires N * r * 128 bytes of memory