/src/cryptsetup/lib/luks2/luks2_keyslot_reenc.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * LUKS - Linux Unified Key Setup v2, reencryption keyslot handler |
4 | | * |
5 | | * Copyright (C) 2016-2025 Red Hat, Inc. All rights reserved. |
6 | | * Copyright (C) 2016-2025 Ondrej Kozina |
7 | | */ |
8 | | |
9 | | #include "luks2_internal.h" |
10 | | |
11 | | static int reenc_keyslot_open(struct crypt_device *cd __attribute__((unused)), |
12 | | int keyslot __attribute__((unused)), |
13 | | const char *password __attribute__((unused)), |
14 | | size_t password_len __attribute__((unused)), |
15 | | char *volume_key __attribute__((unused)), |
16 | | size_t volume_key_len __attribute__((unused))) |
17 | 0 | { |
18 | 0 | return -ENOENT; |
19 | 0 | } |
20 | | |
21 | | static json_object *reencrypt_keyslot_area_jobj(struct crypt_device *cd, |
22 | | const struct crypt_params_reencrypt *params, |
23 | | size_t alignment, |
24 | | uint64_t area_offset, |
25 | | uint64_t area_length) |
26 | 0 | { |
27 | 0 | json_object *jobj_area = json_object_new_object(); |
28 | |
|
29 | 0 | if (!jobj_area || !params || !params->resilience) |
30 | 0 | return NULL; |
31 | | |
32 | 0 | json_object_object_add(jobj_area, "offset", crypt_jobj_new_uint64(area_offset)); |
33 | 0 | json_object_object_add(jobj_area, "size", crypt_jobj_new_uint64(area_length)); |
34 | 0 | json_object_object_add(jobj_area, "type", json_object_new_string(params->resilience)); |
35 | |
|
36 | 0 | if (!strcmp(params->resilience, "checksum")) { |
37 | 0 | log_dbg(cd, "Setting reencrypt keyslot for checksum protection."); |
38 | 0 | json_object_object_add(jobj_area, "hash", json_object_new_string(params->hash)); |
39 | 0 | json_object_object_add(jobj_area, "sector_size", json_object_new_int64(alignment)); |
40 | 0 | } else if (!strcmp(params->resilience, "journal")) { |
41 | 0 | log_dbg(cd, "Setting reencrypt keyslot for journal protection."); |
42 | 0 | } else if (!strcmp(params->resilience, "none")) { |
43 | 0 | log_dbg(cd, "Setting reencrypt keyslot for none protection."); |
44 | 0 | } else if (!strcmp(params->resilience, "datashift")) { |
45 | 0 | log_dbg(cd, "Setting reencrypt keyslot for datashift protection."); |
46 | 0 | json_object_object_add(jobj_area, "shift_size", |
47 | 0 | crypt_jobj_new_uint64(params->data_shift << SECTOR_SHIFT)); |
48 | 0 | } else if (!strcmp(params->resilience, "datashift-checksum")) { |
49 | 0 | log_dbg(cd, "Setting reencrypt keyslot for datashift and checksum protection."); |
50 | 0 | json_object_object_add(jobj_area, "hash", json_object_new_string(params->hash)); |
51 | 0 | json_object_object_add(jobj_area, "sector_size", json_object_new_int64(alignment)); |
52 | 0 | json_object_object_add(jobj_area, "shift_size", |
53 | 0 | crypt_jobj_new_uint64(params->data_shift << SECTOR_SHIFT)); |
54 | 0 | } else if (!strcmp(params->resilience, "datashift-journal")) { |
55 | 0 | log_dbg(cd, "Setting reencrypt keyslot for datashift and journal protection."); |
56 | 0 | json_object_object_add(jobj_area, "shift_size", |
57 | 0 | crypt_jobj_new_uint64(params->data_shift << SECTOR_SHIFT)); |
58 | 0 | } else { |
59 | 0 | json_object_put(jobj_area); |
60 | 0 | return NULL; |
61 | 0 | } |
62 | | |
63 | 0 | return jobj_area; |
64 | 0 | } |
65 | | |
66 | | static json_object *reencrypt_keyslot_area_jobj_update_block_size(struct crypt_device *cd, |
67 | | json_object *jobj_area, size_t alignment) |
68 | 0 | { |
69 | 0 | json_object *jobj_type, *jobj_area_new = NULL; |
70 | |
|
71 | 0 | if (!jobj_area || |
72 | 0 | !json_object_object_get_ex(jobj_area, "type", &jobj_type) || |
73 | 0 | (strcmp(json_object_get_string(jobj_type), "checksum") && |
74 | 0 | strcmp(json_object_get_string(jobj_type), "datashift-checksum"))) |
75 | 0 | return NULL; |
76 | | |
77 | 0 | if (json_object_copy(jobj_area, &jobj_area_new)) |
78 | 0 | return NULL; |
79 | | |
80 | 0 | log_dbg(cd, "Updating reencrypt resilience checksum block size."); |
81 | |
|
82 | 0 | json_object_object_add(jobj_area_new, "sector_size", json_object_new_int64(alignment)); |
83 | |
|
84 | 0 | return jobj_area_new; |
85 | 0 | } |
86 | | |
87 | | static int reenc_keyslot_alloc(struct crypt_device *cd, |
88 | | struct luks2_hdr *hdr, |
89 | | int keyslot, |
90 | | const struct crypt_params_reencrypt *params, |
91 | | size_t alignment) |
92 | 0 | { |
93 | 0 | int r; |
94 | 0 | json_object *jobj_keyslots, *jobj_keyslot, *jobj_area; |
95 | 0 | uint64_t area_offset, area_length; |
96 | |
|
97 | 0 | log_dbg(cd, "Allocating reencrypt keyslot %d.", keyslot); |
98 | |
|
99 | 0 | if (!params || !params->resilience || params->direction > CRYPT_REENCRYPT_BACKWARD) |
100 | 0 | return -EINVAL; |
101 | | |
102 | 0 | if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX) |
103 | 0 | return -ENOMEM; |
104 | | |
105 | 0 | if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots)) |
106 | 0 | return -EINVAL; |
107 | | |
108 | | /* only plain datashift resilience mode does not require additional storage */ |
109 | 0 | if (!strcmp(params->resilience, "datashift")) |
110 | 0 | r = LUKS2_find_area_gap(cd, hdr, 1, &area_offset, &area_length); |
111 | 0 | else |
112 | 0 | r = LUKS2_find_area_max_gap(cd, hdr, &area_offset, &area_length); |
113 | 0 | if (r < 0) |
114 | 0 | return r; |
115 | | |
116 | 0 | jobj_area = reencrypt_keyslot_area_jobj(cd, params, alignment, area_offset, area_length); |
117 | 0 | if (!jobj_area) |
118 | 0 | return -EINVAL; |
119 | | |
120 | 0 | jobj_keyslot = json_object_new_object(); |
121 | 0 | if (!jobj_keyslot) { |
122 | 0 | json_object_put(jobj_area); |
123 | 0 | return -ENOMEM; |
124 | 0 | } |
125 | 0 | json_object_object_add(jobj_keyslot, "area", jobj_area); |
126 | |
|
127 | 0 | json_object_object_add(jobj_keyslot, "type", json_object_new_string("reencrypt")); |
128 | 0 | json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(1)); /* useless but mandatory */ |
129 | 0 | json_object_object_add(jobj_keyslot, "mode", json_object_new_string(crypt_reencrypt_mode_to_str(params->mode))); |
130 | 0 | if (params->direction == CRYPT_REENCRYPT_FORWARD) |
131 | 0 | json_object_object_add(jobj_keyslot, "direction", json_object_new_string("forward")); |
132 | 0 | else |
133 | 0 | json_object_object_add(jobj_keyslot, "direction", json_object_new_string("backward")); |
134 | |
|
135 | 0 | r = json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot); |
136 | 0 | if (r) { |
137 | 0 | json_object_put(jobj_keyslot); |
138 | 0 | return r; |
139 | 0 | } |
140 | | |
141 | 0 | if (LUKS2_check_json_size(cd, hdr)) { |
142 | 0 | log_dbg(cd, "New keyslot too large to fit in free metadata space."); |
143 | 0 | json_object_object_del_by_uint(jobj_keyslots, keyslot); |
144 | 0 | return -ENOSPC; |
145 | 0 | } |
146 | | |
147 | 0 | JSON_DBG(cd, hdr->jobj, "JSON:"); |
148 | |
|
149 | 0 | return 0; |
150 | 0 | } |
151 | | |
152 | | static int reenc_keyslot_store_data(struct crypt_device *cd, |
153 | | json_object *jobj_keyslot, |
154 | | const void *buffer, size_t buffer_len) |
155 | 0 | { |
156 | 0 | int devfd, r; |
157 | 0 | json_object *jobj_area, *jobj_offset, *jobj_length; |
158 | 0 | uint64_t area_offset, area_length; |
159 | 0 | struct device *device = crypt_metadata_device(cd); |
160 | |
|
161 | 0 | if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) || |
162 | 0 | !json_object_object_get_ex(jobj_area, "offset", &jobj_offset) || |
163 | 0 | !json_object_object_get_ex(jobj_area, "size", &jobj_length)) |
164 | 0 | return -EINVAL; |
165 | | |
166 | 0 | area_offset = crypt_jobj_get_uint64(jobj_offset); |
167 | 0 | area_length = crypt_jobj_get_uint64(jobj_length); |
168 | |
|
169 | 0 | if (!area_offset || !area_length || ((uint64_t)buffer_len > area_length)) |
170 | 0 | return -EINVAL; |
171 | | |
172 | 0 | devfd = device_open_locked(cd, device, O_RDWR); |
173 | 0 | if (devfd >= 0) { |
174 | 0 | if (write_lseek_blockwise(devfd, device_block_size(cd, device), |
175 | 0 | device_alignment(device), CONST_CAST(void *)buffer, |
176 | 0 | buffer_len, area_offset) < 0) |
177 | 0 | r = -EIO; |
178 | 0 | else |
179 | 0 | r = 0; |
180 | 0 | } else |
181 | 0 | r = -EINVAL; |
182 | |
|
183 | 0 | if (r) |
184 | 0 | log_err(cd, _("IO error while encrypting keyslot.")); |
185 | |
|
186 | 0 | return r; |
187 | 0 | } |
188 | | |
189 | | static int reenc_keyslot_store(struct crypt_device *cd, |
190 | | int keyslot, |
191 | | const char *password __attribute__((unused)), |
192 | | size_t password_len __attribute__((unused)), |
193 | | const char *buffer, |
194 | | size_t buffer_len) |
195 | 0 | { |
196 | 0 | struct luks2_hdr *hdr; |
197 | 0 | json_object *jobj_keyslot; |
198 | 0 | int r = 0; |
199 | |
|
200 | 0 | if (!cd || !buffer || !buffer_len) |
201 | 0 | return -EINVAL; |
202 | | |
203 | 0 | if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) |
204 | 0 | return -EINVAL; |
205 | | |
206 | 0 | log_dbg(cd, "Reencrypt keyslot %d store.", keyslot); |
207 | |
|
208 | 0 | jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); |
209 | 0 | if (!jobj_keyslot) |
210 | 0 | return -EINVAL; |
211 | | |
212 | 0 | r = LUKS2_device_write_lock(cd, hdr, crypt_metadata_device(cd)); |
213 | 0 | if (r) |
214 | 0 | return r; |
215 | | |
216 | 0 | r = reenc_keyslot_store_data(cd, jobj_keyslot, buffer, buffer_len); |
217 | 0 | if (r < 0) { |
218 | 0 | device_write_unlock(cd, crypt_metadata_device(cd)); |
219 | 0 | return r; |
220 | 0 | } |
221 | | |
222 | 0 | r = LUKS2_hdr_write(cd, hdr); |
223 | |
|
224 | 0 | device_write_unlock(cd, crypt_metadata_device(cd)); |
225 | |
|
226 | 0 | return r < 0 ? r : keyslot; |
227 | 0 | } |
228 | | |
229 | | static int reenc_keyslot_wipe(struct crypt_device *cd, |
230 | | int keyslot) |
231 | 0 | { |
232 | 0 | struct luks2_hdr *hdr; |
233 | |
|
234 | 0 | if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) |
235 | 0 | return -EINVAL; |
236 | | |
237 | | /* remove reencryption verification data */ |
238 | 0 | LUKS2_digest_assign(cd, hdr, keyslot, CRYPT_ANY_DIGEST, 0, 0); |
239 | |
|
240 | 0 | return 0; |
241 | 0 | } |
242 | | |
243 | | static int reenc_keyslot_dump(struct crypt_device *cd, int keyslot) |
244 | 0 | { |
245 | 0 | json_object *jobj_keyslot, *jobj_area, *jobj_direction, *jobj_mode, *jobj_resilience, |
246 | 0 | *jobj1; |
247 | |
|
248 | 0 | jobj_keyslot = LUKS2_get_keyslot_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), keyslot); |
249 | 0 | if (!jobj_keyslot) |
250 | 0 | return -EINVAL; |
251 | | |
252 | 0 | if (!json_object_object_get_ex(jobj_keyslot, "direction", &jobj_direction) || |
253 | 0 | !json_object_object_get_ex(jobj_keyslot, "mode", &jobj_mode) || |
254 | 0 | !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) || |
255 | 0 | !json_object_object_get_ex(jobj_area, "type", &jobj_resilience)) |
256 | 0 | return -EINVAL; |
257 | | |
258 | 0 | log_std(cd, "\t%-12s%s\n", "Mode:", json_object_get_string(jobj_mode)); |
259 | 0 | log_std(cd, "\t%-12s%s\n", "Direction:", json_object_get_string(jobj_direction)); |
260 | 0 | log_std(cd, "\t%-12s%s\n", "Resilience:", json_object_get_string(jobj_resilience)); |
261 | |
|
262 | 0 | if (!strcmp(json_object_get_string(jobj_resilience), "checksum")) { |
263 | 0 | json_object_object_get_ex(jobj_area, "hash", &jobj1); |
264 | 0 | log_std(cd, "\t%-12s%s\n", "Hash:", json_object_get_string(jobj1)); |
265 | 0 | json_object_object_get_ex(jobj_area, "sector_size", &jobj1); |
266 | 0 | log_std(cd, "\t%-12s%d [bytes]\n", "Hash data:", json_object_get_int(jobj1)); |
267 | 0 | } else if (!strcmp(json_object_get_string(jobj_resilience), "datashift")) { |
268 | 0 | json_object_object_get_ex(jobj_area, "shift_size", &jobj1); |
269 | 0 | log_std(cd, "\t%-12s%" PRIu64 "[bytes]\n", "Shift size:", crypt_jobj_get_uint64(jobj1)); |
270 | 0 | } |
271 | |
|
272 | 0 | json_object_object_get_ex(jobj_area, "offset", &jobj1); |
273 | 0 | log_std(cd, "\tArea offset:%" PRIu64 " [bytes]\n", crypt_jobj_get_uint64(jobj1)); |
274 | |
|
275 | 0 | json_object_object_get_ex(jobj_area, "size", &jobj1); |
276 | 0 | log_std(cd, "\tArea length:%" PRIu64 " [bytes]\n", crypt_jobj_get_uint64(jobj1)); |
277 | |
|
278 | 0 | return 0; |
279 | 0 | } |
280 | | |
281 | | static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_keyslot) |
282 | 0 | { |
283 | 0 | json_object *jobj_mode, *jobj_area, *jobj_type, *jobj_shift_size, *jobj_hash, |
284 | 0 | *jobj_sector_size, *jobj_direction, *jobj_key_size; |
285 | 0 | const char *mode, *type, *direction; |
286 | 0 | uint32_t sector_size; |
287 | 0 | uint64_t shift_size; |
288 | | |
289 | | /* mode (string: encrypt,reencrypt,decrypt) |
290 | | * direction (string:) |
291 | | * area { |
292 | | * type: (string: datashift, journal, checksum, none, datashift-journal, datashift-checksum) |
293 | | * hash: (string: checksum and datashift-checksum types) |
294 | | * sector_size (uint32: checksum and datashift-checksum types) |
295 | | * shift_size (uint64: all datashift based types) |
296 | | * } |
297 | | */ |
298 | | |
299 | | /* area and area type are validated in general validation code */ |
300 | 0 | if (!jobj_keyslot || !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) || |
301 | 0 | !json_object_object_get_ex(jobj_area, "type", &jobj_type)) |
302 | 0 | return -EINVAL; |
303 | | |
304 | 0 | jobj_key_size = json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "key_size", json_type_int); |
305 | 0 | jobj_mode = json_contains_string(cd, jobj_keyslot, "", "reencrypt keyslot", "mode"); |
306 | 0 | jobj_direction = json_contains_string(cd, jobj_keyslot, "", "reencrypt keyslot", "direction"); |
307 | |
|
308 | 0 | if (!jobj_mode || !jobj_direction || !jobj_key_size) |
309 | 0 | return -EINVAL; |
310 | | |
311 | 0 | if (!validate_json_uint32(jobj_key_size) || crypt_jobj_get_uint32(jobj_key_size) != 1) { |
312 | 0 | log_dbg(cd, "Illegal reencrypt key size."); |
313 | 0 | return -EINVAL; |
314 | 0 | } |
315 | | |
316 | 0 | mode = json_object_get_string(jobj_mode); |
317 | 0 | type = json_object_get_string(jobj_type); |
318 | 0 | direction = json_object_get_string(jobj_direction); |
319 | |
|
320 | 0 | if (strcmp(mode, "reencrypt") && strcmp(mode, "encrypt") && |
321 | 0 | strcmp(mode, "decrypt")) { |
322 | 0 | log_dbg(cd, "Illegal reencrypt mode %s.", mode); |
323 | 0 | return -EINVAL; |
324 | 0 | } |
325 | | |
326 | 0 | if (strcmp(direction, "forward") && strcmp(direction, "backward")) { |
327 | 0 | log_dbg(cd, "Illegal reencrypt direction %s.", direction); |
328 | 0 | return -EINVAL; |
329 | 0 | } |
330 | | |
331 | 0 | if (!strcmp(type, "checksum") || !strcmp(type, "datashift-checksum")) { |
332 | 0 | jobj_hash = json_contains_string(cd, jobj_area, "type:checksum", |
333 | 0 | "Keyslot area", "hash"); |
334 | 0 | jobj_sector_size = json_contains(cd, jobj_area, "type:checksum", |
335 | 0 | "Keyslot area", "sector_size", json_type_int); |
336 | 0 | if (!jobj_hash || !jobj_sector_size) |
337 | 0 | return -EINVAL; |
338 | 0 | if (!validate_json_uint32(jobj_sector_size)) |
339 | 0 | return -EINVAL; |
340 | 0 | sector_size = crypt_jobj_get_uint32(jobj_sector_size); |
341 | 0 | if (sector_size < SECTOR_SIZE || NOTPOW2(sector_size)) { |
342 | 0 | log_dbg(cd, "Invalid sector_size (%" PRIu32 ") for checksum resilience mode.", |
343 | 0 | sector_size); |
344 | 0 | return -EINVAL; |
345 | 0 | } |
346 | 0 | } else if (!strcmp(type, "datashift") || |
347 | 0 | !strcmp(type, "datashift-checksum") || |
348 | 0 | !strcmp(type, "datashift-journal")) { |
349 | 0 | if (!(jobj_shift_size = json_contains_string(cd, jobj_area, "type:datashift", |
350 | 0 | "Keyslot area", "shift_size"))) |
351 | 0 | return -EINVAL; |
352 | | |
353 | 0 | shift_size = crypt_jobj_get_uint64(jobj_shift_size); |
354 | 0 | if (!shift_size) |
355 | 0 | return -EINVAL; |
356 | | |
357 | 0 | if (MISALIGNED_512(shift_size)) { |
358 | 0 | log_dbg(cd, "Shift size field has to be aligned to 512 bytes."); |
359 | 0 | return -EINVAL; |
360 | 0 | } |
361 | 0 | } |
362 | | |
363 | 0 | return 0; |
364 | 0 | } |
365 | | |
366 | | static int reenc_keyslot_update_needed(json_object *jobj_keyslot, |
367 | | const struct crypt_params_reencrypt *params, |
368 | | size_t alignment) |
369 | 0 | { |
370 | 0 | const char *type; |
371 | 0 | json_object *jobj_area, *jobj_type, *jobj; |
372 | |
|
373 | 0 | if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) || |
374 | 0 | !json_object_object_get_ex(jobj_area, "type", &jobj_type) || |
375 | 0 | !(type = json_object_get_string(jobj_type))) |
376 | 0 | return -EINVAL; |
377 | | |
378 | | /* |
379 | | * If no resilience mode change is requested and effective |
380 | | * resilience mode is 'checksum' then check alignment matches |
381 | | * stored checksum block size. |
382 | | */ |
383 | 0 | if (!params || !params->resilience) { |
384 | 0 | if (!strcmp(json_object_get_string(jobj_type), "checksum") || |
385 | 0 | !strcmp(json_object_get_string(jobj_type), "datashift-checksum")) |
386 | 0 | return (json_object_object_get_ex(jobj_area, "sector_size", &jobj) || |
387 | 0 | alignment != crypt_jobj_get_uint32(jobj)); |
388 | 0 | return 0; |
389 | 0 | } |
390 | | |
391 | 0 | if (strcmp(params->resilience, type)) |
392 | 0 | return 1; |
393 | | |
394 | 0 | if (!strcmp(type, "checksum") || |
395 | 0 | !strcmp(type, "datashift-checksum")) { |
396 | 0 | if (!params->hash) |
397 | 0 | return -EINVAL; |
398 | 0 | if (!json_object_object_get_ex(jobj_area, "hash", &jobj) || |
399 | 0 | strcmp(json_object_get_string(jobj), params->hash) || |
400 | 0 | !json_object_object_get_ex(jobj_area, "sector_size", &jobj) || |
401 | 0 | crypt_jobj_get_uint32(jobj) != alignment) |
402 | 0 | return 1; |
403 | 0 | } |
404 | | |
405 | 0 | if (!strncmp(type, "datashift", 9)) { |
406 | 0 | if (!json_object_object_get_ex(jobj_area, "shift_size", &jobj)) |
407 | 0 | return -EINVAL; |
408 | 0 | if ((params->data_shift << SECTOR_SHIFT) != crypt_jobj_get_uint64(jobj)) |
409 | 0 | return 1; |
410 | 0 | } |
411 | | |
412 | | /* nothing to compare with 'none' and 'journal' */ |
413 | 0 | return 0; |
414 | 0 | } |
415 | | |
416 | | static int load_checksum_protection(struct crypt_device *cd, |
417 | | json_object *jobj_area, |
418 | | uint64_t area_length, |
419 | | struct reenc_protection *rp) |
420 | 0 | { |
421 | 0 | int r; |
422 | 0 | json_object *jobj_hash, *jobj_block_size; |
423 | |
|
424 | 0 | if (!jobj_area || !rp || |
425 | 0 | !json_object_object_get_ex(jobj_area, "hash", &jobj_hash) || |
426 | 0 | !json_object_object_get_ex(jobj_area, "sector_size", &jobj_block_size)) |
427 | 0 | return -EINVAL; |
428 | | |
429 | 0 | r = snprintf(rp->p.csum.hash, sizeof(rp->p.csum.hash), "%s", json_object_get_string(jobj_hash)); |
430 | 0 | if (r < 0 || (size_t)r >= sizeof(rp->p.csum.hash)) |
431 | 0 | return -EINVAL; |
432 | | |
433 | 0 | if (crypt_hash_init(&rp->p.csum.ch, rp->p.csum.hash)) { |
434 | 0 | log_err(cd, _("Hash algorithm %s is not available."), rp->p.csum.hash); |
435 | 0 | return -EINVAL; |
436 | 0 | } |
437 | | |
438 | 0 | r = crypt_hash_size(rp->p.csum.hash); |
439 | 0 | if (r <= 0) { |
440 | 0 | crypt_hash_destroy(rp->p.csum.ch); |
441 | 0 | rp->p.csum.ch = NULL; |
442 | 0 | log_dbg(cd, "Invalid hash size"); |
443 | 0 | return -EINVAL; |
444 | 0 | } |
445 | | |
446 | 0 | rp->p.csum.hash_size = r; |
447 | 0 | rp->p.csum.block_size = crypt_jobj_get_uint32(jobj_block_size); |
448 | 0 | rp->p.csum.checksums_len = area_length; |
449 | |
|
450 | 0 | rp->type = REENC_PROTECTION_CHECKSUM; |
451 | 0 | return 0; |
452 | 0 | } |
453 | | |
454 | | static int reenc_keyslot_load_resilience_primary(struct crypt_device *cd, |
455 | | const char *type, |
456 | | json_object *jobj_area, |
457 | | uint64_t area_length, |
458 | | struct reenc_protection *rp) |
459 | 0 | { |
460 | 0 | json_object *jobj; |
461 | |
|
462 | 0 | if (!strcmp(type, "checksum")) { |
463 | 0 | log_dbg(cd, "Initializing checksum resilience mode."); |
464 | 0 | return load_checksum_protection(cd, jobj_area, area_length, rp); |
465 | 0 | } else if (!strcmp(type, "journal")) { |
466 | 0 | log_dbg(cd, "Initializing journal resilience mode."); |
467 | 0 | rp->type = REENC_PROTECTION_JOURNAL; |
468 | 0 | } else if (!strcmp(type, "none")) { |
469 | 0 | log_dbg(cd, "Initializing none resilience mode."); |
470 | 0 | rp->type = REENC_PROTECTION_NONE; |
471 | 0 | } else if (!strcmp(type, "datashift") || |
472 | 0 | !strcmp(type, "datashift-checksum") || |
473 | 0 | !strcmp(type, "datashift-journal")) { |
474 | 0 | log_dbg(cd, "Initializing datashift resilience mode."); |
475 | 0 | if (!json_object_object_get_ex(jobj_area, "shift_size", &jobj)) |
476 | 0 | return -EINVAL; |
477 | 0 | rp->type = REENC_PROTECTION_DATASHIFT; |
478 | 0 | rp->p.ds.data_shift = crypt_jobj_get_uint64(jobj); |
479 | 0 | } else |
480 | 0 | return -EINVAL; |
481 | | |
482 | 0 | return 0; |
483 | 0 | } |
484 | | |
485 | | static int reenc_keyslot_load_resilience_secondary(struct crypt_device *cd, |
486 | | const char *type, |
487 | | json_object *jobj_area, |
488 | | uint64_t area_length, |
489 | | struct reenc_protection *rp) |
490 | 0 | { |
491 | 0 | if (!strcmp(type, "datashift-checksum")) { |
492 | 0 | log_dbg(cd, "Initializing checksum resilience mode."); |
493 | 0 | return load_checksum_protection(cd, jobj_area, area_length, rp); |
494 | 0 | } else if (!strcmp(type, "datashift-journal")) { |
495 | 0 | log_dbg(cd, "Initializing journal resilience mode."); |
496 | 0 | rp->type = REENC_PROTECTION_JOURNAL; |
497 | 0 | } else |
498 | 0 | rp->type = REENC_PROTECTION_NOT_SET; |
499 | | |
500 | 0 | return 0; |
501 | 0 | } |
502 | | |
503 | | static int reenc_keyslot_load_resilience(struct crypt_device *cd, |
504 | | json_object *jobj_keyslot, |
505 | | struct reenc_protection *rp, |
506 | | bool primary) |
507 | 0 | { |
508 | 0 | const char *type; |
509 | 0 | int r; |
510 | 0 | json_object *jobj_area, *jobj_type; |
511 | 0 | uint64_t dummy, area_length; |
512 | |
|
513 | 0 | if (!rp || !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) || |
514 | 0 | !json_object_object_get_ex(jobj_area, "type", &jobj_type)) |
515 | 0 | return -EINVAL; |
516 | | |
517 | 0 | r = LUKS2_keyslot_jobj_area(jobj_keyslot, &dummy, &area_length); |
518 | 0 | if (r < 0) |
519 | 0 | return r; |
520 | | |
521 | 0 | type = json_object_get_string(jobj_type); |
522 | 0 | if (!type) |
523 | 0 | return -EINVAL; |
524 | | |
525 | 0 | if (primary) |
526 | 0 | return reenc_keyslot_load_resilience_primary(cd, type, jobj_area, area_length, rp); |
527 | 0 | else |
528 | 0 | return reenc_keyslot_load_resilience_secondary(cd, type, jobj_area, area_length, rp); |
529 | 0 | } |
530 | | |
531 | | static bool reenc_keyslot_update_is_valid(json_object *jobj_area, |
532 | | const struct crypt_params_reencrypt *params) |
533 | 0 | { |
534 | 0 | const char *type; |
535 | 0 | json_object *jobj_type, *jobj; |
536 | |
|
537 | 0 | if (!json_object_object_get_ex(jobj_area, "type", &jobj_type) || |
538 | 0 | !(type = json_object_get_string(jobj_type))) |
539 | 0 | return false; |
540 | | |
541 | | /* do not allow switch to/away from datashift resilience type */ |
542 | 0 | if ((strcmp(params->resilience, "datashift") && !strcmp(type, "datashift")) || |
543 | 0 | (!strcmp(params->resilience, "datashift") && strcmp(type, "datashift"))) |
544 | 0 | return false; |
545 | | |
546 | | /* do not allow switch to/away from datashift- resilience subvariants */ |
547 | 0 | if ((strncmp(params->resilience, "datashift-", 10) && |
548 | 0 | !strncmp(type, "datashift-", 10)) || |
549 | 0 | (!strncmp(params->resilience, "datashift-", 10) && |
550 | 0 | strncmp(type, "datashift-", 10))) |
551 | 0 | return false; |
552 | | |
553 | | /* datashift value is also immutable */ |
554 | 0 | if (!strncmp(type, "datashift", 9)) { |
555 | 0 | if (!json_object_object_get_ex(jobj_area, "shift_size", &jobj)) |
556 | 0 | return false; |
557 | 0 | return (params->data_shift << SECTOR_SHIFT) == crypt_jobj_get_uint64(jobj); |
558 | 0 | } |
559 | | |
560 | 0 | return true; |
561 | 0 | } |
562 | | |
563 | | static int reenc_keyslot_update(struct crypt_device *cd, |
564 | | json_object *jobj_keyslot, |
565 | | const struct crypt_params_reencrypt *params, |
566 | | size_t alignment) |
567 | 0 | { |
568 | 0 | int r; |
569 | 0 | json_object *jobj_area, *jobj_area_new; |
570 | 0 | uint64_t area_offset, area_length; |
571 | |
|
572 | 0 | if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) |
573 | 0 | return -EINVAL; |
574 | | |
575 | 0 | r = LUKS2_keyslot_jobj_area(jobj_keyslot, &area_offset, &area_length); |
576 | 0 | if (r < 0) |
577 | 0 | return r; |
578 | | |
579 | 0 | if (!params || !params->resilience) |
580 | 0 | jobj_area_new = reencrypt_keyslot_area_jobj_update_block_size(cd, jobj_area, alignment); |
581 | 0 | else { |
582 | 0 | if (!reenc_keyslot_update_is_valid(jobj_area, params)) { |
583 | 0 | log_err(cd, _("Invalid reencryption resilience mode change requested.")); |
584 | 0 | return -EINVAL; |
585 | 0 | } |
586 | | |
587 | 0 | jobj_area_new = reencrypt_keyslot_area_jobj(cd, params, alignment, |
588 | 0 | area_offset, area_length); |
589 | 0 | } |
590 | | |
591 | 0 | if (!jobj_area_new) |
592 | 0 | return -EINVAL; |
593 | | |
594 | | /* increase refcount for validation purposes */ |
595 | 0 | json_object_get(jobj_area); |
596 | |
|
597 | 0 | json_object_object_add(jobj_keyslot, "area", jobj_area_new); |
598 | |
|
599 | 0 | r = reenc_keyslot_validate(cd, jobj_keyslot); |
600 | 0 | if (r) { |
601 | | /* replace invalid object with previous valid one */ |
602 | 0 | json_object_object_add(jobj_keyslot, "area", jobj_area); |
603 | 0 | return -EINVAL; |
604 | 0 | } |
605 | | |
606 | | /* previous area object is no longer needed */ |
607 | 0 | json_object_put(jobj_area); |
608 | |
|
609 | 0 | return 0; |
610 | 0 | } |
611 | | |
612 | | int LUKS2_keyslot_reencrypt_allocate(struct crypt_device *cd, |
613 | | struct luks2_hdr *hdr, |
614 | | int keyslot, |
615 | | const struct crypt_params_reencrypt *params, |
616 | | size_t alignment) |
617 | 0 | { |
618 | 0 | int r; |
619 | |
|
620 | 0 | if (keyslot == CRYPT_ANY_SLOT) |
621 | 0 | return -EINVAL; |
622 | | |
623 | 0 | r = reenc_keyslot_alloc(cd, hdr, keyslot, params, alignment); |
624 | 0 | if (r < 0) |
625 | 0 | return r; |
626 | | |
627 | 0 | r = LUKS2_keyslot_priority_set(cd, hdr, keyslot, CRYPT_SLOT_PRIORITY_IGNORE, 0); |
628 | 0 | if (r < 0) |
629 | 0 | return r; |
630 | | |
631 | 0 | r = reenc_keyslot_validate(cd, LUKS2_get_keyslot_jobj(hdr, keyslot)); |
632 | 0 | if (r) { |
633 | 0 | log_dbg(cd, "Keyslot validation failed."); |
634 | 0 | return r; |
635 | 0 | } |
636 | | |
637 | 0 | return 0; |
638 | 0 | } |
639 | | |
640 | | int LUKS2_keyslot_reencrypt_update_needed(struct crypt_device *cd, |
641 | | struct luks2_hdr *hdr, |
642 | | int keyslot, |
643 | | const struct crypt_params_reencrypt *params, |
644 | | size_t alignment) |
645 | 0 | { |
646 | 0 | int r; |
647 | 0 | json_object *jobj_type, *jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); |
648 | |
|
649 | 0 | if (!jobj_keyslot || |
650 | 0 | !json_object_object_get_ex(jobj_keyslot, "type", &jobj_type) || |
651 | 0 | strcmp(json_object_get_string(jobj_type), "reencrypt")) |
652 | 0 | return -EINVAL; |
653 | | |
654 | 0 | r = reenc_keyslot_update_needed(jobj_keyslot, params, alignment); |
655 | 0 | if (!r) |
656 | 0 | log_dbg(cd, "No update of reencrypt keyslot needed."); |
657 | |
|
658 | 0 | return r; |
659 | 0 | } |
660 | | |
661 | | int LUKS2_keyslot_reencrypt_update(struct crypt_device *cd, |
662 | | struct luks2_hdr *hdr, |
663 | | int keyslot, |
664 | | const struct crypt_params_reencrypt *params, |
665 | | size_t alignment, |
666 | | struct volume_key *vks) |
667 | 0 | { |
668 | 0 | int r; |
669 | 0 | uint8_t version; |
670 | 0 | uint64_t max_size, moved_segment_size; |
671 | 0 | json_object *jobj_type, *jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); |
672 | 0 | struct reenc_protection check_rp = {}; |
673 | |
|
674 | 0 | if (!jobj_keyslot || |
675 | 0 | !json_object_object_get_ex(jobj_keyslot, "type", &jobj_type) || |
676 | 0 | strcmp(json_object_get_string(jobj_type), "reencrypt")) |
677 | 0 | return -EINVAL; |
678 | | |
679 | 0 | if (LUKS2_config_get_reencrypt_version(hdr, &version)) |
680 | 0 | return -EINVAL; |
681 | | |
682 | | /* verify existing reencryption metadata before updating */ |
683 | 0 | r = LUKS2_reencrypt_digest_verify(cd, hdr, vks); |
684 | 0 | if (r < 0) |
685 | 0 | return r; |
686 | | |
687 | 0 | r = reenc_keyslot_update(cd, jobj_keyslot, params, alignment); |
688 | 0 | if (r < 0) |
689 | 0 | return r; |
690 | | |
691 | 0 | r = reenc_keyslot_load_resilience(cd, jobj_keyslot, &check_rp, false); |
692 | 0 | if (r < 0) |
693 | 0 | return r; |
694 | | |
695 | 0 | if (check_rp.type != REENC_PROTECTION_NOT_SET) { |
696 | 0 | r = LUKS2_reencrypt_max_hotzone_size(cd, hdr, &check_rp, keyslot, &max_size); |
697 | 0 | LUKS2_reencrypt_protection_erase(&check_rp); |
698 | 0 | if (r < 0) |
699 | 0 | return r; |
700 | 0 | moved_segment_size = json_segment_get_size(LUKS2_get_segment_by_flag(hdr, "backup-moved-segment"), 0); |
701 | 0 | if (!moved_segment_size) |
702 | 0 | return -EINVAL; |
703 | 0 | if (moved_segment_size > max_size) { |
704 | 0 | log_err(cd, _("Can not update resilience type. " |
705 | 0 | "New type only provides %" PRIu64 " bytes, " |
706 | 0 | "required space is: %" PRIu64 " bytes."), |
707 | 0 | max_size, moved_segment_size); |
708 | 0 | return -EINVAL; |
709 | 0 | } |
710 | 0 | } |
711 | | |
712 | 0 | r = LUKS2_keyslot_reencrypt_digest_create(cd, hdr, version, vks); |
713 | 0 | if (r < 0) |
714 | 0 | log_err(cd, _("Failed to refresh reencryption verification digest.")); |
715 | |
|
716 | 0 | return r ?: LUKS2_hdr_write(cd, hdr); |
717 | 0 | } |
718 | | |
719 | | int LUKS2_keyslot_reencrypt_load(struct crypt_device *cd, |
720 | | struct luks2_hdr *hdr, |
721 | | int keyslot, |
722 | | struct reenc_protection *rp, |
723 | | bool primary) |
724 | 0 | { |
725 | 0 | json_object *jobj_type, *jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); |
726 | |
|
727 | 0 | if (!jobj_keyslot || |
728 | 0 | !json_object_object_get_ex(jobj_keyslot, "type", &jobj_type) || |
729 | 0 | strcmp(json_object_get_string(jobj_type), "reencrypt")) |
730 | 0 | return -EINVAL; |
731 | | |
732 | 0 | return reenc_keyslot_load_resilience(cd, jobj_keyslot, rp, primary); |
733 | 0 | } |
734 | | |
735 | | const keyslot_handler reenc_keyslot = { |
736 | | .name = "reencrypt", |
737 | | .open = reenc_keyslot_open, |
738 | | .store = reenc_keyslot_store, /* initialization only or also per every chunk write */ |
739 | | .wipe = reenc_keyslot_wipe, |
740 | | .dump = reenc_keyslot_dump, |
741 | | .validate = reenc_keyslot_validate |
742 | | }; |