/src/cryptsetup/lib/luks2/luks2_keyslot_luks2.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * LUKS - Linux Unified Key Setup v2, LUKS2 type keyslot handler |
4 | | * |
5 | | * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. |
6 | | * Copyright (C) 2015-2025 Milan Broz |
7 | | */ |
8 | | |
9 | | #include <limits.h> |
10 | | #include "luks2_internal.h" |
11 | | |
12 | | /* FIXME: move keyslot encryption to crypto backend */ |
13 | | #include "../luks1/af.h" |
14 | | |
15 | 0 | #define LUKS_SALTSIZE 32 |
16 | | #define LUKS_SLOT_ITERATIONS_MIN 1000 |
17 | | |
18 | | /* Serialize memory-hard keyslot access: optional workaround for parallel processing */ |
19 | 0 | #define MIN_MEMORY_FOR_SERIALIZE_LOCK_KB 32*1024 /* 32MB */ |
20 | | |
21 | | /* coverity[ -taint_source : arg-0 ] */ |
22 | | static int luks2_encrypt_to_storage(char *src, size_t srcLength, |
23 | | const char *cipher, const char *cipher_mode, |
24 | | struct volume_key *vk, unsigned int sector, |
25 | | struct crypt_device *cd) |
26 | 0 | { |
27 | | #if !ENABLE_AF_ALG /* Support for old kernel without Crypto API */ |
28 | | return LUKS_encrypt_to_storage(src, srcLength, cipher, cipher_mode, vk, sector, cd); |
29 | | #else |
30 | 0 | struct crypt_storage *s; |
31 | 0 | int devfd, r; |
32 | 0 | struct device *device = crypt_metadata_device(cd); |
33 | | |
34 | | /* Only whole sector writes supported */ |
35 | 0 | if (MISALIGNED_512(srcLength)) |
36 | 0 | return -EINVAL; |
37 | | |
38 | | /* Encrypt buffer */ |
39 | 0 | r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, |
40 | 0 | crypt_volume_key_get_key(vk), crypt_volume_key_length(vk), false); |
41 | 0 | if (r) { |
42 | 0 | log_err(cd, _("Cannot use %s-%s cipher for keyslot encryption."), cipher, cipher_mode); |
43 | 0 | return r; |
44 | 0 | } |
45 | | |
46 | 0 | r = crypt_storage_encrypt(s, 0, srcLength, src); |
47 | 0 | crypt_storage_destroy(s); |
48 | 0 | if (r) { |
49 | 0 | log_err(cd, _("IO error while encrypting keyslot.")); |
50 | 0 | return r; |
51 | 0 | } |
52 | | |
53 | 0 | devfd = device_open_locked(cd, device, O_RDWR); |
54 | 0 | if (devfd >= 0) { |
55 | 0 | if (write_lseek_blockwise(devfd, device_block_size(cd, device), |
56 | 0 | device_alignment(device), src, |
57 | 0 | srcLength, sector * SECTOR_SIZE) < 0) |
58 | 0 | r = -EIO; |
59 | 0 | else |
60 | 0 | r = 0; |
61 | |
|
62 | 0 | device_sync(cd, device); |
63 | 0 | } else |
64 | 0 | r = -EIO; |
65 | |
|
66 | 0 | if (r) |
67 | 0 | log_err(cd, _("IO error while encrypting keyslot.")); |
68 | |
|
69 | 0 | return r; |
70 | 0 | #endif |
71 | 0 | } |
72 | | |
73 | | static int luks2_decrypt_from_storage(char *dst, size_t dstLength, |
74 | | const char *cipher, const char *cipher_mode, struct volume_key *vk, |
75 | | unsigned int sector, struct crypt_device *cd) |
76 | 0 | { |
77 | 0 | struct device *device = crypt_metadata_device(cd); |
78 | | #if !ENABLE_AF_ALG /* Support for old kernel without Crypto API */ |
79 | | int r = device_read_lock(cd, device); |
80 | | if (r) { |
81 | | log_err(cd, _("Failed to acquire read lock on device %s."), device_path(device)); |
82 | | return r; |
83 | | } |
84 | | r = LUKS_decrypt_from_storage(dst, dstLength, cipher, cipher_mode, vk, sector, cd); |
85 | | device_read_unlock(cd, crypt_metadata_device(cd)); |
86 | | return r; |
87 | | #else |
88 | 0 | struct crypt_storage *s; |
89 | 0 | int devfd, r; |
90 | | |
91 | | /* Only whole sector writes supported */ |
92 | 0 | if (MISALIGNED_512(dstLength)) |
93 | 0 | return -EINVAL; |
94 | | |
95 | 0 | r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, |
96 | 0 | crypt_volume_key_get_key(vk), |
97 | 0 | crypt_volume_key_length(vk), false); |
98 | 0 | if (r) { |
99 | 0 | log_err(cd, _("Cannot use %s-%s cipher for keyslot encryption."), cipher, cipher_mode); |
100 | 0 | return r; |
101 | 0 | } |
102 | | |
103 | 0 | r = device_read_lock(cd, device); |
104 | 0 | if (r) { |
105 | 0 | log_err(cd, _("Failed to acquire read lock on device %s."), |
106 | 0 | device_path(device)); |
107 | 0 | crypt_storage_destroy(s); |
108 | 0 | return r; |
109 | 0 | } |
110 | | |
111 | 0 | devfd = device_open_locked(cd, device, O_RDONLY); |
112 | 0 | if (devfd >= 0) { |
113 | 0 | if (read_lseek_blockwise(devfd, device_block_size(cd, device), |
114 | 0 | device_alignment(device), dst, |
115 | 0 | dstLength, sector * SECTOR_SIZE) < 0) |
116 | 0 | r = -EIO; |
117 | 0 | else |
118 | 0 | r = 0; |
119 | 0 | } else |
120 | 0 | r = -EIO; |
121 | |
|
122 | 0 | device_read_unlock(cd, device); |
123 | | |
124 | | /* Decrypt buffer */ |
125 | 0 | if (!r) |
126 | 0 | r = crypt_storage_decrypt(s, 0, dstLength, dst); |
127 | 0 | else |
128 | 0 | log_err(cd, _("IO error while decrypting keyslot.")); |
129 | |
|
130 | 0 | crypt_storage_destroy(s); |
131 | 0 | return r; |
132 | 0 | #endif |
133 | 0 | } |
134 | | |
135 | | static int luks2_keyslot_get_pbkdf_params(json_object *jobj_keyslot, |
136 | | struct crypt_pbkdf_type *pbkdf, char **salt) |
137 | 0 | { |
138 | 0 | json_object *jobj_kdf, *jobj1, *jobj2; |
139 | 0 | size_t salt_len; |
140 | 0 | int r; |
141 | |
|
142 | 0 | if (!jobj_keyslot || !pbkdf) |
143 | 0 | return -EINVAL; |
144 | | |
145 | 0 | memset(pbkdf, 0, sizeof(*pbkdf)); |
146 | |
|
147 | 0 | if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf)) |
148 | 0 | return -EINVAL; |
149 | | |
150 | 0 | if (!json_object_object_get_ex(jobj_kdf, "type", &jobj1)) |
151 | 0 | return -EINVAL; |
152 | 0 | pbkdf->type = json_object_get_string(jobj1); |
153 | 0 | if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) { |
154 | 0 | if (!json_object_object_get_ex(jobj_kdf, "hash", &jobj2)) |
155 | 0 | return -EINVAL; |
156 | 0 | pbkdf->hash = json_object_get_string(jobj2); |
157 | 0 | if (!json_object_object_get_ex(jobj_kdf, "iterations", &jobj2)) |
158 | 0 | return -EINVAL; |
159 | 0 | pbkdf->iterations = json_object_get_int(jobj2); |
160 | 0 | pbkdf->max_memory_kb = 0; |
161 | 0 | pbkdf->parallel_threads = 0; |
162 | 0 | } else { |
163 | 0 | if (!json_object_object_get_ex(jobj_kdf, "time", &jobj2)) |
164 | 0 | return -EINVAL; |
165 | 0 | pbkdf->iterations = json_object_get_int(jobj2); |
166 | 0 | if (!json_object_object_get_ex(jobj_kdf, "memory", &jobj2)) |
167 | 0 | return -EINVAL; |
168 | 0 | pbkdf->max_memory_kb = json_object_get_int(jobj2); |
169 | 0 | if (!json_object_object_get_ex(jobj_kdf, "cpus", &jobj2)) |
170 | 0 | return -EINVAL; |
171 | 0 | pbkdf->parallel_threads = json_object_get_int(jobj2); |
172 | 0 | } |
173 | | |
174 | 0 | if (!json_object_object_get_ex(jobj_kdf, "salt", &jobj2)) |
175 | 0 | return -EINVAL; |
176 | | |
177 | 0 | r = crypt_base64_decode(salt, &salt_len, json_object_get_string(jobj2), |
178 | 0 | json_object_get_string_len(jobj2)); |
179 | 0 | if (r < 0) |
180 | 0 | return r; |
181 | | |
182 | 0 | if (salt_len != LUKS_SALTSIZE) { |
183 | 0 | free(*salt); |
184 | 0 | return -EINVAL; |
185 | 0 | } |
186 | | |
187 | 0 | return 0; |
188 | 0 | } |
189 | | |
190 | | static int luks2_keyslot_set_key(struct crypt_device *cd, |
191 | | json_object *jobj_keyslot, |
192 | | const char *password, size_t passwordLen, |
193 | | const char *volume_key, size_t volume_key_len) |
194 | 0 | { |
195 | 0 | char *salt = NULL, cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; |
196 | 0 | char *AfKey = NULL; |
197 | 0 | const char *af_hash = NULL; |
198 | 0 | size_t AFEKSize, keyslot_key_len; |
199 | 0 | json_object *jobj2, *jobj_kdf, *jobj_af, *jobj_area; |
200 | 0 | uint64_t area_offset; |
201 | 0 | struct crypt_pbkdf_type pbkdf; |
202 | 0 | int r; |
203 | 0 | struct volume_key *derived_vk = NULL; |
204 | 0 | void *derived_key = NULL; |
205 | |
|
206 | 0 | if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) || |
207 | 0 | !json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) || |
208 | 0 | !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) |
209 | 0 | return -EINVAL; |
210 | | |
211 | | /* prevent accidental volume key size change after allocation */ |
212 | 0 | if (!json_object_object_get_ex(jobj_keyslot, "key_size", &jobj2)) |
213 | 0 | return -EINVAL; |
214 | 0 | if (json_object_get_int(jobj2) != (int)volume_key_len) |
215 | 0 | return -EINVAL; |
216 | | |
217 | 0 | if (!json_object_object_get_ex(jobj_area, "offset", &jobj2)) |
218 | 0 | return -EINVAL; |
219 | 0 | area_offset = crypt_jobj_get_uint64(jobj2); |
220 | |
|
221 | 0 | if (!json_object_object_get_ex(jobj_area, "encryption", &jobj2)) |
222 | 0 | return -EINVAL; |
223 | 0 | r = crypt_parse_name_and_mode(json_object_get_string(jobj2), cipher, NULL, cipher_mode); |
224 | 0 | if (r < 0) |
225 | 0 | return r; |
226 | | |
227 | 0 | if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2)) |
228 | 0 | return -EINVAL; |
229 | 0 | keyslot_key_len = json_object_get_int(jobj2); |
230 | |
|
231 | 0 | if (!json_object_object_get_ex(jobj_af, "hash", &jobj2)) |
232 | 0 | return -EINVAL; |
233 | 0 | af_hash = json_object_get_string(jobj2); |
234 | |
|
235 | 0 | r = luks2_keyslot_get_pbkdf_params(jobj_keyslot, &pbkdf, &salt); |
236 | 0 | if (r < 0) |
237 | 0 | return r; |
238 | | |
239 | | /* |
240 | | * Allocate derived key storage. |
241 | | */ |
242 | 0 | derived_key = crypt_safe_alloc(keyslot_key_len); |
243 | 0 | if (!derived_key) { |
244 | 0 | free(salt); |
245 | 0 | return -ENOMEM; |
246 | 0 | } |
247 | | /* |
248 | | * Calculate keyslot content, split and store it to keyslot area. |
249 | | */ |
250 | 0 | log_dbg(cd, "Running keyslot key derivation."); |
251 | 0 | r = crypt_pbkdf(pbkdf.type, pbkdf.hash, password, passwordLen, |
252 | 0 | salt, LUKS_SALTSIZE, |
253 | 0 | derived_key, keyslot_key_len, |
254 | 0 | pbkdf.iterations, pbkdf.max_memory_kb, |
255 | 0 | pbkdf.parallel_threads); |
256 | 0 | free(salt); |
257 | 0 | if (r < 0) { |
258 | 0 | if ((crypt_backend_flags() & CRYPT_BACKEND_PBKDF2_INT) && |
259 | 0 | pbkdf.iterations > INT_MAX) |
260 | 0 | log_err(cd, _("PBKDF2 iteration value overflow.")); |
261 | 0 | if (r == -ENOMEM) |
262 | 0 | log_err(cd, _("Not enough memory for keyslot key derivation.")); |
263 | 0 | goto out; |
264 | 0 | } |
265 | | |
266 | | // FIXME: verity key_size to AFEKSize |
267 | 0 | AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE; |
268 | 0 | AfKey = crypt_safe_alloc(AFEKSize); |
269 | 0 | if (!AfKey) { |
270 | 0 | r = -ENOMEM; |
271 | 0 | goto out; |
272 | 0 | } |
273 | | |
274 | 0 | r = crypt_hash_size(af_hash); |
275 | 0 | if (r < 0) |
276 | 0 | log_err(cd, _("Hash algorithm %s is not available."), af_hash); |
277 | 0 | else |
278 | 0 | r = AF_split(cd, volume_key, AfKey, volume_key_len, LUKS_STRIPES, af_hash); |
279 | |
|
280 | 0 | if (r < 0) |
281 | 0 | goto out; |
282 | | |
283 | 0 | derived_vk = crypt_alloc_volume_key_by_safe_alloc(&derived_key); |
284 | 0 | if (!derived_vk) { |
285 | 0 | r = -ENOMEM; |
286 | 0 | goto out; |
287 | 0 | } |
288 | | |
289 | 0 | log_dbg(cd, "Updating keyslot area [0x%04" PRIx64 "].", area_offset); |
290 | | /* FIXME: sector_offset should be size_t, fix LUKS_encrypt... accordingly */ |
291 | 0 | r = luks2_encrypt_to_storage(AfKey, AFEKSize, cipher, cipher_mode, |
292 | 0 | derived_vk, (unsigned)(area_offset / SECTOR_SIZE), cd); |
293 | 0 | out: |
294 | 0 | crypt_safe_free(AfKey); |
295 | 0 | crypt_safe_free(derived_key); |
296 | 0 | crypt_free_volume_key(derived_vk); |
297 | 0 | if (r < 0) |
298 | 0 | return r; |
299 | | |
300 | 0 | return 0; |
301 | 0 | } |
302 | | |
303 | | static int luks2_keyslot_get_key(struct crypt_device *cd, |
304 | | json_object *jobj_keyslot, |
305 | | const char *password, size_t passwordLen, |
306 | | char *volume_key, size_t volume_key_len) |
307 | 0 | { |
308 | 0 | struct crypt_pbkdf_type pbkdf; |
309 | 0 | char *AfKey = NULL; |
310 | 0 | size_t AFEKSize; |
311 | 0 | const char *af_hash = NULL; |
312 | 0 | char *salt = NULL, cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; |
313 | 0 | json_object *jobj2, *jobj_af, *jobj_area; |
314 | 0 | uint64_t area_offset; |
315 | 0 | size_t keyslot_key_len; |
316 | 0 | bool try_serialize_lock = false; |
317 | 0 | int r; |
318 | 0 | struct volume_key *derived_vk = NULL; |
319 | 0 | void *derived_key = NULL; |
320 | |
|
321 | 0 | if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) || |
322 | 0 | !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) |
323 | 0 | return -EINVAL; |
324 | | |
325 | 0 | if (!json_object_object_get_ex(jobj_af, "hash", &jobj2)) |
326 | 0 | return -EINVAL; |
327 | 0 | af_hash = json_object_get_string(jobj2); |
328 | |
|
329 | 0 | if (!json_object_object_get_ex(jobj_area, "offset", &jobj2)) |
330 | 0 | return -EINVAL; |
331 | 0 | area_offset = crypt_jobj_get_uint64(jobj2); |
332 | |
|
333 | 0 | if (!json_object_object_get_ex(jobj_area, "encryption", &jobj2)) |
334 | 0 | return -EINVAL; |
335 | 0 | r = crypt_parse_name_and_mode(json_object_get_string(jobj2), cipher, NULL, cipher_mode); |
336 | 0 | if (r < 0) |
337 | 0 | return r; |
338 | | |
339 | | /* Allow only empty passphrase with null cipher */ |
340 | 0 | if (crypt_is_cipher_null(cipher) && passwordLen) |
341 | 0 | return -EPERM; |
342 | | |
343 | 0 | if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2)) |
344 | 0 | return -EINVAL; |
345 | 0 | keyslot_key_len = json_object_get_int(jobj2); |
346 | |
|
347 | 0 | r = luks2_keyslot_get_pbkdf_params(jobj_keyslot, &pbkdf, &salt); |
348 | 0 | if (r < 0) |
349 | 0 | return r; |
350 | | |
351 | | /* |
352 | | * Allocate derived key storage space. |
353 | | */ |
354 | 0 | derived_key = crypt_safe_alloc(keyslot_key_len); |
355 | 0 | if (!derived_key) { |
356 | 0 | r = -ENOMEM; |
357 | 0 | goto out; |
358 | 0 | } |
359 | | |
360 | 0 | AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE; |
361 | 0 | AfKey = crypt_safe_alloc(AFEKSize); |
362 | 0 | if (!AfKey) { |
363 | 0 | r = -ENOMEM; |
364 | 0 | goto out; |
365 | 0 | } |
366 | | |
367 | | /* |
368 | | * If requested, serialize unlocking for memory-hard KDF. Usually NOOP. |
369 | | */ |
370 | 0 | if (pbkdf.max_memory_kb > MIN_MEMORY_FOR_SERIALIZE_LOCK_KB) |
371 | 0 | try_serialize_lock = true; |
372 | 0 | if (try_serialize_lock && (r = crypt_serialize_lock(cd))) |
373 | 0 | goto out; |
374 | | |
375 | | /* |
376 | | * Calculate derived key, decrypt keyslot content and merge it. |
377 | | */ |
378 | 0 | log_dbg(cd, "Running keyslot key derivation."); |
379 | 0 | r = crypt_pbkdf(pbkdf.type, pbkdf.hash, password, passwordLen, |
380 | 0 | salt, LUKS_SALTSIZE, |
381 | 0 | derived_key, keyslot_key_len, |
382 | 0 | pbkdf.iterations, pbkdf.max_memory_kb, |
383 | 0 | pbkdf.parallel_threads); |
384 | |
|
385 | 0 | if (try_serialize_lock) |
386 | 0 | crypt_serialize_unlock(cd); |
387 | |
|
388 | 0 | if (r < 0) |
389 | 0 | goto out; |
390 | | |
391 | 0 | derived_vk = crypt_alloc_volume_key_by_safe_alloc(&derived_key); |
392 | 0 | if (!derived_vk) { |
393 | 0 | r = -ENOMEM; |
394 | 0 | goto out; |
395 | 0 | } |
396 | | |
397 | 0 | log_dbg(cd, "Reading keyslot area [0x%04" PRIx64 "].", area_offset); |
398 | | /* FIXME: sector_offset should be size_t, fix LUKS_decrypt... accordingly */ |
399 | 0 | r = luks2_decrypt_from_storage(AfKey, AFEKSize, cipher, cipher_mode, |
400 | 0 | derived_vk, (unsigned)(area_offset / SECTOR_SIZE), cd); |
401 | |
|
402 | 0 | if (r == 0) { |
403 | 0 | r = crypt_hash_size(af_hash); |
404 | 0 | if (r < 0) |
405 | 0 | log_err(cd, _("Hash algorithm %s is not available."), af_hash); |
406 | 0 | else |
407 | 0 | r = AF_merge(AfKey, volume_key, volume_key_len, LUKS_STRIPES, af_hash); |
408 | 0 | } |
409 | 0 | out: |
410 | 0 | free(salt); |
411 | 0 | crypt_free_volume_key(derived_vk); |
412 | 0 | crypt_safe_free(AfKey); |
413 | 0 | crypt_safe_free(derived_key); |
414 | |
|
415 | 0 | return r; |
416 | 0 | } |
417 | | |
418 | | /* |
419 | | * currently we support update of only: |
420 | | * |
421 | | * - af hash function |
422 | | * - kdf params |
423 | | */ |
424 | | static int luks2_keyslot_update_json(struct crypt_device *cd, |
425 | | json_object *jobj_keyslot, |
426 | | const struct luks2_keyslot_params *params) |
427 | 0 | { |
428 | 0 | const struct crypt_pbkdf_type *pbkdf; |
429 | 0 | json_object *jobj_af, *jobj_area, *jobj_kdf; |
430 | 0 | char salt[LUKS_SALTSIZE], *salt_base64 = NULL; |
431 | 0 | int r; |
432 | | |
433 | | /* jobj_keyslot is not yet validated */ |
434 | |
|
435 | 0 | if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) || |
436 | 0 | !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) |
437 | 0 | return -EINVAL; |
438 | | |
439 | | /* update area encryption parameters */ |
440 | 0 | json_object_object_add(jobj_area, "encryption", json_object_new_string(params->area.raw.encryption)); |
441 | 0 | json_object_object_add(jobj_area, "key_size", json_object_new_int(params->area.raw.key_size)); |
442 | |
|
443 | 0 | pbkdf = crypt_get_pbkdf_type(cd); |
444 | 0 | if (!pbkdf) |
445 | 0 | return -EINVAL; |
446 | | |
447 | 0 | r = crypt_benchmark_pbkdf_internal(cd, CONST_CAST(struct crypt_pbkdf_type *)pbkdf, params->area.raw.key_size); |
448 | 0 | if (r < 0) |
449 | 0 | return r; |
450 | | |
451 | | /* refresh whole 'kdf' object */ |
452 | 0 | jobj_kdf = json_object_new_object(); |
453 | 0 | if (!jobj_kdf) |
454 | 0 | return -ENOMEM; |
455 | 0 | json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type)); |
456 | 0 | if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) { |
457 | 0 | json_object_object_add(jobj_kdf, "hash", json_object_new_string(pbkdf->hash)); |
458 | 0 | json_object_object_add(jobj_kdf, "iterations", json_object_new_int(pbkdf->iterations)); |
459 | 0 | } else { |
460 | 0 | json_object_object_add(jobj_kdf, "time", json_object_new_int(pbkdf->iterations)); |
461 | 0 | json_object_object_add(jobj_kdf, "memory", json_object_new_int(pbkdf->max_memory_kb)); |
462 | 0 | json_object_object_add(jobj_kdf, "cpus", json_object_new_int(pbkdf->parallel_threads)); |
463 | 0 | } |
464 | 0 | json_object_object_add(jobj_keyslot, "kdf", jobj_kdf); |
465 | | |
466 | | /* |
467 | | * Regenerate salt and add it in 'kdf' object |
468 | | */ |
469 | 0 | r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT); |
470 | 0 | if (r < 0) |
471 | 0 | return r; |
472 | 0 | r = crypt_base64_encode(&salt_base64, NULL, salt, LUKS_SALTSIZE); |
473 | 0 | if (r < 0) |
474 | 0 | return r; |
475 | 0 | json_object_object_add(jobj_kdf, "salt", json_object_new_string(salt_base64)); |
476 | 0 | free(salt_base64); |
477 | | |
478 | | /* update 'af' hash */ |
479 | 0 | json_object_object_add(jobj_af, "hash", json_object_new_string(params->af.luks1.hash)); |
480 | |
|
481 | 0 | JSON_DBG(cd, jobj_keyslot, "Keyslot JSON:"); |
482 | 0 | return 0; |
483 | 0 | } |
484 | | |
485 | | static int luks2_keyslot_alloc(struct crypt_device *cd, |
486 | | int keyslot, |
487 | | size_t volume_key_len, |
488 | | const struct luks2_keyslot_params *params) |
489 | 0 | { |
490 | 0 | struct luks2_hdr *hdr; |
491 | 0 | uint64_t area_offset, area_length; |
492 | 0 | json_object *jobj_keyslots, *jobj_keyslot, *jobj_af, *jobj_area; |
493 | 0 | int r; |
494 | |
|
495 | 0 | log_dbg(cd, "Trying to allocate LUKS2 keyslot %d.", keyslot); |
496 | |
|
497 | 0 | if (!params || params->area_type != LUKS2_KEYSLOT_AREA_RAW || |
498 | 0 | params->af_type != LUKS2_KEYSLOT_AF_LUKS1) { |
499 | 0 | log_dbg(cd, "Invalid LUKS2 keyslot parameters."); |
500 | 0 | return -EINVAL; |
501 | 0 | } |
502 | | |
503 | 0 | if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) |
504 | 0 | return -EINVAL; |
505 | | |
506 | 0 | if (keyslot == CRYPT_ANY_SLOT) |
507 | 0 | keyslot = LUKS2_keyslot_find_empty(cd, hdr, 0); |
508 | |
|
509 | 0 | if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX) |
510 | 0 | return -ENOMEM; |
511 | | |
512 | 0 | if (LUKS2_get_keyslot_jobj(hdr, keyslot)) { |
513 | 0 | log_dbg(cd, "Cannot modify already active keyslot %d.", keyslot); |
514 | 0 | return -EINVAL; |
515 | 0 | } |
516 | | |
517 | 0 | if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots)) |
518 | 0 | return -EINVAL; |
519 | | |
520 | 0 | r = LUKS2_find_area_gap(cd, hdr, volume_key_len, &area_offset, &area_length); |
521 | 0 | if (r < 0) { |
522 | 0 | log_err(cd, _("No space for new keyslot.")); |
523 | 0 | return r; |
524 | 0 | } |
525 | | |
526 | 0 | jobj_keyslot = json_object_new_object(); |
527 | 0 | if (!jobj_keyslot) { |
528 | 0 | r = -ENOMEM; |
529 | 0 | goto err; |
530 | 0 | } |
531 | | |
532 | 0 | json_object_object_add(jobj_keyslot, "type", json_object_new_string("luks2")); |
533 | 0 | json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(volume_key_len)); |
534 | | |
535 | | /* AF object */ |
536 | 0 | jobj_af = json_object_new_object(); |
537 | 0 | if (!jobj_af) { |
538 | 0 | r = -ENOMEM; |
539 | 0 | goto err; |
540 | 0 | } |
541 | | |
542 | 0 | json_object_object_add(jobj_af, "type", json_object_new_string("luks1")); |
543 | 0 | json_object_object_add(jobj_af, "stripes", json_object_new_int(params->af.luks1.stripes)); |
544 | 0 | json_object_object_add(jobj_keyslot, "af", jobj_af); |
545 | | |
546 | | /* Area object */ |
547 | 0 | jobj_area = json_object_new_object(); |
548 | 0 | if (!jobj_area) { |
549 | 0 | r = -ENOMEM; |
550 | 0 | goto err; |
551 | 0 | } |
552 | | |
553 | 0 | json_object_object_add(jobj_area, "type", json_object_new_string("raw")); |
554 | 0 | json_object_object_add(jobj_area, "offset", crypt_jobj_new_uint64(area_offset)); |
555 | 0 | json_object_object_add(jobj_area, "size", crypt_jobj_new_uint64(area_length)); |
556 | 0 | json_object_object_add(jobj_keyslot, "area", jobj_area); |
557 | |
|
558 | 0 | r = json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot); |
559 | 0 | if (r) { |
560 | 0 | json_object_put(jobj_keyslot); |
561 | 0 | return r; |
562 | 0 | } |
563 | | |
564 | 0 | r = luks2_keyslot_update_json(cd, jobj_keyslot, params); |
565 | |
|
566 | 0 | if (!r && LUKS2_check_json_size(cd, hdr)) { |
567 | 0 | log_dbg(cd, "Not enough space in header json area for new keyslot."); |
568 | 0 | r = -ENOSPC; |
569 | 0 | } |
570 | |
|
571 | 0 | if (r) |
572 | 0 | json_object_object_del_by_uint(jobj_keyslots, keyslot); |
573 | |
|
574 | 0 | return r; |
575 | 0 | err: |
576 | 0 | json_object_put(jobj_keyslot); |
577 | 0 | return r; |
578 | 0 | } |
579 | | |
580 | | static int luks2_keyslot_open(struct crypt_device *cd, |
581 | | int keyslot, |
582 | | const char *password, |
583 | | size_t password_len, |
584 | | char *volume_key, |
585 | | size_t volume_key_len) |
586 | 0 | { |
587 | 0 | struct luks2_hdr *hdr; |
588 | 0 | json_object *jobj_keyslot; |
589 | |
|
590 | 0 | log_dbg(cd, "Trying to open LUKS2 keyslot %d.", keyslot); |
591 | |
|
592 | 0 | if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) |
593 | 0 | return -EINVAL; |
594 | | |
595 | 0 | jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); |
596 | 0 | if (!jobj_keyslot) |
597 | 0 | return -EINVAL; |
598 | | |
599 | 0 | return luks2_keyslot_get_key(cd, jobj_keyslot, |
600 | 0 | password, password_len, |
601 | 0 | volume_key, volume_key_len); |
602 | 0 | } |
603 | | |
604 | | /* |
605 | | * This function must not modify json. |
606 | | * It's called after luks2 keyslot validation. |
607 | | */ |
608 | | static int luks2_keyslot_store(struct crypt_device *cd, |
609 | | int keyslot, |
610 | | const char *password, |
611 | | size_t password_len, |
612 | | const char *volume_key, |
613 | | size_t volume_key_len) |
614 | 0 | { |
615 | 0 | struct luks2_hdr *hdr; |
616 | 0 | json_object *jobj_keyslot; |
617 | 0 | int r; |
618 | |
|
619 | 0 | log_dbg(cd, "Calculating attributes for LUKS2 keyslot %d.", keyslot); |
620 | |
|
621 | 0 | if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) |
622 | 0 | return -EINVAL; |
623 | | |
624 | 0 | jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); |
625 | 0 | if (!jobj_keyslot) |
626 | 0 | return -EINVAL; |
627 | | |
628 | 0 | r = LUKS2_device_write_lock(cd, hdr, crypt_metadata_device(cd)); |
629 | 0 | if(r) |
630 | 0 | return r; |
631 | | |
632 | 0 | r = luks2_keyslot_set_key(cd, jobj_keyslot, |
633 | 0 | password, password_len, |
634 | 0 | volume_key, volume_key_len); |
635 | 0 | if (!r) |
636 | 0 | r = LUKS2_hdr_write(cd, hdr); |
637 | |
|
638 | 0 | device_write_unlock(cd, crypt_metadata_device(cd)); |
639 | |
|
640 | 0 | return r < 0 ? r : keyslot; |
641 | 0 | } |
642 | | |
643 | | static int luks2_keyslot_wipe(struct crypt_device *cd, int keyslot) |
644 | 0 | { |
645 | 0 | struct luks2_hdr *hdr; |
646 | |
|
647 | 0 | if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) |
648 | 0 | return -EINVAL; |
649 | | |
650 | | /* Remove any reference of deleted keyslot from digests and tokens */ |
651 | 0 | LUKS2_digest_assign(cd, hdr, keyslot, CRYPT_ANY_DIGEST, 0, 0); |
652 | 0 | LUKS2_token_assign(cd, hdr, keyslot, CRYPT_ANY_TOKEN, 0, 0); |
653 | |
|
654 | 0 | return 0; |
655 | 0 | } |
656 | | |
657 | | static int luks2_keyslot_dump(struct crypt_device *cd, int keyslot) |
658 | 0 | { |
659 | 0 | json_object *jobj_keyslot, *jobj1, *jobj_kdf, *jobj_af, *jobj_area; |
660 | |
|
661 | 0 | jobj_keyslot = LUKS2_get_keyslot_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), keyslot); |
662 | 0 | if (!jobj_keyslot) |
663 | 0 | return -EINVAL; |
664 | | |
665 | 0 | if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) || |
666 | 0 | !json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) || |
667 | 0 | !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) |
668 | 0 | return -EINVAL; |
669 | | |
670 | 0 | json_object_object_get_ex(jobj_area, "encryption", &jobj1); |
671 | 0 | log_std(cd, "\tCipher: %s\n", json_object_get_string(jobj1)); |
672 | |
|
673 | 0 | json_object_object_get_ex(jobj_area, "key_size", &jobj1); |
674 | 0 | log_std(cd, "\tCipher key: %u bits\n", crypt_jobj_get_uint32(jobj1) * 8); |
675 | |
|
676 | 0 | json_object_object_get_ex(jobj_kdf, "type", &jobj1); |
677 | 0 | log_std(cd, "\tPBKDF: %s\n", json_object_get_string(jobj1)); |
678 | |
|
679 | 0 | if (!strcmp(json_object_get_string(jobj1), CRYPT_KDF_PBKDF2)) { |
680 | 0 | json_object_object_get_ex(jobj_kdf, "hash", &jobj1); |
681 | 0 | log_std(cd, "\tHash: %s\n", json_object_get_string(jobj1)); |
682 | |
|
683 | 0 | json_object_object_get_ex(jobj_kdf, "iterations", &jobj1); |
684 | 0 | log_std(cd, "\tIterations: %" PRIu64 "\n", crypt_jobj_get_uint64(jobj1)); |
685 | 0 | } else { |
686 | 0 | json_object_object_get_ex(jobj_kdf, "time", &jobj1); |
687 | 0 | log_std(cd, "\tTime cost: %" PRIu64 "\n", json_object_get_int64(jobj1)); |
688 | |
|
689 | 0 | json_object_object_get_ex(jobj_kdf, "memory", &jobj1); |
690 | 0 | log_std(cd, "\tMemory: %" PRIu64 "\n", json_object_get_int64(jobj1)); |
691 | |
|
692 | 0 | json_object_object_get_ex(jobj_kdf, "cpus", &jobj1); |
693 | 0 | log_std(cd, "\tThreads: %" PRIu64 "\n", json_object_get_int64(jobj1)); |
694 | 0 | } |
695 | 0 | json_object_object_get_ex(jobj_kdf, "salt", &jobj1); |
696 | 0 | log_std(cd, "\tSalt: "); |
697 | 0 | hexprint_base64(cd, jobj1, " ", " "); |
698 | | |
699 | |
|
700 | 0 | json_object_object_get_ex(jobj_af, "stripes", &jobj1); |
701 | 0 | log_std(cd, "\tAF stripes: %u\n", json_object_get_int(jobj1)); |
702 | |
|
703 | 0 | json_object_object_get_ex(jobj_af, "hash", &jobj1); |
704 | 0 | log_std(cd, "\tAF hash: %s\n", json_object_get_string(jobj1)); |
705 | |
|
706 | 0 | json_object_object_get_ex(jobj_area, "offset", &jobj1); |
707 | 0 | log_std(cd, "\tArea offset:%" PRIu64 " [bytes]\n", crypt_jobj_get_uint64(jobj1)); |
708 | |
|
709 | 0 | json_object_object_get_ex(jobj_area, "size", &jobj1); |
710 | 0 | log_std(cd, "\tArea length:%" PRIu64 " [bytes]\n", crypt_jobj_get_uint64(jobj1)); |
711 | |
|
712 | 0 | return 0; |
713 | 0 | } |
714 | | |
715 | | static int luks2_keyslot_validate(struct crypt_device *cd, json_object *jobj_keyslot) |
716 | 0 | { |
717 | 0 | json_object *jobj_kdf, *jobj_af, *jobj_area, *jobj1; |
718 | 0 | const char *type; |
719 | 0 | int count; |
720 | |
|
721 | 0 | if (!jobj_keyslot) |
722 | 0 | return -EINVAL; |
723 | | |
724 | 0 | if (!(jobj_kdf = json_contains(cd, jobj_keyslot, "", "keyslot", "kdf", json_type_object)) || |
725 | 0 | !(jobj_af = json_contains(cd, jobj_keyslot, "", "keyslot", "af", json_type_object)) || |
726 | 0 | !(jobj_area = json_contains(cd, jobj_keyslot, "", "keyslot", "area", json_type_object))) |
727 | 0 | return -EINVAL; |
728 | | |
729 | 0 | count = json_object_object_length(jobj_kdf); |
730 | |
|
731 | 0 | jobj1 = json_contains_string(cd, jobj_kdf, "", "kdf section", "type"); |
732 | 0 | if (!jobj1) |
733 | 0 | return -EINVAL; |
734 | 0 | type = json_object_get_string(jobj1); |
735 | |
|
736 | 0 | if (!strcmp(type, CRYPT_KDF_PBKDF2)) { |
737 | 0 | if (count != 4 || /* type, salt, hash, iterations only */ |
738 | 0 | !json_contains_string(cd, jobj_kdf, "kdf type", type, "hash") || |
739 | 0 | !json_contains(cd, jobj_kdf, "kdf type", type, "iterations", json_type_int) || |
740 | 0 | !json_contains_string(cd, jobj_kdf, "kdf type", type, "salt")) |
741 | 0 | return -EINVAL; |
742 | 0 | } else if (!strcmp(type, CRYPT_KDF_ARGON2I) || !strcmp(type, CRYPT_KDF_ARGON2ID)) { |
743 | 0 | if (count != 5 || /* type, salt, time, memory, cpus only */ |
744 | 0 | !json_contains(cd, jobj_kdf, "kdf type", type, "time", json_type_int) || |
745 | 0 | !json_contains(cd, jobj_kdf, "kdf type", type, "memory", json_type_int) || |
746 | 0 | !json_contains(cd, jobj_kdf, "kdf type", type, "cpus", json_type_int) || |
747 | 0 | !json_contains_string(cd, jobj_kdf, "kdf type", type, "salt")) |
748 | 0 | return -EINVAL; |
749 | 0 | } |
750 | | |
751 | 0 | jobj1 = json_contains_string(cd, jobj_af, "", "af section", "type"); |
752 | 0 | if (!jobj1) |
753 | 0 | return -EINVAL; |
754 | 0 | type = json_object_get_string(jobj1); |
755 | |
|
756 | 0 | if (!strcmp(type, "luks1")) { |
757 | 0 | if (!json_contains_string(cd, jobj_af, "", "luks1 af", "hash") || |
758 | 0 | !json_contains(cd, jobj_af, "", "luks1 af", "stripes", json_type_int)) |
759 | 0 | return -EINVAL; |
760 | 0 | } else |
761 | 0 | return -EINVAL; |
762 | | |
763 | | // FIXME check numbered |
764 | 0 | jobj1 = json_contains_string(cd, jobj_area, "", "area section", "type"); |
765 | 0 | if (!jobj1) |
766 | 0 | return -EINVAL; |
767 | 0 | type = json_object_get_string(jobj1); |
768 | |
|
769 | 0 | if (!strcmp(type, "raw")) { |
770 | 0 | if (!json_contains_string(cd, jobj_area, "area", "raw type", "encryption") || |
771 | 0 | !json_contains(cd, jobj_area, "area", "raw type", "key_size", json_type_int) || |
772 | 0 | !json_contains_string(cd, jobj_area, "area", "raw type", "offset") || |
773 | 0 | !json_contains_string(cd, jobj_area, "area", "raw type", "size")) |
774 | 0 | return -EINVAL; |
775 | 0 | } else |
776 | 0 | return -EINVAL; |
777 | | |
778 | 0 | return 0; |
779 | 0 | } |
780 | | |
781 | | static int luks2_keyslot_update(struct crypt_device *cd, |
782 | | int keyslot, |
783 | | const struct luks2_keyslot_params *params) |
784 | 0 | { |
785 | 0 | struct luks2_hdr *hdr; |
786 | 0 | json_object *jobj_keyslot; |
787 | 0 | int r; |
788 | |
|
789 | 0 | log_dbg(cd, "Updating LUKS2 keyslot %d.", keyslot); |
790 | |
|
791 | 0 | if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) |
792 | 0 | return -EINVAL; |
793 | | |
794 | 0 | jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); |
795 | 0 | if (!jobj_keyslot) |
796 | 0 | return -EINVAL; |
797 | | |
798 | 0 | r = luks2_keyslot_update_json(cd, jobj_keyslot, params); |
799 | |
|
800 | 0 | if (!r && LUKS2_check_json_size(cd, hdr)) { |
801 | 0 | log_dbg(cd, "Not enough space in header json area for updated keyslot %d.", keyslot); |
802 | 0 | r = -ENOSPC; |
803 | 0 | } |
804 | |
|
805 | 0 | return r; |
806 | 0 | } |
807 | | |
808 | | static void luks2_keyslot_repair(json_object *jobj_keyslot) |
809 | 232 | { |
810 | 232 | const char *type; |
811 | 232 | json_object *jobj_kdf, *jobj_type; |
812 | | |
813 | 232 | if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) || |
814 | 174 | !json_object_is_type(jobj_kdf, json_type_object)) |
815 | 58 | return; |
816 | | |
817 | 174 | if (!json_object_object_get_ex(jobj_kdf, "type", &jobj_type) || |
818 | 160 | !json_object_is_type(jobj_type, json_type_string)) |
819 | 24 | return; |
820 | | |
821 | 150 | type = json_object_get_string(jobj_type); |
822 | | |
823 | 150 | if (!strcmp(type, CRYPT_KDF_PBKDF2)) { |
824 | | /* type, salt, hash, iterations only */ |
825 | 467 | json_object_object_foreach(jobj_kdf, key, val) { |
826 | 467 | UNUSED(val); |
827 | 467 | if (!strcmp(key, "type") || !strcmp(key, "salt") || |
828 | 361 | !strcmp(key, "hash") || !strcmp(key, "iterations")) |
829 | 170 | continue; |
830 | 297 | json_object_object_del(jobj_kdf, key); |
831 | 297 | } |
832 | 94 | } else if (!strcmp(type, CRYPT_KDF_ARGON2I) || !strcmp(type, CRYPT_KDF_ARGON2ID)) { |
833 | | /* type, salt, time, memory, cpus only */ |
834 | 461 | json_object_object_foreach(jobj_kdf, key, val) { |
835 | 461 | UNUSED(val); |
836 | 461 | if (!strcmp(key, "type") || !strcmp(key, "salt") || |
837 | 324 | !strcmp(key, "time") || !strcmp(key, "memory") || |
838 | 212 | !strcmp(key, "cpus")) |
839 | 303 | continue; |
840 | 158 | json_object_object_del(jobj_kdf, key); |
841 | 158 | } |
842 | 71 | } |
843 | 150 | } |
844 | | |
845 | | const keyslot_handler luks2_keyslot = { |
846 | | .name = "luks2", |
847 | | .alloc = luks2_keyslot_alloc, |
848 | | .update = luks2_keyslot_update, |
849 | | .open = luks2_keyslot_open, |
850 | | .store = luks2_keyslot_store, |
851 | | .wipe = luks2_keyslot_wipe, |
852 | | .dump = luks2_keyslot_dump, |
853 | | .validate = luks2_keyslot_validate, |
854 | | .repair = luks2_keyslot_repair |
855 | | }; |