/*
* LUKS - Linux Unified Key Setup v2, LUKS1 conversion code
*
- * Copyright (C) 2015-2021 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2021 Ondrej Kozina
- * Copyright (C) 2015-2021 Milan Broz
+ * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2023 Ondrej Kozina
+ * Copyright (C) 2015-2023 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
#include "../luks1/luks.h"
#include "../luks1/af.h"
+/* This differs from LUKS_check_cipher() that it does not check dm-crypt fallback. */
int LUKS2_check_cipher(struct crypt_device *cd,
size_t keylength,
const char *cipher,
const char *cipher_mode)
{
- return LUKS_check_cipher(cd, keylength, cipher, cipher_mode);
+ int r;
+ struct crypt_storage *s;
+ char buf[SECTOR_SIZE], *empty_key;
+
+ log_dbg(cd, "Checking if cipher %s-%s is usable (storage wrapper).", cipher, cipher_mode);
+
+ empty_key = crypt_safe_alloc(keylength);
+ if (!empty_key)
+ return -ENOMEM;
+
+ /* No need to get KEY quality random but it must avoid known weak keys. */
+ r = crypt_random_get(cd, empty_key, keylength, CRYPT_RND_NORMAL);
+ if (r < 0)
+ goto out;
+
+ r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, empty_key, keylength, false);
+ if (r < 0)
+ goto out;
+
+ memset(buf, 0, sizeof(buf));
+ r = crypt_storage_decrypt(s, 0, sizeof(buf), buf);
+ crypt_storage_destroy(s);
+out:
+ crypt_safe_free(empty_key);
+ crypt_safe_memzero(buf, sizeof(buf));
+ return r;
}
static int json_luks1_keyslot(const struct luks_phdr *hdr_v1, int keyslot, struct json_object **keyslot_object)
char *base64_str, cipher[LUKS_CIPHERNAME_L+LUKS_CIPHERMODE_L];
size_t base64_len;
struct json_object *keyslot_obj, *field, *jobj_kdf, *jobj_af, *jobj_area;
- uint64_t offset, area_size, offs_a, offs_b, length;
+ uint64_t offset, area_size, length;
+ int r;
keyslot_obj = json_object_new_object();
json_object_object_add(keyslot_obj, "type", json_object_new_string("luks2"));
json_object_object_add(jobj_kdf, "hash", json_object_new_string(hdr_v1->hashSpec));
json_object_object_add(jobj_kdf, "iterations", json_object_new_int64(hdr_v1->keyblock[keyslot].passwordIterations));
/* salt field */
- base64_len = base64_encode_alloc(hdr_v1->keyblock[keyslot].passwordSalt, LUKS_SALTSIZE, &base64_str);
- if (!base64_str) {
+ r = crypt_base64_encode(&base64_str, &base64_len, hdr_v1->keyblock[keyslot].passwordSalt, LUKS_SALTSIZE);
+ if (r < 0) {
json_object_put(keyslot_obj);
json_object_put(jobj_kdf);
- if (!base64_len)
- return -EINVAL;
- return -ENOMEM;
+ return r;
}
field = json_object_new_string_len(base64_str, base64_len);
free(base64_str);
json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
json_object_object_add(jobj_af, "hash", json_object_new_string(hdr_v1->hashSpec));
/* stripes field ignored, fixed to LUKS_STRIPES (4000) */
- json_object_object_add(jobj_af, "stripes", json_object_new_int(4000));
+ json_object_object_add(jobj_af, "stripes", json_object_new_int(LUKS_STRIPES));
json_object_object_add(keyslot_obj, "af", jobj_af);
/* Area */
/* encryption algorithm field */
if (*hdr_v1->cipherMode != '\0') {
- (void) snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode);
+ if (snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode) < 0) {
+ json_object_put(keyslot_obj);
+ json_object_put(jobj_area);
+ return -EINVAL;
+ }
json_object_object_add(jobj_area, "encryption", json_object_new_string(cipher));
} else
json_object_object_add(jobj_area, "encryption", json_object_new_string(hdr_v1->cipherName));
/* area */
- if (LUKS_keyslot_area(hdr_v1, 0, &offs_a, &length) ||
- LUKS_keyslot_area(hdr_v1, 1, &offs_b, &length) ||
- LUKS_keyslot_area(hdr_v1, keyslot, &offset, &length)) {
+ if (LUKS_keyslot_area(hdr_v1, keyslot, &offset, &length)) {
json_object_put(keyslot_obj);
json_object_put(jobj_area);
return -EINVAL;
}
- area_size = offs_b - offs_a;
+ area_size = size_round_up(length, 4096);
json_object_object_add(jobj_area, "key_size", json_object_new_int(hdr_v1->keyBytes));
json_object_object_add(jobj_area, "offset", crypt_jobj_new_uint64(offset));
json_object_object_add(jobj_area, "size", crypt_jobj_new_uint64(area_size));
/* cipher field */
if (*hdr_v1->cipherMode != '\0') {
- (void) snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode);
+ if (snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode) < 0) {
+ json_object_put(segment_obj);
+ return -EINVAL;
+ }
c = cipher;
} else
c = hdr_v1->cipherName;
static int json_luks1_digest(const struct luks_phdr *hdr_v1, struct json_object **digest_object)
{
- char keyslot_str[2], *base64_str;
- int ks;
+ char keyslot_str[16], *base64_str;
+ int r, ks;
size_t base64_len;
struct json_object *digest_obj, *array, *field;
for (ks = 0; ks < LUKS_NUMKEYS; ks++) {
if (hdr_v1->keyblock[ks].active != LUKS_KEY_ENABLED)
continue;
- (void) snprintf(keyslot_str, sizeof(keyslot_str), "%d", ks);
+ if (snprintf(keyslot_str, sizeof(keyslot_str), "%d", ks) < 0) {
+ json_object_put(field);
+ json_object_put(array);
+ json_object_put(digest_obj);
+ return -EINVAL;
+ }
field = json_object_new_string(keyslot_str);
if (!field || json_object_array_add(array, field) < 0) {
json_object_object_add(digest_obj, "hash", field);
/* salt field */
- base64_len = base64_encode_alloc(hdr_v1->mkDigestSalt, LUKS_SALTSIZE, &base64_str);
- if (!base64_str) {
+ r = crypt_base64_encode(&base64_str, &base64_len, hdr_v1->mkDigestSalt, LUKS_SALTSIZE);
+ if (r < 0) {
json_object_put(digest_obj);
- if (!base64_len)
- return -EINVAL;
- return -ENOMEM;
+ return r;
}
field = json_object_new_string_len(base64_str, base64_len);
json_object_object_add(digest_obj, "salt", field);
/* digest field */
- base64_len = base64_encode_alloc(hdr_v1->mkDigest, LUKS_DIGESTSIZE, &base64_str);
- if (!base64_str) {
+ r = crypt_base64_encode(&base64_str, &base64_len, hdr_v1->mkDigest, LUKS_DIGESTSIZE);
+ if (r < 0) {
json_object_put(digest_obj);
- if (!base64_len)
- return -EINVAL;
- return -ENOMEM;
+ return r;
}
field = json_object_new_string_len(base64_str, base64_len);
}
}
-/* FIXME: return specific error code for partial write error (aka keyslots are gone) */
static int move_keyslot_areas(struct crypt_device *cd, off_t offset_from,
off_t offset_to, size_t buf_size)
{
return -EINVAL;
}
+ if (LUKS2_check_cipher(cd, hdr1->keyBytes, hdr1->cipherName, hdr1->cipherMode)) {
+ log_err(cd, _("Unable to use cipher specification %s-%s for LUKS2."),
+ hdr1->cipherName, hdr1->cipherMode);
+ return -EINVAL;
+ }
+
if (luksmeta_header_present(cd, luks1_size))
return -EINVAL;
move_keyslot_offset(jobj, luks1_shift);
- // fill hdr2
+ /* Create and fill LUKS2 hdr */
memset(hdr2, 0, sizeof(*hdr2));
hdr2->hdr_size = LUKS2_HDR_16K_LEN;
hdr2->seqid = 1;
/* check future LUKS2 metadata before moving keyslots area */
if (LUKS2_hdr_validate(cd, hdr2->jobj, hdr2->hdr_size - LUKS2_HDR_BIN_LEN)) {
+ log_err(cd, _("Cannot convert to LUKS2 format - invalid metadata."));
r = -EINVAL;
goto out;
}
goto out;
}
- // move keyslots 4k -> 32k offset
+ /* move keyslots 4k -> 32k offset */
buf_offset = 2 * LUKS2_HDR_16K_LEN;
buf_size = luks1_size - LUKS_ALIGN_KEYSLOTS;
goto out;
}
- // Write JSON hdr2
+ /* Write new LUKS2 JSON */
r = LUKS2_hdr_write(cd, hdr2);
out:
LUKS2_hdr_free(cd, hdr2);
strcmp(json_object_get_string(jobj), hash))
return 0;
- /* FIXME: should this go to validation code instead (aka invalid luks2 header if assigned to segment 0)? */
- /* FIXME: check all keyslots are assigned to segment id 0, and segments count == 1 */
ks_cipher = LUKS2_get_keyslot_cipher(hdr, keyslot, &ks_key_size);
data_cipher = LUKS2_get_cipher(hdr, CRYPT_DEFAULT_SEGMENT);
if (!ks_cipher || !data_cipher || key_size != ks_key_size || strcmp(ks_cipher, data_cipher)) {
{
size_t buf_size, buf_offset;
char cipher[LUKS_CIPHERNAME_L], cipher_mode[LUKS_CIPHERMODE_L];
- char digest[LUKS_DIGESTSIZE], digest_salt[LUKS_SALTSIZE];
+ char *digest, *digest_salt;
const char *hash;
size_t len;
json_object *jobj_keyslot, *jobj_digest, *jobj_segment, *jobj_kdf, *jobj_area, *jobj1, *jobj2;
uint32_t key_size;
int i, r, last_active = 0;
uint64_t offset, area_length;
- char buf[256], luksMagic[] = LUKS_MAGIC;
+ char *buf, luksMagic[] = LUKS_MAGIC;
jobj_digest = LUKS2_get_digest_jobj(hdr2, 0);
if (!jobj_digest)
return -EINVAL;
}
+ if (json_segments_count(LUKS2_get_segments_jobj(hdr2)) != 1) {
+ log_err(cd, _("Cannot convert to LUKS1 format - device uses more segments."));
+ return -EINVAL;
+ }
+
r = LUKS2_tokens_count(hdr2);
if (r < 0)
return r;
* inactive keyslots. Otherwise we would allocate all
* inactive luks1 keyslots over same binary keyslot area.
*/
- if (placeholder_keyslot_alloc(cd, i, offset, area_length, key_size))
+ if (placeholder_keyslot_alloc(cd, i, offset, area_length))
return -EINVAL;
}
if (!json_object_object_get_ex(jobj_kdf, "salt", &jobj1))
continue;
- len = sizeof(buf);
- memset(buf, 0, len);
- if (!base64_decode(json_object_get_string(jobj1),
- json_object_get_string_len(jobj1), buf, &len))
+
+ if (crypt_base64_decode(&buf, &len, json_object_get_string(jobj1),
+ json_object_get_string_len(jobj1)))
continue;
- if (len > 0 && len != LUKS_SALTSIZE)
+ if (len > 0 && len != LUKS_SALTSIZE) {
+ free(buf);
continue;
+ }
memcpy(hdr1->keyblock[i].passwordSalt, buf, LUKS_SALTSIZE);
+ free(buf);
}
if (!jobj_keyslot) {
if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
return -EINVAL;
- len = sizeof(digest);
- if (!base64_decode(json_object_get_string(jobj1),
- json_object_get_string_len(jobj1), digest, &len))
- return -EINVAL;
+ r = crypt_base64_decode(&digest, &len, json_object_get_string(jobj1),
+ json_object_get_string_len(jobj1));
+ if (r < 0)
+ return r;
/* We can store full digest here, not only sha1 length */
- if (len < LUKS_DIGESTSIZE)
+ if (len < LUKS_DIGESTSIZE) {
+ free(digest);
return -EINVAL;
+ }
memcpy(hdr1->mkDigest, digest, LUKS_DIGESTSIZE);
+ free(digest);
if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
return -EINVAL;
- len = sizeof(digest_salt);
- if (!base64_decode(json_object_get_string(jobj1),
- json_object_get_string_len(jobj1), digest_salt, &len))
- return -EINVAL;
- if (len != LUKS_SALTSIZE)
+ r = crypt_base64_decode(&digest_salt, &len, json_object_get_string(jobj1),
+ json_object_get_string_len(jobj1));
+ if (r < 0)
+ return r;
+ if (len != LUKS_SALTSIZE) {
+ free(digest_salt);
return -EINVAL;
+ }
memcpy(hdr1->mkDigestSalt, digest_salt, LUKS_SALTSIZE);
+ free(digest_salt);
if (!json_object_object_get_ex(jobj_segment, "offset", &jobj1))
return -EINVAL;
offset = crypt_jobj_get_uint64(jobj1) / SECTOR_SIZE;
if (offset > UINT32_MAX)
return -EINVAL;
- /* FIXME: LUKS1 requires offset == 0 || offset >= luks1_hdr_size */
hdr1->payloadOffset = offset;
strncpy(hdr1->uuid, hdr2->uuid, UUID_STRING_L); /* max 36 chars */
if (r)
return r > 0 ? -EBUSY : r;
- // move keyslots 32k -> 4k offset
+ /* move keyslots 32k -> 4k offset */
buf_offset = 2 * LUKS2_HDR_16K_LEN;
buf_size = LUKS2_keyslots_size(hdr2);
r = move_keyslot_areas(cd, buf_offset, 8 * SECTOR_SIZE, buf_size);
crypt_wipe_device(cd, crypt_metadata_device(cd), CRYPT_WIPE_ZERO, 0,
8 * SECTOR_SIZE, 8 * SECTOR_SIZE, NULL, NULL);
- // Write LUKS1 hdr
+ /* Write new LUKS1 hdr */
return LUKS_write_phdr(hdr1, cd);
}