From: Nandakumar Raghavan Date: Mon, 6 Apr 2026 10:42:51 +0000 (+0000) Subject: repart: add EncryptKDF= option for LUKS2 partitions X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fa6eb2e016a53d741ec48a784a8ea075ec3d4fe0;p=thirdparty%2Fsystemd.git repart: add EncryptKDF= option for LUKS2 partitions systemd-repart currently creates LUKS2 encrypted partitions using libcryptsetup's default KDF (Argon2id), which requires ~1GB of memory during key derivation. This is too much for memory-constrained environments such as kdump with limited crashkernel memory, where luksOpen fails due to insufficient memory. Add an EncryptKDF= option to repart.d partition definitions that allows selecting the KDF type. Supported values are: - "argon2id" — Argon2id with libcryptsetup-benchmarked parameters - "pbkdf2" — PBKDF2 with libcryptsetup-benchmarked parameters - "minimal" — PBKDF2 with SHA-512, 1000 iterations, no benchmarking, matching the existing cryptsetup_set_minimal_pbkdf() behaviour used for TPM2-sealed keys When not specified, the libcryptsetup default (argon2id) is used, preserving existing behaviour. The KDF type is applied via sym_crypt_set_pbkdf_type() after sym_crypt_format() and before any keyslots are added. --- diff --git a/man/repart.d.xml b/man/repart.d.xml index 217692d8135..028929ac9e8 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -695,6 +695,30 @@ + + EncryptKDF= + + Specifies the key derivation function (KDF) to use for LUKS2 encryption keyslots. + Takes one of argon2id, pbkdf2, or minimal. + If not specified, the default is determined by the cryptsetup library (typically + argon2id). This option has no effect if Encrypt= is + off. + + When set to argon2id or pbkdf2, the specified KDF is + used with parameters benchmarked by the cryptsetup library. When set to minimal, + PBKDF2 is used with SHA-512, 1000 iterations, and no benchmarking — this is appropriate for + high-entropy keys (e.g. generated by a hardware key manager or sealed to a TPM) where the KDF only + needs to satisfy the LUKS2 format requirement, not strengthen a weak passphrase. + + Note that Argon2-based KDFs may require significant memory (up to 1GB) during key derivation. + In memory-constrained environments such as kdump with limited crashkernel memory, + minimal or pbkdf2 may be more appropriate. When + Encrypt= includes tpm2, the TPM2 keyslot always uses a minimal + PBKDF2 configuration regardless of this setting. + + + + Verity= diff --git a/src/repart/repart.c b/src/repart/repart.c index b0b92ba2ae0..b8443fab195 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -290,6 +290,14 @@ typedef enum IntegrityAlg { _INTEGRITY_ALG_INVALID = -EINVAL, } IntegrityAlg; +typedef enum EncryptKDF { + ENCRYPT_KDF_ARGON2ID, + ENCRYPT_KDF_PBKDF2, + ENCRYPT_KDF_MINIMAL, + _ENCRYPT_KDF_MAX, + _ENCRYPT_KDF_INVALID = -EINVAL, +} EncryptKDF; + typedef enum VerityMode { VERITY_OFF, VERITY_DATA, @@ -472,6 +480,7 @@ typedef struct Partition { size_t tpm2_n_hash_pcr_values; IntegrityMode integrity; IntegrityAlg integrity_alg; + EncryptKDF encrypt_kdf; VerityMode verity; char *verity_match_key; MinimizeMode minimize; @@ -598,6 +607,12 @@ static const char *integrity_alg_table[_INTEGRITY_ALG_MAX] = { [INTEGRITY_ALG_HMAC_SHA512] = "hmac-sha512", }; +static const char *encrypt_kdf_table[_ENCRYPT_KDF_MAX] = { + [ENCRYPT_KDF_ARGON2ID] = "argon2id", + [ENCRYPT_KDF_PBKDF2] = "pbkdf2", + [ENCRYPT_KDF_MINIMAL] = "minimal", +}; + static const char *verity_mode_table[_VERITY_MODE_MAX] = { [VERITY_OFF] = "off", [VERITY_DATA] = "data", @@ -636,6 +651,7 @@ DEFINE_PRIVATE_STRING_TABLE_LOOKUP(append_mode, AppendMode); DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE); DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(integrity_mode, IntegrityMode, INTEGRITY_INLINE); DEFINE_PRIVATE_STRING_TABLE_LOOKUP(integrity_alg, IntegrityAlg); +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(encrypt_kdf, EncryptKDF); DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode); DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(minimize_mode, MinimizeMode, MINIMIZE_BEST); DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(progress_phase, ProgressPhase); @@ -738,6 +754,7 @@ static Partition *partition_new(Context *c) { .progress_ratelimit = { 100 * USEC_PER_MSEC, 1 }, .fs_sector_size = UINT64_MAX, .discard = -1, + .encrypt_kdf = _ENCRYPT_KDF_INVALID, }; return p; @@ -2735,6 +2752,7 @@ static int config_parse_key_file( static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_integrity, integrity_mode, IntegrityMode, INTEGRITY_OFF); static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_integrity_alg, integrity_alg, IntegrityAlg, INTEGRITY_ALG_HMAC_SHA256); +static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_encrypt_kdf, encrypt_kdf, EncryptKDF, _ENCRYPT_KDF_INVALID); static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF); static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF); @@ -2892,6 +2910,7 @@ static int partition_read_definition( { "Partition", "KeyFile", config_parse_key_file, 0, p }, { "Partition", "Integrity", config_parse_integrity, 0, &p->integrity }, { "Partition", "IntegrityAlgorithm", config_parse_integrity_alg, 0, &p->integrity_alg }, + { "Partition", "EncryptKDF", config_parse_encrypt_kdf, 0, &p->encrypt_kdf }, { "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression }, { "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level }, { "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name }, @@ -3066,6 +3085,10 @@ static int partition_read_definition( log_syntax(NULL, LOG_WARNING, path, 1, 0, "Discard=yes has no effect with Encrypt=off."); + if (p->encrypt == ENCRYPT_OFF && p->encrypt_kdf >= 0) + log_syntax(NULL, LOG_WARNING, path, 1, 0, + "EncryptKDF= has no effect with Encrypt=off."); + if (p->encrypt != ENCRYPT_OFF && p->integrity == INTEGRITY_INLINE && p->discard > 0) return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), "Integrity=inline is incompatible with Discard=yes."); @@ -5266,6 +5289,30 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta if (r < 0) return log_error_errno(r, "Failed to LUKS2 format future partition: %m"); + /* If an explicit KDF is configured, apply it before adding any keyslots. */ + if (p->encrypt_kdf >= 0) { + if (p->encrypt_kdf == ENCRYPT_KDF_MINIMAL) { + /* Minimal PBKDF2 with sha512, 1000 iterations, no benchmarking — appropriate + * for high-entropy keys where the KDF only satisfies the LUKS2 format requirement + * (e.g. kdump with crashkernel=512MB). */ + r = cryptsetup_set_minimal_pbkdf(cd); + } else { + /* For argon2id or pbkdf2, set the type and let libcryptsetup benchmark + * and determine the parameters. */ + static const struct crypt_pbkdf_type argon2id_pbkdf = { + .type = "argon2id", + }; + static const struct crypt_pbkdf_type pbkdf2_pbkdf = { + .type = "pbkdf2", + }; + + r = sym_crypt_set_pbkdf_type(cd, p->encrypt_kdf == ENCRYPT_KDF_ARGON2ID ? + &argon2id_pbkdf : &pbkdf2_pbkdf); + } + if (r < 0) + return log_error_errno(r, "Failed to set KDF type: %m"); + } + bool allow_discards = p->integrity != INTEGRITY_INLINE && (arg_discard ? p->discard != 0 : p->discard > 0); if (allow_discards) { uint32_t flags;