diff --git a/lib/luks2/luks2.h b/lib/luks2/luks2.h index e9e5c6b8..4b12d9cc 100644 --- a/lib/luks2/luks2.h +++ b/lib/luks2/luks2.h @@ -95,6 +95,25 @@ struct luks2_hdr { json_object *jobj; }; +struct luks2_keyslot_params { + enum { LUKS2_KEYSLOT_AF_LUKS1 = 0 } af_type; + enum { LUKS2_KEYSLOT_AREA_RAW = 0 } area_type; + + union { + struct { + char hash[LUKS2_CHECKSUM_ALG_L]; // or include luks.h + unsigned int stripes; + } luks1; + } af; + + union { + struct { + char encryption[65]; // or include utils_crypt.h + size_t key_size; + } raw; + } area; +}; + /* * Supportable header sizes (hdr_disk + JSON area) * Also used as offset for the 2nd header. @@ -151,7 +170,8 @@ int LUKS2_keyslot_store(struct crypt_device *cd, int keyslot, const char *password, size_t password_len, - const struct volume_key *vk); + const struct volume_key *vk, + const struct luks2_keyslot_params *params); int LUKS2_keyslot_wipe(struct crypt_device *cd, struct luks2_hdr *hdr, @@ -299,6 +319,10 @@ uint64_t LUKS2_get_data_offset(struct luks2_hdr *hdr); int LUKS2_get_sector_size(struct luks2_hdr *hdr); const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment); const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment); +int LUKS2_keyslot_params_default(struct crypt_device *cd, struct luks2_hdr *hdr, + size_t key_size, struct luks2_keyslot_params *params); +int LUKS2_get_keyslot_params(struct luks2_hdr *hdr, int keyslot, + struct luks2_keyslot_params *params); int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment); int LUKS2_get_keyslot_key_size(struct luks2_hdr *hdr, int keyslot); int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr, const char *type); diff --git a/lib/luks2/luks2_internal.h b/lib/luks2/luks2_internal.h index 6a83d77a..9a9ccf89 100644 --- a/lib/luks2/luks2_internal.h +++ b/lib/luks2/luks2_internal.h @@ -83,7 +83,8 @@ struct json_object *LUKS2_array_remove(struct json_object *array, const char *nu * LUKS2 keyslots handlers (EXPERIMENTAL) */ typedef int (*keyslot_alloc_func)(struct crypt_device *cd, int keyslot, - size_t volume_key_len); + size_t volume_key_len, + const struct luks2_keyslot_params *params); typedef int (*keyslot_open_func) (struct crypt_device *cd, int keyslot, const char *password, size_t password_len, char *volume_key, size_t volume_key_len); @@ -96,7 +97,8 @@ typedef int (*keyslot_validate_func) (struct crypt_device *cd, int keyslot); int luks2_keyslot_alloc(struct crypt_device *cd, int keyslot, - size_t volume_key_len); + size_t volume_key_len, + const struct luks2_keyslot_params *params); typedef struct { const char *name; diff --git a/lib/luks2/luks2_json_metadata.c b/lib/luks2/luks2_json_metadata.c index 12dcd551..676f680d 100644 --- a/lib/luks2/luks2_json_metadata.c +++ b/lib/luks2/luks2_json_metadata.c @@ -26,6 +26,8 @@ #include #include +#define LUKS_STRIPES 4000 + struct interval { uint64_t offset; uint64_t length; @@ -1611,6 +1613,78 @@ const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment) return json_object_get_string(jobj3); } +static int luks2_keyslot_af_params(json_object *jobj_af, struct luks2_keyslot_params *params) +{ + int r; + json_object *jobj_type, *jobj1; + + /* currently we only support luks1 AF */ + json_object_object_get_ex(jobj_af, "type", &jobj_type); + if (strcmp(json_object_get_string(jobj_type), "luks1")) + return 1; + + /* process luks1 af params */ + json_object_object_get_ex(jobj_af, "stripes", &jobj1); + params->af.luks1.stripes = json_object_get_int(jobj1); + if (params->af.luks1.stripes != LUKS_STRIPES) + return 1; + + json_object_object_get_ex(jobj_af, "hash", &jobj1); + r = snprintf(params->af.luks1.hash, sizeof(params->af.luks1.hash), "%s", + json_object_get_string(jobj1)); + if (r < 0 || (size_t)r >= sizeof(params->af.luks1.hash)) + return 1; + + params->af_type = LUKS2_KEYSLOT_AF_LUKS1; + + return 0; +} + +static int luks2_keyslot_area_params(json_object *jobj_area, struct luks2_keyslot_params *params) +{ + int r; + json_object *jobj_type, *jobj1; + + /* currently we only support raw length preserving area encryption */ + json_object_object_get_ex(jobj_area, "type", &jobj_type); + if (strcmp(json_object_get_string(jobj_type), "raw")) + return 1; + + /* process raw area encryption params */ + json_object_object_get_ex(jobj_area, "encryption", &jobj1); + r = snprintf(params->area.raw.encryption, sizeof(params->area.raw.encryption), "%s", + json_object_get_string(jobj1)); + if (r < 0 || (size_t)r >= sizeof(params->area.raw.encryption)) + return 1; + json_object_object_get_ex(jobj_area, "key_size", &jobj1); + params->area.raw.key_size = json_object_get_int(jobj1); + + params->area_type = LUKS2_KEYSLOT_AREA_RAW; + + return 0; +} + +/* keyslot must be validated */ +int LUKS2_get_keyslot_params(struct luks2_hdr *hdr, int keyslot, struct luks2_keyslot_params *params) +{ + json_object *jobj_keyslot, *jobj_af, *jobj_area; + + jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); + if (!jobj_keyslot) + return -ENOENT; + + if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) || + !json_object_object_get_ex(jobj_keyslot, "af", &jobj_af)) + return -EINVAL; + + if (luks2_keyslot_af_params(jobj_af, params)) + return -EINVAL; + if (luks2_keyslot_area_params(jobj_area, params)) + return -EINVAL; + + return 0; +} + const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment) { json_object *jobj1, *jobj2, *jobj3, *jobj4; diff --git a/lib/luks2/luks2_keyslot.c b/lib/luks2/luks2_keyslot.c index 20c0eec1..8b665eea 100644 --- a/lib/luks2/luks2_keyslot.c +++ b/lib/luks2/luks2_keyslot.c @@ -117,6 +117,50 @@ int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment) return num; } +int LUKS2_keyslot_params_default(struct crypt_device *cd, struct luks2_hdr *hdr, + size_t key_size, struct luks2_keyslot_params *params) +{ + int r, integrity_key_size = crypt_get_integrity_key_size(cd); + const struct crypt_pbkdf_type *pbkdf = crypt_get_pbkdf_type(cd); + + if (!hdr || !pbkdf || !params) + return -EINVAL; + + params->af_type = LUKS2_KEYSLOT_AF_LUKS1; + params->area_type = LUKS2_KEYSLOT_AREA_RAW; + + /* set keyslot AF parameters */ + /* currently we use hash for AF from pbkdf settings */ + r = snprintf(params->af.luks1.hash, sizeof(params->af.luks1.hash), + "%s", pbkdf->hash); + if (r < 0 || (size_t)r >= sizeof(params->af.luks1.hash)) + return -EINVAL; + + params->af.luks1.stripes = 4000; + + /* set keyslot area encryption parameters */ + /* short circuit authenticated encryption hardcoded defaults */ + if (crypt_get_integrity_tag_size(cd) || key_size == 0) { + // FIXME: fixed cipher and key size can be wrong + snprintf(params->area.raw.encryption, sizeof(params->area.raw.encryption), + "aes-xts-plain64"); + params->area.raw.key_size = 32; + return 0; + } + + r = snprintf(params->area.raw.encryption, sizeof(params->area.raw.encryption), + "%s", LUKS2_get_cipher(hdr, CRYPT_DEFAULT_SEGMENT)); + if (r < 0 || (size_t)r >= sizeof(params->area.raw.encryption)) + return -EINVAL; + + /* Slot encryption tries to use the same key size as for the main algorithm */ + if (integrity_key_size > key_size) + return -EINVAL; + params->area.raw.key_size = key_size - integrity_key_size; + + return 0; +} + crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot) { crypt_keyslot_info ki; @@ -281,7 +325,8 @@ int LUKS2_keyslot_store(struct crypt_device *cd, int keyslot, const char *password, size_t password_len, - const struct volume_key *vk) + const struct volume_key *vk, + const struct luks2_keyslot_params *params) { const keyslot_handler *h; int r; @@ -295,7 +340,7 @@ int LUKS2_keyslot_store(struct crypt_device *cd, if (!h) return -EINVAL; - r = h->alloc(cd, keyslot, vk->keylength); + r = h->alloc(cd, keyslot, vk->keylength, params); if (r) return r; } else if (!(h = LUKS2_keyslot_handler(cd, keyslot))) diff --git a/lib/luks2/luks2_keyslot_luks2.c b/lib/luks2/luks2_keyslot_luks2.c index a5019681..19a764f5 100644 --- a/lib/luks2/luks2_keyslot_luks2.c +++ b/lib/luks2/luks2_keyslot_luks2.c @@ -376,18 +376,24 @@ static int luks2_keyslot_get_key(struct crypt_device *cd, int luks2_keyslot_alloc(struct crypt_device *cd, int keyslot, - size_t volume_key_len) + size_t volume_key_len, + const struct luks2_keyslot_params *params) { struct luks2_hdr *hdr; const struct crypt_pbkdf_type *pbkdf; - char cipher[2 * MAX_CIPHER_LEN + 1], num[16]; + char num[16]; uint64_t area_offset, area_length; json_object *jobj_keyslots, *jobj_keyslot, *jobj_kdf, *jobj_af, *jobj_area; - size_t keyslot_key_len; int r; log_dbg("Trying to allocate LUKS2 keyslot %d.", keyslot); + if (!params || params->area_type != LUKS2_KEYSLOT_AREA_RAW || + params->af_type != LUKS2_KEYSLOT_AF_LUKS1) { + log_dbg("Invalid LUKS2 keyslot parameters."); + return -EINVAL; + } + if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) return -EINVAL; @@ -439,28 +445,15 @@ int luks2_keyslot_alloc(struct crypt_device *cd, /* AF object */ jobj_af = json_object_new_object(); json_object_object_add(jobj_af, "type", json_object_new_string("luks1")); - json_object_object_add(jobj_af, "hash", json_object_new_string(pbkdf->hash)); - json_object_object_add(jobj_af, "stripes", json_object_new_int(4000)); + json_object_object_add(jobj_af, "hash", json_object_new_string(params->af.luks1.hash)); + json_object_object_add(jobj_af, "stripes", json_object_new_int(params->af.luks1.stripes)); json_object_object_add(jobj_keyslot, "af", jobj_af); /* Area object */ jobj_area = json_object_new_object(); json_object_object_add(jobj_area, "type", json_object_new_string("raw")); - - /* Slot encryption tries to use the same key size as fot the main algorithm */ - keyslot_key_len = volume_key_len - crypt_get_integrity_key_size(cd); - - /* Cannot use metadata tags in keyslot */ - if (crypt_get_integrity_tag_size(cd)) { - snprintf(cipher, sizeof(cipher), "aes-xts-plain64"); // FIXME: fixed cipher and key size can be wrong - keyslot_key_len = 32; - } else if (crypt_get_cipher_mode(cd)) - snprintf(cipher, sizeof(cipher), "%s-%s", crypt_get_cipher(cd), crypt_get_cipher_mode(cd)); - else - snprintf(cipher, sizeof(cipher), "%s", crypt_get_cipher(cd)); - - json_object_object_add(jobj_area, "encryption", json_object_new_string(cipher)); - json_object_object_add(jobj_area, "key_size", json_object_new_int(keyslot_key_len)); + json_object_object_add(jobj_area, "encryption", json_object_new_string(params->area.raw.encryption)); + json_object_object_add(jobj_area, "key_size", json_object_new_int(params->area.raw.key_size)); json_object_object_add(jobj_area, "offset", json_object_new_uint64(area_offset)); json_object_object_add(jobj_area, "size", json_object_new_uint64(area_length)); json_object_object_add(jobj_keyslot, "area", jobj_area); diff --git a/lib/luks2/luks2_luks1_convert.c b/lib/luks2/luks2_luks1_convert.c index 4f5c12f0..7e561164 100644 --- a/lib/luks2/luks2_luks1_convert.c +++ b/lib/luks2/luks2_luks1_convert.c @@ -611,6 +611,7 @@ int LUKS2_luks2_to_luks1(struct crypt_device *cd, struct luks2_hdr *hdr2, struct int i, r, last_active = 0; uint64_t offset, area_length; char buf[256], luksMagic[] = LUKS_MAGIC; + struct luks2_keyslot_params params; jobj_digest = LUKS2_get_digest_jobj(hdr2, 0); if (!jobj_digest) @@ -628,10 +629,17 @@ int LUKS2_luks2_to_luks1(struct crypt_device *cd, struct luks2_hdr *hdr2, struct return -EINVAL; } - key_size = r = LUKS2_get_volume_key_size(hdr2, 0); + /* We really do not care about params later except keys_size */ + r = LUKS2_keyslot_params_default(cd, hdr2, 0, ¶ms); if (r < 0) return -EINVAL; + r = LUKS2_get_volume_key_size(hdr2, 0); + if (r < 0) + return -EINVAL; + key_size = r; + params.area.raw.key_size = key_size; + for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) { if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INACTIVE) continue; @@ -670,7 +678,7 @@ int LUKS2_luks2_to_luks1(struct crypt_device *cd, struct luks2_hdr *hdr2, struct if (LUKS2_find_area_gap(cd, hdr2, key_size, &offset, &area_length)) return -EINVAL; /* FIXME: luks2 reload is required! */ - if (luks2_keyslot_alloc(cd, i, key_size)) + if (luks2_keyslot_alloc(cd, i, key_size, ¶ms)) return -EINVAL; } diff --git a/lib/setup.c b/lib/setup.c index 4866d5a5..8c08cf8b 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -2563,6 +2563,7 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, size_t new_passphrase_size) { int digest, r, active_slots; + struct luks2_keyslot_params params; struct volume_key *vk = NULL; log_dbg("Adding new keyslot, existing passphrase %sprovided," @@ -2614,13 +2615,16 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk); digest = r; + if (r >= 0) + r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, vk->keylength, ¶ms); + if (r >= 0) r = LUKS2_digest_assign(cd, &cd->u.luks2.hdr, keyslot, digest, 1, 0); if (r >= 0) r = LUKS2_keyslot_store(cd, &cd->u.luks2.hdr, keyslot, CONST_CAST(char*)new_passphrase, - new_passphrase_size, vk); + new_passphrase_size, vk, ¶ms); } if (r < 0) @@ -2645,6 +2649,7 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd, size_t new_passphrase_size) { int digest, r; + struct luks2_keyslot_params params; struct volume_key *vk = NULL; if (!passphrase || !new_passphrase) @@ -2696,6 +2701,9 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd, r = LUKS_set_key(keyslot_new, new_passphrase, new_passphrase_size, &cd->u.luks1.hdr, vk, cd); } else if (isLUKS2(cd->type)) { + r = LUKS2_get_keyslot_params(&cd->u.luks2.hdr, keyslot_old, ¶ms); + if (r) + goto out; if (keyslot_old != keyslot_new) { r = LUKS2_digest_assign(cd, &cd->u.luks2.hdr, keyslot_new, digest, 1, 0); if (r < 0) @@ -2713,7 +2721,7 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd, r = LUKS2_keyslot_store(cd, &cd->u.luks2.hdr, keyslot_new, new_passphrase, - new_passphrase_size, vk); + new_passphrase_size, vk, ¶ms); } else r = -EINVAL; @@ -2748,6 +2756,7 @@ int crypt_keyslot_add_by_keyfile_device_offset(struct crypt_device *cd, { int digest, r, active_slots; size_t passwordLen, new_passwordLen; + struct luks2_keyslot_params params; char *password = NULL, *new_password = NULL; struct volume_key *vk = NULL; @@ -2807,12 +2816,15 @@ int crypt_keyslot_add_by_keyfile_device_offset(struct crypt_device *cd, r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk); digest = r; + if (r >= 0) + r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, vk->keylength, ¶ms); + if (r >= 0) r = LUKS2_digest_assign(cd, &cd->u.luks2.hdr, keyslot, digest, 1, 0); if (r >= 0) r = LUKS2_keyslot_store(cd, &cd->u.luks2.hdr, keyslot, - new_password, new_passwordLen, vk); + new_password, new_passwordLen, vk, ¶ms); } out: crypt_safe_free(password); @@ -4385,6 +4397,7 @@ int crypt_keyslot_add_by_key(struct crypt_device *cd, uint32_t flags) { int digest, r; + struct luks2_keyslot_params params; struct volume_key *vk = NULL; if (!passphrase) @@ -4411,13 +4424,20 @@ int crypt_keyslot_add_by_key(struct crypt_device *cd, return -ENOMEM; /* no segment means we're going to store key without assigned segment (unused in dm-crypt) */ - if (flags & CRYPT_VOLUME_KEY_NO_SEGMENT) + if (flags & CRYPT_VOLUME_KEY_NO_SEGMENT) { digest = LUKS2_digest_create(cd, "pbkdf2", &cd->u.luks2.hdr, vk); - else + r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, 0, ¶ms); + } else { digest = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk); + r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, vk->keylength, ¶ms); + } + + if (r < 0) { + log_err(cd, _("Failed to initialise default LUKS2 keyslot parameters.\n")); + goto out; + } r = digest; - if (r < 0) { log_err(cd, _("Volume key does not match the volume.\n")); goto out; @@ -4429,8 +4449,8 @@ int crypt_keyslot_add_by_key(struct crypt_device *cd, goto out; } - r = LUKS2_keyslot_store(cd, &cd->u.luks2.hdr, keyslot, - passphrase, passphrase_size, vk); + r = LUKS2_keyslot_store(cd, &cd->u.luks2.hdr, keyslot, + passphrase, passphrase_size, vk, ¶ms); out: crypt_free_volume_key(vk); if (r < 0) { diff --git a/tests/api-test-2.c b/tests/api-test-2.c index e835072c..ff09caf6 100644 --- a/tests/api-test-2.c +++ b/tests/api-test-2.c @@ -2262,6 +2262,11 @@ static void Luks2KeyslotAdd(void) /* otoh passphrase check should pass */ EQ_(crypt_activate_by_passphrase(cd, NULL, 1, PASSPHRASE1, strlen(PASSPHRASE1), 0), 1); EQ_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, PASSPHRASE1, strlen(PASSPHRASE1), 0), 1); + /* in general crypt_keyslot_add_by_key must allow any reasonable key size + * even though such keyslot will not be usable for segment encryption */ + EQ_(crypt_keyslot_add_by_key(cd, 2, key2, key_size-1, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT), 2); + EQ_(crypt_keyslot_add_by_key(cd, 3, key2, 13, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT), 3); + crypt_free(cd); }