Coverage Report

Created: 2026-02-22 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
};