diff --git a/configure.ac b/configure.ac index 86c6fc4c..001bb96b 100644 --- a/configure.ac +++ b/configure.ac @@ -564,6 +564,9 @@ CS_NUM_WITH([luks2-iter-time], [Argon2 PBKDF iteration time for LUKS2 (in CS_NUM_WITH([luks2-memory-kb], [Argon2 PBKDF memory cost for LUKS2 (in kB)], [1048576]) CS_NUM_WITH([luks2-parallel-threads],[Argon2 PBKDF max parallel cost for LUKS2 (if CPUs available)], [4]) +CS_STR_WITH([luks2-keyslot-cipher], [fallback cipher for LUKS2 keyslot (if data encryption is incompatible)], [aes-xts-plain64]) +CS_NUM_WITH([luks2-keyslot-keybits],[fallback key size for LUKS2 keyslot (if data encryption is incompatible)], [512]) + CS_STR_WITH([loopaes-cipher], [cipher for loop-AES mode], [aes]) CS_NUM_WITH([loopaes-keybits],[key length in bits for loop-AES mode], [256]) diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index 5a5c92a6..a3b69996 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -1634,6 +1634,39 @@ int crypt_keyslot_area(struct crypt_device *cd, */ int crypt_keyslot_get_key_size(struct crypt_device *cd, int keyslot); +/** + * Get cipher and key size for keyslot encryption. + * Use for LUKS2 keyslot to set different encryption type than for data encryption. + * Parameters will be used for next keyslot operations. + * + * @param cd crypt device handle + * @param keyslot keyslot number of CRYPT_ANY_SLOT for default + * @param key_size encryption key size (in bytes) + * + * @return cipher specification on success or @e NULL. + * + * @note This is the encryption of keyslot itself, not the data encryption algorithm! + */ +const char *crypt_keyslot_get_encryption(struct crypt_device *cd, int keyslot, size_t *key_size); + +/** + * Set encryption for keyslot. + * Use for LUKS2 keyslot to set different encryption type than for data encryption. + * Parameters will be used for next keyslot operations that create or change a keyslot. + * + * @param cd crypt device handle + * @param cipher (e.g. "aes-xts-plain64") + * @param key_size encryption key size (in bytes) + * + * @return @e 0 on success or negative errno value otherwise. + * + * @note To reset to default keyslot encryption (the same as for data) + * set cipher to NULL and key size to 0. + */ +int crypt_keyslot_set_encryption(struct crypt_device *cd, + const char *cipher, + size_t key_size); + /** * Get directory where mapped crypt devices are created * diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym index 7ad01337..8d401037 100644 --- a/lib/libcryptsetup.sym +++ b/lib/libcryptsetup.sym @@ -97,6 +97,8 @@ CRYPTSETUP_2.0 { crypt_keyslot_area; crypt_keyslot_status; crypt_keyslot_get_key_size; + crypt_keyslot_set_encryption; + crypt_keyslot_get_encryption; crypt_get_dir; crypt_set_debug_level; crypt_log; diff --git a/lib/luks2/luks2.h b/lib/luks2/luks2.h index 1cc3eb3a..ff9812e1 100644 --- a/lib/luks2/luks2.h +++ b/lib/luks2/luks2.h @@ -163,7 +163,7 @@ uint64_t LUKS2_hdr_and_areas_size(json_object *jobj); uint64_t LUKS2_keyslots_size(json_object *jobj); uint64_t LUKS2_metadata_size(json_object *jobj); -int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd); +int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd, const char *cipher_spec); /* * Generic LUKS2 keyslot @@ -341,11 +341,10 @@ 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); + struct luks2_keyslot_params *params); int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment); int LUKS2_get_keyslot_stored_key_size(struct luks2_hdr *hdr, int keyslot); +const char *LUKS2_get_keyslot_cipher(struct luks2_hdr *hdr, int keyslot, size_t *key_size); int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr, const char *type); int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment); int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment); diff --git a/lib/luks2/luks2_json_metadata.c b/lib/luks2/luks2_json_metadata.c index 91ba5e44..0d40e9f3 100644 --- a/lib/luks2/luks2_json_metadata.c +++ b/lib/luks2/luks2_json_metadata.c @@ -1662,76 +1662,30 @@ const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment) } -static int luks2_keyslot_af_params(json_object *jobj_af, struct luks2_keyslot_params *params) +const char *LUKS2_get_keyslot_cipher(struct luks2_hdr *hdr, int keyslot, size_t *key_size) { - 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; + json_object *jobj_keyslot, *jobj_area, *jobj1; jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); if (!jobj_keyslot) - return -ENOENT; + return NULL; - if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) || - !json_object_object_get_ex(jobj_keyslot, "af", &jobj_af)) - return -EINVAL; + if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) + return NULL; - if (luks2_keyslot_af_params(jobj_af, params)) - return -EINVAL; - if (luks2_keyslot_area_params(jobj_area, params)) - return -EINVAL; + /* currently we only support raw length preserving area encryption */ + json_object_object_get_ex(jobj_area, "type", &jobj1); + if (strcmp(json_object_get_string(jobj1), "raw")) + return NULL; - return 0; + if (!json_object_object_get_ex(jobj_area, "key_size", &jobj1)) + return NULL; + *key_size = json_object_get_int(jobj1); + + if (!json_object_object_get_ex(jobj_area, "encryption", &jobj1)) + return NULL; + + return json_object_get_string(jobj1); } const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment) diff --git a/lib/luks2/luks2_keyslot.c b/lib/luks2/luks2_keyslot.c index d1f9c0eb..224be26d 100644 --- a/lib/luks2/luks2_keyslot.c +++ b/lib/luks2/luks2_keyslot.c @@ -111,13 +111,18 @@ int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment) return num; } -int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd) +int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd, const char *cipher_spec) { - const char *cipher = crypt_get_cipher(cd); - const char *cipher_mode = crypt_get_cipher_mode(cd); + char cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; + + if (!cipher_spec || !strcmp(cipher_spec, "null") || !strcmp(cipher_spec, "cipher_null")) + return 1; + + if (crypt_parse_name_and_mode(cipher_spec, cipher, NULL, cipher_mode) < 0) + return 1; /* Keyslot is already authenticated; we cannot use integrity tags here */ - if (crypt_get_integrity_tag_size(cd) || !cipher) + if (crypt_get_integrity_tag_size(cd)) return 1; /* Wrapped key schemes cannot be used for keyslot encryption */ @@ -132,45 +137,38 @@ int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd) } int LUKS2_keyslot_params_default(struct crypt_device *cd, struct luks2_hdr *hdr, - size_t key_size, struct luks2_keyslot_params *params) + 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); + const char *cipher_spec; + size_t key_size; + int r; if (!hdr || !pbkdf || !params) return -EINVAL; - params->af_type = LUKS2_KEYSLOT_AF_LUKS1; + /* + * set keyslot area encryption parameters + */ 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)) + cipher_spec = crypt_keyslot_get_encryption(cd, CRYPT_ANY_SLOT, &key_size); + if (!cipher_spec || !key_size) return -EINVAL; - params->af.luks1.stripes = 4000; - - /* set keyslot area encryption parameters */ - /* short circuit authenticated encryption hardcoded defaults */ - if (LUKS2_keyslot_cipher_incompatible(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)); + params->area.raw.key_size = key_size; + r = snprintf(params->area.raw.encryption, sizeof(params->area.raw.encryption), "%s", cipher_spec); 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 ((size_t)integrity_key_size > key_size) + /* + * set keyslot AF parameters + */ + params->af_type = LUKS2_KEYSLOT_AF_LUKS1; + /* 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->area.raw.key_size = key_size - integrity_key_size; + params->af.luks1.stripes = 4000; return 0; } diff --git a/lib/luks2/luks2_keyslot_luks2.c b/lib/luks2/luks2_keyslot_luks2.c index ae7c6f90..fc4e6bd3 100644 --- a/lib/luks2/luks2_keyslot_luks2.c +++ b/lib/luks2/luks2_keyslot_luks2.c @@ -388,27 +388,25 @@ static int luks2_keyslot_update_json(struct crypt_device *cd, const struct luks2_keyslot_params *params) { const struct crypt_pbkdf_type *pbkdf; - json_object *jobj_af, *jobj_area, *jobj_kdf, *jobj1; + json_object *jobj_af, *jobj_area, *jobj_kdf; char salt[LUKS_SALTSIZE], *salt_base64 = NULL; - int r, keyslot_key_len; + int r; /* jobj_keyslot is not yet validated */ if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) || - !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) || - !json_object_object_get_ex(jobj_area, "key_size", &jobj1)) + !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) return -EINVAL; - /* we do not allow any 'area' object modifications yet */ - keyslot_key_len = json_object_get_int(jobj1); - if (keyslot_key_len < 0) - return -EINVAL; + /* update area encryption parameters */ + 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)); pbkdf = crypt_get_pbkdf_type(cd); if (!pbkdf) return -EINVAL; - r = crypt_benchmark_pbkdf_internal(cd, CONST_CAST(struct crypt_pbkdf_type *)pbkdf, keyslot_key_len); + r = crypt_benchmark_pbkdf_internal(cd, CONST_CAST(struct crypt_pbkdf_type *)pbkdf, params->area.raw.key_size); if (r < 0) return r; @@ -498,8 +496,6 @@ static int luks2_keyslot_alloc(struct crypt_device *cd, /* Area object */ jobj_area = json_object_new_object(); json_object_object_add(jobj_area, "type", json_object_new_string("raw")); - 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); @@ -610,6 +606,9 @@ static int luks2_keyslot_dump(struct crypt_device *cd, int keyslot) json_object_object_get_ex(jobj_area, "encryption", &jobj1); log_std(cd, "\tCipher: %s\n", json_object_get_string(jobj1)); + json_object_object_get_ex(jobj_area, "key_size", &jobj1); + log_std(cd, "\tCipher key: %u bits\n", json_object_get_uint32(jobj1) * 8); + json_object_object_get_ex(jobj_kdf, "type", &jobj1); log_std(cd, "\tPBKDF: %s\n", json_object_get_string(jobj1)); diff --git a/lib/luks2/luks2_luks1_convert.c b/lib/luks2/luks2_luks1_convert.c index 8f121bbc..4630ea30 100644 --- a/lib/luks2/luks2_luks1_convert.c +++ b/lib/luks2/luks2_luks1_convert.c @@ -591,7 +591,8 @@ static int keyslot_LUKS1_compatible(struct crypt_device *cd, { json_object *jobj_keyslot, *jobj, *jobj_kdf, *jobj_af; uint64_t l2_offset, l2_length; - int ks_key_size; + size_t ks_key_size; + const char *ks_cipher, *data_cipher; jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); if (!jobj_keyslot) @@ -621,9 +622,10 @@ static int keyslot_LUKS1_compatible(struct crypt_device *cd, /* 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_key_size = LUKS2_get_keyslot_stored_key_size(hdr, keyslot); - if (ks_key_size < 0 || (int)key_size != LUKS2_get_keyslot_stored_key_size(hdr, keyslot)) { - log_dbg(cd, "Key length in keyslot %d is different from volume key length", keyslot); + 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)) { + log_dbg(cd, "Cipher in keyslot %d is different from volume key encryption.", keyslot); return 0; } diff --git a/lib/setup.c b/lib/setup.c index 45b001f4..6c1b90eb 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -70,6 +70,8 @@ struct crypt_device { struct luks2_hdr hdr; char cipher[MAX_CIPHER_LEN]; /* only for compatibility */ char cipher_mode[MAX_CIPHER_LEN]; /* only for compatibility */ + char *keyslot_cipher; + unsigned int keyslot_key_size; } luks2; struct { /* used in CRYPT_PLAIN */ struct crypt_params_plain hdr; @@ -687,6 +689,7 @@ static int _crypt_load_luks2(struct crypt_device *cd, int reload, int repair) r = 0; memcpy(&cd->u.luks2.hdr, &hdr2, sizeof(hdr2)); + cd->u.luks2.keyslot_cipher = NULL; out: if (r) { @@ -1033,6 +1036,7 @@ static void crypt_free_type(struct crypt_device *cd) free(cd->u.plain.cipher_spec); } else if (isLUKS2(cd->type)) { LUKS2_hdr_free(cd, &cd->u.luks2.hdr); + free(cd->u.luks2.keyslot_cipher); } else if (isLUKS1(cd->type)) { free(cd->u.luks1.cipher_spec); } else if (isLOOPAES(cd->type)) { @@ -1570,6 +1574,7 @@ static int _crypt_format_luks2(struct crypt_device *cd, uint64_t dev_size; cd->u.luks2.hdr.jobj = NULL; + cd->u.luks2.keyslot_cipher = NULL; if (!cipher || !cipher_mode) return -EINVAL; @@ -3130,7 +3135,7 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, digest = r; if (r >= 0) - r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, vk->keylength, ¶ms); + r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, ¶ms); if (r >= 0) r = LUKS2_digest_assign(cd, &cd->u.luks2.hdr, keyslot, digest, 1, 0); @@ -3215,9 +3220,10 @@ 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); + r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, ¶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) @@ -3325,7 +3331,7 @@ int crypt_keyslot_add_by_keyfile_device_offset(struct crypt_device *cd, digest = r; if (r >= 0) - r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, vk->keylength, ¶ms); + r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, ¶ms); if (r >= 0) r = LUKS2_digest_assign(cd, &cd->u.luks2.hdr, keyslot, digest, 1, 0); @@ -4564,6 +4570,61 @@ int crypt_keyslot_get_key_size(struct crypt_device *cd, int keyslot) return -EINVAL; } +int crypt_keyslot_set_encryption(struct crypt_device *cd, + const char *cipher, + size_t key_size) +{ + if (!cd || !cipher || ! key_size || !isLUKS2(cd->type)) + return -EINVAL; + + if (LUKS2_keyslot_cipher_incompatible(cd, cipher)) + return -EINVAL; + + free(cd->u.luks2.keyslot_cipher); + cd->u.luks2.keyslot_cipher = strdup(cipher); + if (!cd->u.luks2.keyslot_cipher) + return -ENOMEM; + cd->u.luks2.keyslot_key_size = key_size; + + return 0; +} + +const char *crypt_keyslot_get_encryption(struct crypt_device *cd, int keyslot, size_t *key_size) +{ + const char *cipher; + + if (!cd || !isLUKS(cd->type) || !key_size) + return NULL; + + if (isLUKS1(cd->type)) { + if (keyslot != CRYPT_ANY_SLOT && + LUKS_keyslot_info(&cd->u.luks1.hdr, keyslot) < CRYPT_SLOT_ACTIVE) + return NULL; + *key_size = crypt_get_volume_key_size(cd); + return cd->u.luks1.cipher_spec; + } + + if (keyslot != CRYPT_ANY_SLOT) + return LUKS2_get_keyslot_cipher(&cd->u.luks2.hdr, keyslot, key_size); + + /* Keyslot encryption was set through crypt_keyslot_set_encryption() */ + if (cd->u.luks2.keyslot_cipher) { + *key_size = cd->u.luks2.keyslot_key_size; + return cd->u.luks2.keyslot_cipher; + } + + /* Try to reuse volume encryption parameters */ + cipher = LUKS2_get_cipher(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT); + if (!LUKS2_keyslot_cipher_incompatible(cd, cipher)) { + *key_size = crypt_get_volume_key_size(cd); + return cipher; + } + + /* Fallback to default LUKS2 keyslot encryption */ + *key_size = DEFAULT_LUKS2_KEYSLOT_KEYBITS / 8; + return DEFAULT_LUKS2_KEYSLOT_CIPHER; +} + int crypt_set_data_offset(struct crypt_device *cd, uint64_t data_offset) { if (!cd) @@ -5185,16 +5246,8 @@ int crypt_keyslot_add_by_key(struct crypt_device *cd, flags &= ~CRYPT_VOLUME_KEY_SET; /* no segment flag or new vk flag requires new key digest */ - if (flags & (CRYPT_VOLUME_KEY_NO_SEGMENT | CRYPT_VOLUME_KEY_SET)) { + if (flags & (CRYPT_VOLUME_KEY_NO_SEGMENT | CRYPT_VOLUME_KEY_SET)) digest = LUKS2_digest_create(cd, "pbkdf2", &cd->u.luks2.hdr, vk); - r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, 0, ¶ms); - } else - 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.")); - goto out; - } r = digest; if (r < 0) { @@ -5202,6 +5255,12 @@ int crypt_keyslot_add_by_key(struct crypt_device *cd, goto out; } + r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, ¶ms); + if (r < 0) { + log_err(cd, _("Failed to initialise default LUKS2 keyslot parameters.")); + goto out; + } + r = LUKS2_digest_assign(cd, &cd->u.luks2.hdr, keyslot, digest, 1, 0); if (r < 0) { log_err(cd, _("Failed to assign keyslot %d to digest."), keyslot); diff --git a/man/cryptsetup.8 b/man/cryptsetup.8 index 6339e179..95f24ad3 100644 --- a/man/cryptsetup.8 +++ b/man/cryptsetup.8 @@ -254,8 +254,9 @@ For LUKS2, additional \fB\fR can be [\-\-integrity, \-\-integrity\-no\-wipe, \-\-sector\-size, \-\-label, \-\-subsystem, \-\-pbkdf, \-\-pbkdf\-memory, \-\-pbkdf\-parallel, -\-\-disable\-locks, \-\-disable\-keyring. -\-\-luks2\-metadata\-size, \-\-luks2\-keyslots\-size]. +\-\-disable\-locks, \-\-disable\-keyring, +\-\-luks2\-metadata\-size, \-\-luks2\-keyslots\-size, +\-\-keyslot\-cipher, \-\-keyslot\-key\-size]. \fBWARNING:\fR Doing a luksFormat on an existing LUKS container will make all data the old container permanently irretrievable unless @@ -317,7 +318,7 @@ is not required. \-\-keyfile\-size, \-\-new\-keyfile\-offset, \-\-new\-keyfile\-size, \-\-key\-slot, \-\-master\-key\-file, \-\-iter\-time, \-\-force\-password, \-\-header, \-\-disable\-locks, -\-\-unbound, \-\-type]. +\-\-unbound, \-\-type, \-\-keyslot\-cipher, \-\-keyslot\-key\-size]. .PP \fIluksRemoveKey\fR [] .IP @@ -360,7 +361,7 @@ inaccessible. \fB\fR can be [\-\-key\-file, \-\-keyfile\-offset, \-\-keyfile\-size, \-\-new\-keyfile\-offset, \-\-new\-keyfile\-size, \-\-key\-slot, \-\-force\-password, \-\-header, -\-\-disable\-locks, \-\-type]. +\-\-disable\-locks, \-\-type, \-\-keyslot\-cipher, \-\-keyslot\-key\-size]. .PP .PP \fIluksConvertKey\fR @@ -384,7 +385,8 @@ parameters have been wiped and make the LUKS container inaccessible. \fB\fR can be [\-\-key\-file, \-\-keyfile\-offset, \-\-keyfile\-size, \-\-key\-slot, \-\-header, \-\-disable\-locks, \-\-iter-time, \-\-pbkdf, \-\-pbkdf\-force\-iterations, -\-\-pbkdf\-memory, \-\-pbkdf\-parallel]. +\-\-pbkdf\-memory, \-\-pbkdf\-parallel, +\-\-keyslot\-cipher, \-\-keyslot\-key\-size]. .PP \fIluksKillSlot\fR .IP @@ -1235,6 +1237,12 @@ This option can be used to set specific size of the LUKS2 binary keyslot area of 4096 bytes with maximum size 128MB. The can be specified with unit suffix (for example 128k). .TP +.B "\-\-keyslot\-cipher " +This option can be used to set specific cipher encryption for the LUKS2 keyslot area. +.TP +.B "\-\-keyslot\-key\-size " +This option can be used to set specific key size for the LUKS2 keyslot area. +.TP .B "\-\-integrity\-no\-journal" Activate device with integrity protection without using data journal (direct write of data and integrity tags). diff --git a/src/cryptsetup.c b/src/cryptsetup.c index 5765914e..5094289f 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -25,6 +25,7 @@ #include static const char *opt_cipher = NULL; +static const char *opt_keyslot_cipher = NULL; static const char *opt_hash = NULL; static int opt_verify_passphrase = 0; @@ -40,6 +41,7 @@ static const char *opt_uuid = NULL; static const char *opt_header_device = NULL; static const char *opt_type = "luks"; static int opt_key_size = 0; +static int opt_keyslot_key_size = 0; static long opt_keyfile_size = 0; static long opt_new_keyfile_size = 0; static uint64_t opt_keyfile_offset = 0; @@ -169,6 +171,21 @@ static void _set_activation_flags(uint32_t *flags) *flags |= CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY; } +static int _set_keyslot_encryption_params(struct crypt_device *cd) +{ + const char *type = crypt_get_type(cd); + + if (!opt_keyslot_key_size && !opt_keyslot_cipher) + return 0; + + if (!type || strcmp(type, CRYPT_LUKS2)) { + log_err(_("Keyslot encryption parameters can be set only for LUKS2 device.")); + return -EINVAL; + } + + return crypt_keyslot_set_encryption(cd, opt_keyslot_cipher, opt_keyslot_key_size / 8); +} + static int action_open_plain(void) { struct crypt_device *cd = NULL, *cd1 = NULL; @@ -1163,6 +1180,10 @@ static int action_luksFormat(void) if (r < 0) goto out; + r = _set_keyslot_encryption_params(cd); + if (r < 0) + goto out; + r = crypt_keyslot_add_by_volume_key(cd, opt_key_slot, key, keysize, password, passwordLen); @@ -1445,6 +1466,10 @@ static int luksAddUnboundKey(void) goto out; } + r = _set_keyslot_encryption_params(cd); + if (r < 0) + goto out; + /* Never call pwquality if using null cipher */ if (tools_is_cipher_null(crypt_get_cipher(cd))) opt_force_password = 1; @@ -1508,6 +1533,10 @@ static int action_luksAddKey(void) goto out; } + r = _set_keyslot_encryption_params(cd); + if (r < 0) + goto out; + /* Never call pwquality if using null cipher */ if (tools_is_cipher_null(crypt_get_cipher(cd))) opt_force_password = 1; @@ -1600,6 +1629,10 @@ static int action_luksChangeKey(void) goto out; } + r = _set_keyslot_encryption_params(cd); + if (r < 0) + goto out; + /* Never call pwquality if using null cipher */ if (tools_is_cipher_null(crypt_get_cipher(cd))) opt_force_password = 1; @@ -1660,6 +1693,10 @@ static int action_luksConvertKey(void) goto out; } + r = _set_keyslot_encryption_params(cd); + if (r < 0) + goto out; + if (crypt_keyslot_status(cd, opt_key_slot) == CRYPT_SLOT_INACTIVE) { r = -EINVAL; log_err(_("Keyslot %d is not active."), opt_key_slot); @@ -2523,6 +2560,8 @@ int main(int argc, const char **argv) { "luks2-metadata-size",'\0',POPT_ARG_STRING,&opt_luks2_metadata_size_str,0,N_("LUKS2 header metadata area size"), N_("bytes") }, { "luks2-keyslots-size",'\0',POPT_ARG_STRING,&opt_luks2_keyslots_size_str,0,N_("LUKS2 header keyslots area size"), N_("bytes") }, { "refresh", '\0', POPT_ARG_NONE, &opt_refresh, 0, N_("Refresh (reactivate) device with new parameters"), NULL }, + { "keyslot-key-size", '\0', POPT_ARG_INT, &opt_keyslot_key_size, 0, N_("LUKS2 keyslot: The size of the encryption key"), N_("BITS") }, + { "keyslot-cipher", '\0', POPT_ARG_STRING, &opt_keyslot_cipher, 0, N_("LUKS2 keyslot: The cipher used for keyslot encryption"), NULL }, POPT_TABLEEND }; poptContext popt_context; @@ -2733,7 +2772,7 @@ int main(int argc, const char **argv) _("Option --test-passphrase is allowed only for open of LUKS and TCRYPT devices.\n"), poptGetInvocationName(popt_context)); - if (opt_key_size % 8) + if (opt_key_size % 8 || opt_keyslot_key_size % 8) usage(popt_context, EXIT_FAILURE, _("Key size must be a multiple of 8 bits"), poptGetInvocationName(popt_context)); diff --git a/tests/api-test-2.c b/tests/api-test-2.c index 5708f4a8..81def0e9 100644 --- a/tests/api-test-2.c +++ b/tests/api-test-2.c @@ -2550,6 +2550,126 @@ static void Luks2KeyslotAdd(void) crypt_free(cd); } +static void Luks2KeyslotParams(void) +{ + char key[128], key2[128]; + struct crypt_device *cd; + const char *cipher = "aes", *cipher_mode="xts-plain64"; + const char *cipher_spec = "aes-xts-plain64", *cipher_keyslot = "aes-cbc-essiv:sha256"; + const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a"; + const char *mk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e"; + size_t key_size_ret, key_size = strlen(mk_hex) / 2, keyslot_key_size = 16; + + crypt_decode_key(key, mk_hex, key_size); + crypt_decode_key(key2, mk_hex2, key_size); + + OK_(prepare_keyfile(KEYFILE1, PASSPHRASE, strlen(PASSPHRASE))); + OK_(prepare_keyfile(KEYFILE2, PASSPHRASE1, strlen(PASSPHRASE1))); + + EQ_(key_size, 2 * keyslot_key_size); + /* test crypt_keyslot_add_by_key */ + OK_(crypt_init(&cd, DEVICE_1)); + crypt_set_iteration_time(cd, 1); + OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, NULL)); + NULL_(crypt_keyslot_get_encryption(cd, 0, &key_size_ret)); + OK_(strcmp(crypt_keyslot_get_encryption(cd, CRYPT_ANY_SLOT, &key_size_ret), cipher_spec)); + EQ_(key_size_ret, key_size); + + // Normal slots + EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, PASSPHRASE, strlen(PASSPHRASE))); + EQ_(1, crypt_keyslot_add_by_passphrase(cd, 1, PASSPHRASE, strlen(PASSPHRASE), PASSPHRASE1,strlen(PASSPHRASE1))); + EQ_(2, crypt_keyslot_add_by_key(cd, 2, key2, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT)); + EQ_(6, crypt_keyslot_add_by_keyfile(cd, 6, KEYFILE1, 0, KEYFILE2, 0)); + + // Slots with different encryption type + OK_(crypt_keyslot_set_encryption(cd, cipher_keyslot, keyslot_key_size)); + OK_(strcmp(crypt_keyslot_get_encryption(cd, CRYPT_ANY_SLOT, &key_size_ret), cipher_keyslot)); + EQ_(key_size_ret, keyslot_key_size); + + EQ_(3, crypt_keyslot_add_by_volume_key(cd, 3, key, key_size, PASSPHRASE, strlen(PASSPHRASE))); + EQ_(4, crypt_keyslot_add_by_passphrase(cd, 4, PASSPHRASE, strlen(PASSPHRASE), PASSPHRASE1,strlen(PASSPHRASE1))); + EQ_(5, crypt_keyslot_add_by_key(cd, 5, key2, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT)); + EQ_(7, crypt_keyslot_add_by_keyfile(cd, 7, KEYFILE1, 0, KEYFILE2, 0)); + + crypt_free(cd); + + OK_(crypt_init(&cd, DEVICE_1)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + + EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_ACTIVE); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 0, &key_size_ret), cipher_spec)); + EQ_(key_size_ret, key_size); + + EQ_(crypt_keyslot_status(cd, 1), CRYPT_SLOT_ACTIVE); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 1, &key_size_ret), cipher_spec)); + EQ_(key_size_ret, key_size); + + EQ_(crypt_keyslot_status(cd, 2), CRYPT_SLOT_UNBOUND); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 2, &key_size_ret), cipher_spec)); + EQ_(key_size_ret, key_size); + + EQ_(crypt_keyslot_status(cd, 6), CRYPT_SLOT_ACTIVE); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 6, &key_size_ret), cipher_spec)); + EQ_(key_size_ret, key_size); + + EQ_(crypt_keyslot_status(cd, 3), CRYPT_SLOT_ACTIVE); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 3, &key_size_ret), cipher_keyslot)); + EQ_(key_size_ret, keyslot_key_size); + + EQ_(crypt_keyslot_status(cd, 4), CRYPT_SLOT_ACTIVE); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 4, &key_size_ret), cipher_keyslot)); + EQ_(key_size_ret, keyslot_key_size); + + EQ_(crypt_keyslot_status(cd, 5), CRYPT_SLOT_UNBOUND); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 5, &key_size_ret), cipher_keyslot)); + EQ_(key_size_ret, keyslot_key_size); + + EQ_(crypt_keyslot_status(cd, 7), CRYPT_SLOT_ACTIVE); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 7, &key_size_ret), cipher_keyslot)); + EQ_(key_size_ret, keyslot_key_size); + + crypt_set_iteration_time(cd, 1); + EQ_(8, crypt_keyslot_change_by_passphrase(cd, 1, 8, PASSPHRASE1, strlen(PASSPHRASE1), PASSPHRASE, strlen(PASSPHRASE))); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 8, &key_size_ret), cipher_spec)); + EQ_(key_size_ret, key_size); + + /* Revert to default */ + EQ_(9, crypt_keyslot_change_by_passphrase(cd, 5, 9, PASSPHRASE1, strlen(PASSPHRASE1), PASSPHRASE, strlen(PASSPHRASE))); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 9, &key_size_ret), cipher_spec)); + EQ_(key_size_ret, key_size); + + /* Set new encryption params */ + OK_(crypt_keyslot_set_encryption(cd, cipher_keyslot, keyslot_key_size)); + + EQ_(1, crypt_keyslot_change_by_passphrase(cd, 8, 1, PASSPHRASE, strlen(PASSPHRASE), PASSPHRASE1, strlen(PASSPHRASE1))); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 1, &key_size_ret), cipher_keyslot)); + EQ_(key_size_ret, keyslot_key_size); + + EQ_(10, crypt_keyslot_change_by_passphrase(cd, 2, 10, PASSPHRASE1, strlen(PASSPHRASE1), PASSPHRASE, strlen(PASSPHRASE))); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 10, &key_size_ret), cipher_keyslot)); + EQ_(key_size_ret, keyslot_key_size); + + EQ_(0, crypt_keyslot_change_by_passphrase(cd, 0, 0, PASSPHRASE, strlen(PASSPHRASE), PASSPHRASE1, strlen(PASSPHRASE1))); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 0, &key_size_ret), cipher_keyslot)); + EQ_(key_size_ret, keyslot_key_size); + + crypt_free(cd); + + /* LUKS1 compatible calls */ + OK_(crypt_init(&cd, DEVICE_1)); + crypt_set_iteration_time(cd, 1); + OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, NULL)); + NULL_(crypt_keyslot_get_encryption(cd, 0, &key_size_ret)); + OK_(strcmp(crypt_keyslot_get_encryption(cd, CRYPT_ANY_SLOT, &key_size_ret), cipher_spec)); + EQ_(key_size_ret, key_size); + EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, PASSPHRASE, strlen(PASSPHRASE))); + OK_(strcmp(crypt_keyslot_get_encryption(cd, 0, &key_size_ret), cipher_spec)); + EQ_(key_size_ret, key_size); + crypt_free(cd); + + _remove_keyfiles(); +} + static void Luks2ActivateByKeyring(void) { #ifdef KERNEL_KEYRING @@ -3293,6 +3413,7 @@ int main(int argc, char *argv[]) RUN_(LuksConvert, "LUKS1 <-> LUKS2 conversions"); RUN_(Pbkdf, "Default PBKDF manipulation routines"); RUN_(Luks2KeyslotAdd, "Add a new keyslot by unused key"); + RUN_(Luks2KeyslotParams, "Add a new keyslot with different encryption"); RUN_(Luks2ActivateByKeyring, "LUKS2 activation by passphrase in keyring"); RUN_(Luks2Requirements, "LUKS2 requirements flags"); RUN_(Luks2Integrity, "LUKS2 with data integrity"); diff --git a/tests/compat-test b/tests/compat-test index 81b8d471..e4ba0d39 100755 --- a/tests/compat-test +++ b/tests/compat-test @@ -398,6 +398,9 @@ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT -s 256 --mas $CRYPTSETUP luksOpen --master-key-file /dev/urandom $LOOPDEV $DEV_NAME 2>/dev/null && fail $CRYPTSETUP luksOpen --master-key-file $KEY1 $LOOPDEV $DEV_NAME || fail $CRYPTSETUP -q luksClose $DEV_NAME || fail +# unsupported pe-keyslot encryption +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT -s 128 --keyslot-cipher "aes-cbc-plain" $LOOPDEV 2>/dev/null && fail +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT -s 128 --keyslot-key-size 256 $LOOPDEV 2>/dev/null && fail prepare "[17] AddKey volume key, passphrase and keyfile" wipe # masterkey diff --git a/tests/compat-test2 b/tests/compat-test2 index 502640f5..715ab9f4 100755 --- a/tests/compat-test2 +++ b/tests/compat-test2 @@ -896,5 +896,26 @@ echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --l $CRYPTSETUP luksDump $LOOPDEV | grep "Metadata area:" | grep -q "16384 \[bytes\]" || fail $CRYPTSETUP luksDump $LOOPDEV | grep "Keyslots area:" | grep -q "131072 \[bytes\]" || fail +prepare "[41] Per-keyslot encryption parameters" wipe +KEYSLOT_CIPHER="aes-cbc-plain64" +$CRYPTSETUP -q luksFormat --type luks2 $LOOPDEV $KEY1 $FAST_PBKDF_OPT --key-slot 0 --keyslot-cipher $KEYSLOT_CIPHER --keyslot-key-size 128 || fail +[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "0: luks2" | grep "Cipher:" | sed -e 's/[[:space:]]\+Cipher:\ \+//g')" = $KEYSLOT_CIPHER ] || fail +[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "0: luks2" | grep "Cipher key:"| sed -e 's/[[:space:]]\+Cipher\ key:\ \+//g')" = "128 bits" ] || fail +$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT --key-slot 1 --keyslot-cipher $KEYSLOT_CIPHER --keyslot-key-size 128 || fail +[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "1: luks2" | grep "Cipher:" | sed -e 's/[[:space:]]\+Cipher:\ \+//g')" = $KEYSLOT_CIPHER ] || fail +[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "1: luks2" | grep "Cipher key:"| sed -e 's/[[:space:]]\+Cipher\ key:\ \+//g')" = "128 bits" ] || fail +$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT --key-slot 2 || fail +$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY2 $KEY1 --key-slot 2 --keyslot-cipher $KEYSLOT_CIPHER --keyslot-key-size 128 || fail +[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "2: luks2" | grep "Cipher:" | sed -e 's/[[:space:]]\+Cipher:\ \+//g')" = $KEYSLOT_CIPHER ] || fail +[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "2: luks2" | grep "Cipher key:"| sed -e 's/[[:space:]]\+Cipher\ key:\ \+//g')" = "128 bits" ] || fail +# unbound keyslot +echo $PWD3 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --key-slot 21 --unbound -s 32 --keyslot-cipher $KEYSLOT_CIPHER --keyslot-key-size 128 $LOOPDEV || fail +[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "21: luks2" | grep "Cipher:" | sed -e 's/[[:space:]]\+Cipher:\ \+//g')" = $KEYSLOT_CIPHER ] || fail +[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "21: luks2" | grep "Cipher key:"| sed -e 's/[[:space:]]\+Cipher\ key:\ \+//g')" = "128 bits" ] || fail +echo $PWD3 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --key-slot 22 --unbound -s 32 $LOOPDEV || fail +echo $PWD3 | $CRYPTSETUP luksConvertKey --key-slot 22 $LOOPDEV --keyslot-cipher $KEYSLOT_CIPHER --keyslot-key-size 128 $LOOPDEV || fail +[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "22: luks2" | grep "Cipher:" | sed -e 's/[[:space:]]\+Cipher:\ \+//g')" = $KEYSLOT_CIPHER ] || fail +[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "22: luks2" | grep "Cipher key:"| sed -e 's/[[:space:]]\+Cipher\ key:\ \+//g')" = "128 bits" ] || fail + remove_mapping exit 0