From 0abf57be5d33833d00e1f7cd1048e5f89f532038 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Sat, 5 Aug 2017 20:34:50 +0200 Subject: [PATCH] Change PBKDF interface API. Prepare API for PBKDF that can set three costs - time (similar to iterations in PBKDF2) - memory (required memory for memory-hard function) - threads (required number of threads/CPUs). This patch also removes wrongly designed API call crypt_benchmark_kdf and replaces it with the new call crypt_benchmark_pbkdf. Two functions for PBKDF per context setting are introduced: crypt_set_pbkdf_type and crypt_get_pbkdf_type. The patch should be backward compatible when using crypt_set_iteration_time function (works only for PBKDF2). Signed-off-by: Milan Broz --- lib/crypto_backend/crypto_backend.h | 2 +- lib/crypto_backend/pbkdf_check.c | 2 +- lib/internal.h | 5 + lib/libcryptsetup.h | 63 +++++++-- lib/libcryptsetup.sym | 5 +- lib/luks1/keymanage.c | 42 +++--- lib/luks1/luks.h | 4 +- lib/setup.c | 191 ++++++++++++++++++++++++++-- lib/utils.c | 6 + lib/utils_benchmark.c | 37 +++--- lib/utils_crypt.c | 23 ++++ lib/utils_crypt.h | 1 + src/cryptsetup.c | 40 +++--- 13 files changed, 341 insertions(+), 80 deletions(-) diff --git a/lib/crypto_backend/crypto_backend.h b/lib/crypto_backend/crypto_backend.h index 2518c379..237b9e14 100644 --- a/lib/crypto_backend/crypto_backend.h +++ b/lib/crypto_backend/crypto_backend.h @@ -60,7 +60,7 @@ int crypt_backend_rng(char *buffer, size_t length, int quality, int fips); int crypt_pbkdf_check(const char *kdf, const char *hash, const char *password, size_t password_length, const char *salt, size_t salt_length, - size_t key_length, uint64_t *iter_secs); + size_t key_length, uint32_t *iter_secs); int crypt_pbkdf(const char *kdf, const char *hash, const char *password, size_t password_length, const char *salt, size_t salt_length, diff --git a/lib/crypto_backend/pbkdf_check.c b/lib/crypto_backend/pbkdf_check.c index 7e86fd12..9c9fa5be 100644 --- a/lib/crypto_backend/pbkdf_check.c +++ b/lib/crypto_backend/pbkdf_check.c @@ -55,7 +55,7 @@ static long time_ms(struct rusage *start, struct rusage *end) int crypt_pbkdf_check(const char *kdf, const char *hash, const char *password, size_t password_length, const char *salt, size_t salt_length, - size_t key_length, uint64_t *iter_secs) + size_t key_length, uint32_t *iter_secs) { struct rusage rstart, rend; int r = 0, step = 0; diff --git a/lib/internal.h b/lib/internal.h index cd4cc10c..d6df7946 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -49,6 +49,9 @@ #define DEFAULT_MEM_ALIGNMENT 4096 #define MAX_ERROR_LENGTH 512 +#define MAX_PBKDF_THREADS 8 +#define MAX_PBKDF_MEMORY 1024*1024 /* 1GiB */ + #define at_least(a, b) ({ __typeof__(a) __at_least = (a); (__at_least >= (b))?__at_least:(b); }) struct crypt_device; @@ -115,6 +118,8 @@ ssize_t write_lseek_blockwise(int fd, size_t bsize, size_t alignment, void *buf, ssize_t read_lseek_blockwise(int fd, size_t bsize, size_t alignment, void *buf, size_t count, off_t offset); size_t crypt_getpagesize(void); +unsigned crypt_cpusonline(void); + int init_crypto(struct crypt_device *ctx); void logger(struct crypt_device *cd, int class, const char *file, int line, const char *format, ...) __attribute__ ((format (printf, 5, 6))); diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index 744087be..2df5773a 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -176,7 +176,7 @@ void crypt_set_iteration_time(struct crypt_device *cd, uint64_t iteration_time_m int crypt_set_data_device(struct crypt_device *cd, const char *device); /** - * @defgroup rng Cryptsetup RNG + * @defgroup rng Cryptsetup RNG and PBKDF * * @addtogroup rng * @{ @@ -206,6 +206,49 @@ void crypt_set_rng_type(struct crypt_device *cd, int rng_type); */ int crypt_get_rng_type(struct crypt_device *cd); +/** + * PBKDF parameters. + */ +struct crypt_pbkdf_type { + const char *type; /**< PBKDF algorithm */ + const char *hash; /**< Hash algorithm */ + uint32_t time_ms; /**< Requested time cost [milliseconds] */ + uint32_t max_memory_kb; /**< Requested memory cost [kilobytes] */ + uint32_t parallel_threads;/**< Requested parallel cost [threads] */ +}; + +/** PBKDF2 for LUKS1 and LUKS2 */ +#define CRYPT_KDF_PBKDF2 "pbkdf2" +/** Argon2i according to RFC */ +#define CRYPT_KDF_ARGON2I "argon2i" +/** Argon2id according to RFC */ +#define CRYPT_KDF_ARGON2ID "argon2id" + +/** + * Set default PBKDF (Password-Based Key Derivation Algorithm) for next keyslot + * about to get created with any crypt_keyslot_add_*() call. Works only with + * valid LUKSv2 device handles. + * + * @param cd crypt device handle + * @param pbkdf PBKDF parameters + * + * @return 0 on success or negative errno value otherwise. + * + */ +int crypt_set_pbkdf_type(struct crypt_device *cd, + const struct crypt_pbkdf_type *pbkdf); + +/** + * Get current default PBKDF (Password-Based Key Derivation Algorithm) for keyslots. + * Works only with LUKS device handles (both versions). + * + * @param cd crypt device handle + * + * @return struct on success or NULL value otherwise. + * + */ +const struct crypt_pbkdf_type *crypt_get_pbkdf_type(struct crypt_device *cd); + /** @} */ /** @@ -1092,27 +1135,29 @@ int crypt_benchmark(struct crypt_device *cd, double *decryption_mbs); /** - * Informational benchmark for KDF. + * Informational benchmark for PBKDF. * * @param cd crypt device handle - * @param kdf Key derivation function (e.g. "pbkdf2") - * @param hash Hash algorithm used in KDF (e.g. "sha256") + * @param pbkdf PBKDF parameters * @param password password for benchmark * @param password_size size of password * @param salt salt for benchmark * @param salt_size size of salt - * @param iterations_sec returns measured KDF iterations per second + * @param volume_key_size output volume key size + * @param iterations_sec returns measured PBKDF iterations per second * * @return @e 0 on success or negative errno value otherwise. */ -int crypt_benchmark_kdf(struct crypt_device *cd, - const char *kdf, - const char *hash, +int crypt_benchmark_pbkdf(struct crypt_device *cd, + const struct crypt_pbkdf_type *pbkdf, const char *password, size_t password_size, const char *salt, size_t salt_size, - uint64_t *iterations_sec); + size_t volume_key_size, + uint32_t *iterations, + uint32_t *memory); + /** @} */ /** diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym index b44a72c8..698b0ab3 100644 --- a/lib/libcryptsetup.sym +++ b/lib/libcryptsetup.sym @@ -3,6 +3,7 @@ CRYPTSETUP_2.0 { crypt_init; crypt_init_by_name; crypt_init_by_name_and_header; + crypt_set_log_callback; crypt_set_confirm_callback; crypt_set_iteration_time; @@ -37,7 +38,7 @@ CRYPTSETUP_2.0 { crypt_status; crypt_dump; crypt_benchmark; - crypt_benchmark_kdf; + crypt_benchmark_pbkdf; crypt_get_cipher; crypt_get_cipher_mode; crypt_get_integrity; @@ -58,6 +59,8 @@ CRYPTSETUP_2.0 { crypt_set_rng_type; crypt_get_rng_type; + crypt_set_pbkdf_type; + crypt_get_pbkdf_type; crypt_keyslot_max; crypt_keyslot_area; diff --git a/lib/luks1/keymanage.c b/lib/luks1/keymanage.c index edf3eebb..63a73f0c 100644 --- a/lib/luks1/keymanage.c +++ b/lib/luks1/keymanage.c @@ -386,7 +386,7 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx) struct luks_phdr temp_phdr; const unsigned char *sector = (const unsigned char*)phdr; struct volume_key *vk; - uint64_t PBKDF2_per_sec = 1; + uint32_t PBKDF2_per_sec = 1; int i, bad, r, need_write = 0; if (phdr->keyBytes != 16 && phdr->keyBytes != 32 && phdr->keyBytes != 64) { @@ -719,7 +719,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, unsigned int alignPayload, unsigned int alignOffset, uint32_t iteration_time_ms, - uint64_t *PBKDF2_per_sec, + uint32_t *PBKDF2_per_sec, int detached_metadata_device, struct crypt_device *ctx) { @@ -727,6 +727,11 @@ int LUKS_generate_phdr(struct luks_phdr *header, size_t blocksPerStripeSet, currentSector; int r; uuid_t partitionUuid; + const struct crypt_pbkdf_type pbkdf = { + .type = CRYPT_KDF_PBKDF2, + .hash = hashSpec, + .time_ms = 1000, + }; char luksMagic[] = LUKS_MAGIC; /* For separate metadata device allow zero alignment */ @@ -779,8 +784,8 @@ int LUKS_generate_phdr(struct luks_phdr *header, return r; } - r = crypt_benchmark_kdf(ctx, "pbkdf2", header->hashSpec, - "foo", 3, "bar", 3, PBKDF2_per_sec); + r = crypt_benchmark_pbkdf(ctx, &pbkdf, "foo", 3, "bar", 3, vk->keylength, + PBKDF2_per_sec, NULL); if (r < 0) { log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"), header->hashSpec); @@ -792,7 +797,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, header->mkDigestIterations = at_least((uint32_t)(*PBKDF2_per_sec/1024) * iteration_time_ms, LUKS_MKD_ITERATIONS_MIN); - r = crypt_pbkdf("pbkdf2", header->hashSpec, vk->key,vk->keylength, + r = crypt_pbkdf(CRYPT_KDF_PBKDF2, header->hashSpec, vk->key,vk->keylength, header->mkDigestSalt, LUKS_SALTSIZE, header->mkDigest,LUKS_DIGESTSIZE, header->mkDigestIterations, 0, 0); @@ -852,13 +857,19 @@ int LUKS_set_key(unsigned int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, struct volume_key *vk, uint32_t iteration_time_ms, - uint64_t *PBKDF2_per_sec, + uint32_t *PBKDF2_per_sec, struct crypt_device *ctx) { struct volume_key *derived_key; char *AfKey = NULL; size_t AFEKSize; - uint64_t PBKDF2_temp; + double PBKDF2_temp; + const struct crypt_pbkdf_type pbkdf = { + .type = CRYPT_KDF_PBKDF2, + .hash = hdr->hashSpec, + .time_ms = 1000, + }; + int r; if(hdr->keyblock[keyIndex].active != LUKS_KEY_DISABLED) { @@ -875,8 +886,8 @@ int LUKS_set_key(unsigned int keyIndex, log_dbg("Calculating data for key slot %d", keyIndex); - r = crypt_benchmark_kdf(ctx, "pbkdf2", hdr->hashSpec, - "foo", 3, "bar", 3, PBKDF2_per_sec); + r = crypt_benchmark_pbkdf(ctx, &pbkdf, "foo", 3, "bar", 3, vk->keylength, + PBKDF2_per_sec, NULL); if (r < 0) { log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"), hdr->hashSpec); @@ -884,13 +895,10 @@ int LUKS_set_key(unsigned int keyIndex, } /* - * Avoid floating point operation * Final iteration count is at least LUKS_SLOT_ITERATIONS_MIN */ - PBKDF2_temp = *PBKDF2_per_sec * (uint64_t)iteration_time_ms; - PBKDF2_temp /= 1024; - if (PBKDF2_temp > UINT32_MAX) - PBKDF2_temp = UINT32_MAX; + PBKDF2_temp = ((double)*PBKDF2_per_sec * iteration_time_ms / 1000.); + assert(PBKDF2_temp < UINT32_MAX); hdr->keyblock[keyIndex].passwordIterations = at_least((uint32_t)PBKDF2_temp, LUKS_SLOT_ITERATIONS_MIN); @@ -905,7 +913,7 @@ int LUKS_set_key(unsigned int keyIndex, if (r < 0) goto out; - r = crypt_pbkdf("pbkdf2", hdr->hashSpec, password, passwordLen, + r = crypt_pbkdf(CRYPT_KDF_PBKDF2, hdr->hashSpec, password, passwordLen, hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE, derived_key->key, hdr->keyBytes, hdr->keyblock[keyIndex].passwordIterations, 0, 0); @@ -963,7 +971,7 @@ int LUKS_verify_volume_key(const struct luks_phdr *hdr, { char checkHashBuf[LUKS_DIGESTSIZE]; - if (crypt_pbkdf("pbkdf2", hdr->hashSpec, vk->key, vk->keylength, + if (crypt_pbkdf(CRYPT_KDF_PBKDF2, hdr->hashSpec, vk->key, vk->keylength, hdr->mkDigestSalt, LUKS_SALTSIZE, checkHashBuf, LUKS_DIGESTSIZE, hdr->mkDigestIterations, 0, 0) < 0) @@ -1007,7 +1015,7 @@ static int LUKS_open_key(unsigned int keyIndex, goto out; } - r = crypt_pbkdf("pbkdf2", hdr->hashSpec, password, passwordLen, + r = crypt_pbkdf(CRYPT_KDF_PBKDF2, hdr->hashSpec, password, passwordLen, hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE, derived_key->key, hdr->keyBytes, hdr->keyblock[keyIndex].passwordIterations, 0, 0); diff --git a/lib/luks1/luks.h b/lib/luks1/luks.h index 791dd34a..115a4d7c 100644 --- a/lib/luks1/luks.h +++ b/lib/luks1/luks.h @@ -107,7 +107,7 @@ int LUKS_generate_phdr( unsigned int alignPayload, unsigned int alignOffset, uint32_t iteration_time_ms, - uint64_t *PBKDF2_per_sec, + uint32_t *PBKDF2_per_sec, int detached_metadata_device, struct crypt_device *ctx); @@ -148,7 +148,7 @@ int LUKS_set_key( struct luks_phdr *hdr, struct volume_key *vk, uint32_t iteration_time_ms, - uint64_t *PBKDF2_per_sec, + uint32_t *PBKDF2_per_sec, struct crypt_device *ctx); int LUKS_open_key_with_hdr( diff --git a/lib/setup.c b/lib/setup.c index b313e454..93895aa0 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -44,8 +44,11 @@ struct crypt_device { struct device *metadata_device; struct volume_key *volume_key; - uint64_t iteration_time; int rng_type; + struct crypt_pbkdf_type pbkdf; + + /* global context scope settings */ + unsigned iter_time_set:1; // FIXME: private binary headers and access it properly // through sub-library (LUKS1, TCRYPT) @@ -53,7 +56,7 @@ struct crypt_device { union { struct { /* used in CRYPT_LUKS1 */ struct luks_phdr hdr; - uint64_t PBKDF2_per_sec; + uint32_t PBKDF2_per_sec; } luks1; struct { /* used in CRYPT_PLAIN */ struct crypt_params_plain hdr; @@ -483,8 +486,8 @@ int crypt_init(struct crypt_device **cd, const char *device) dm_backend_init(); - h->iteration_time = DEFAULT_LUKS1_ITER_TIME; h->rng_type = crypt_random_default_key_rng(); + *cd = h; return 0; bad: @@ -544,9 +547,108 @@ int crypt_set_data_device(struct crypt_device *cd, const char *device) return crypt_check_data_device_size(cd); } +static int verify_pbkdf_params(struct crypt_device *cd, const struct crypt_pbkdf_type *pbkdf) +{ + int r = 0; + + if (!pbkdf->type || !pbkdf->hash) + return -EINVAL; + + /* TODO: initialise crypto and check the hash and pbkdf are both available */ + r = crypt_parse_pbkdf(pbkdf->type, NULL); + if (r < 0) { + log_err(cd, _("Unknown pbkdf type %s.\n"), pbkdf->type); + return r; + } + + if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) { + if (pbkdf->max_memory_kb || pbkdf->parallel_threads) { + log_err(cd, _("PBKDF max memory or parallel threads must not be set with pbkdf2.\n")); + return -EINVAL; + } + return 0; + } + + if (pbkdf->max_memory_kb > MAX_PBKDF_MEMORY) { + log_err(cd, _("Requested maximum PBKDF memory cost is too high (maximum is %d kilobytes).\n"), + MAX_PBKDF_MEMORY); + r = -EINVAL; + } + if (!pbkdf->max_memory_kb) { + log_err(cd, _("Requested maximum PBKDF memory can not be zero.\n")); + r = -EINVAL; + } + if (!pbkdf->parallel_threads) { + log_err(cd, _("Requested PBKDF parallel threads can not be zero.\n")); + r = -EINVAL; + } + if (!pbkdf->time_ms) { + log_err(cd, _("Requested PBKDF target time can not be zero.\n")); + r = -EINVAL; + } + + return r; +} + +static int init_pbkdf_type(struct crypt_device *cd, const struct crypt_pbkdf_type *pbkdf) +{ + const char *hash, *type; + int r; + struct crypt_pbkdf_type default_luks1 = { + .type = CRYPT_KDF_PBKDF2, + .hash = DEFAULT_LUKS1_HASH, + .time_ms = cd->iter_time_set ? cd->pbkdf.time_ms : DEFAULT_LUKS1_ITER_TIME + }; + unsigned cpus = crypt_cpusonline(); + + if (!pbkdf) { + pbkdf = &default_luks1; + + /* + * black magic due to crypt_set_iteration_time() but we don't + * want crypt_get_pbkdf_type() return invalid parameters + */ + r = verify_pbkdf_params(cd, pbkdf); + if (r) + return r; + } + + /* + * Crypto backend may be not initialized here, + * cannot check if algorithms are really available. + * It will fail later anyway :-) + */ + type = strdup(pbkdf->type); + hash = strdup(pbkdf->hash); + + if (!type || !hash) { + free(CONST_CAST(void*)type); + free(CONST_CAST(void*)hash); + return -ENOMEM; + } + + free(CONST_CAST(void*)cd->pbkdf.type); + free(CONST_CAST(void*)cd->pbkdf.hash); + cd->pbkdf.type = type; + cd->pbkdf.hash = hash; + + cd->pbkdf.time_ms = pbkdf->time_ms; + cd->pbkdf.max_memory_kb = pbkdf->max_memory_kb; + + if (pbkdf->parallel_threads > cpus) { + cd->pbkdf.parallel_threads = cpus; + log_dbg("Only %u active CPUs detected, PBKDF threads decreased from %d to %d.", + cpus, pbkdf->parallel_threads, cpus); + } else + cd->pbkdf.parallel_threads = pbkdf->parallel_threads; + + return 0; +} + static int _crypt_load_luks1(struct crypt_device *cd, int require_header, int repair) { struct luks_phdr hdr; + struct crypt_pbkdf_type pbkdf = {}; int r; r = init_crypto(cd); @@ -557,6 +659,14 @@ static int _crypt_load_luks1(struct crypt_device *cd, int require_header, int re if (r < 0) return r; + pbkdf.type = CRYPT_KDF_PBKDF2; + pbkdf.hash = hdr.hashSpec; + pbkdf.time_ms = cd->iter_time_set ? cd->pbkdf.time_ms : DEFAULT_LUKS1_ITER_TIME; + + r = init_pbkdf_type(cd, &pbkdf); + if (r) + return r; + if (!cd->type && !(cd->type = strdup(CRYPT_LUKS1))) return -ENOMEM; @@ -1022,6 +1132,17 @@ static int _crypt_format_luks1(struct crypt_device *cd, if(!cd->volume_key) return -ENOMEM; + r = init_pbkdf_type(cd, NULL); + if (r) + return r; + + if (params && params->hash && strcmp(params->hash, DEFAULT_LUKS1_HASH)) { + free(CONST_CAST(void*)cd->pbkdf.hash); + cd->pbkdf.hash = strdup(params->hash); + if (!cd->pbkdf.hash) + return -ENOMEM; + } + if (params && params->data_device) { cd->metadata_device = cd->device; cd->device = NULL; @@ -1036,11 +1157,10 @@ static int _crypt_format_luks1(struct crypt_device *cd, &alignment_offset, DEFAULT_DISK_ALIGNMENT); r = LUKS_generate_phdr(&cd->u.luks1.hdr, cd->volume_key, cipher, cipher_mode, - (params && params->hash) ? params->hash : DEFAULT_LUKS1_HASH, - uuid, LUKS_STRIPES, + cd->pbkdf.hash, uuid, LUKS_STRIPES, required_alignment / SECTOR_SIZE, alignment_offset / SECTOR_SIZE, - cd->iteration_time, &cd->u.luks1.PBKDF2_per_sec, + cd->pbkdf.time_ms, &cd->u.luks1.PBKDF2_per_sec, cd->metadata_device ? 1 : 0, cd); if (r < 0) return r; @@ -1603,6 +1723,9 @@ void crypt_free(struct crypt_device *cd) free(cd->u.none.active_name); } + free(CONST_CAST(void*)cd->pbkdf.type); + free(CONST_CAST(void*)cd->pbkdf.hash); + free(cd->type); /* Some structures can contain keys (TCRYPT), wipe it */ crypt_memzero(cd, sizeof(*cd)); @@ -1812,7 +1935,7 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, goto out; r = LUKS_set_key(keyslot, CONST_CAST(char*)new_passphrase, new_passphrase_size, - &cd->u.luks1.hdr, vk, cd->iteration_time, &cd->u.luks1.PBKDF2_per_sec, cd); + &cd->u.luks1.hdr, vk, cd->pbkdf.time_ms, &cd->u.luks1.PBKDF2_per_sec, cd); if(r < 0) goto out; @@ -1863,7 +1986,7 @@ 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->iteration_time, + &cd->u.luks1.hdr, vk, cd->pbkdf.time_ms, &cd->u.luks1.PBKDF2_per_sec, cd); if (keyslot_old == keyslot_new) { @@ -1940,7 +2063,7 @@ int crypt_keyslot_add_by_keyfile_offset(struct crypt_device *cd, goto out; r = LUKS_set_key(keyslot, new_password, new_passwordLen, - &cd->u.luks1.hdr, vk, cd->iteration_time, &cd->u.luks1.PBKDF2_per_sec, cd); + &cd->u.luks1.hdr, vk, cd->pbkdf.time_ms, &cd->u.luks1.PBKDF2_per_sec, cd); out: crypt_safe_free(password); crypt_safe_free(new_password); @@ -1998,7 +2121,7 @@ int crypt_keyslot_add_by_volume_key(struct crypt_device *cd, goto out; r = LUKS_set_key(keyslot, passphrase, passphrase_size, - &cd->u.luks1.hdr, vk, cd->iteration_time, &cd->u.luks1.PBKDF2_per_sec, cd); + &cd->u.luks1.hdr, vk, cd->pbkdf.time_ms, &cd->u.luks1.PBKDF2_per_sec, cd); out: crypt_free_volume_key(vk); return (r < 0) ? r : keyslot; @@ -2411,8 +2534,18 @@ int crypt_volume_key_verify(struct crypt_device *cd, void crypt_set_iteration_time(struct crypt_device *cd, uint64_t iteration_time_ms) { - log_dbg("Iteration time set to %" PRIu64 " milliseconds.", iteration_time_ms); - cd->iteration_time = iteration_time_ms; + int r = 0; + + if (!cd) + return; + + if (iteration_time_ms > UINT32_MAX) + iteration_time_ms = DEFAULT_LUKS1_ITER_TIME; + cd->pbkdf.time_ms = (uint32_t)iteration_time_ms; + cd->iter_time_set = 1; + + if (!r) + log_dbg("Iteration time set to %" PRIu64 " miliseconds.", iteration_time_ms); } void crypt_set_rng_type(struct crypt_device *cd, int rng_type) @@ -2433,6 +2566,38 @@ int crypt_get_rng_type(struct crypt_device *cd) return cd->rng_type; } +int crypt_set_pbkdf_type(struct crypt_device *cd, const struct crypt_pbkdf_type *pbkdf) +{ + int r; + + if (!cd) + return -EINVAL; + + if (!pbkdf) { + log_dbg("Resetting pbkdf type to default"); + cd->iter_time_set = 0; + return init_pbkdf_type(cd, NULL); + } + + log_dbg("PBKDF %s, hash %s, time_ms %u, max_memory_kb %u, parallel_threads %u.", + pbkdf->type ?: "(none)", pbkdf->hash ?: "(none)", pbkdf->time_ms, + pbkdf->max_memory_kb, pbkdf->parallel_threads); + + if (verify_pbkdf_params(cd, pbkdf)) + return -EINVAL; + + r = init_pbkdf_type(cd, pbkdf); + if (!r) + cd->iter_time_set = 1; + + return r; +} + +const struct crypt_pbkdf_type *crypt_get_pbkdf_type(struct crypt_device *cd) +{ + return cd ? &cd->pbkdf : NULL; +} + int crypt_memory_lock(struct crypt_device *cd, int lock) { return lock ? crypt_memlock_inc(cd) : crypt_memlock_dec(cd); @@ -2509,7 +2674,7 @@ static int _luks_dump(struct crypt_device *cd) log_std(cd, "\tAF stripes: \t%" PRIu32 "\n", cd->u.luks1.hdr.keyblock[i].stripes); } - else + else log_std(cd, "Key Slot %d: DISABLED\n", i); } return 0; diff --git a/lib/utils.c b/lib/utils.c index 5c1f7cc6..c7c66f69 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -36,6 +36,12 @@ size_t crypt_getpagesize(void) return r <= 0 ? DEFAULT_MEM_ALIGNMENT : (size_t)r; } +unsigned crypt_cpusonline(void) +{ + long r = sysconf(_SC_NPROCESSORS_ONLN); + return r < 0 ? 1 : r; +} + ssize_t read_buffer(int fd, void *buf, size_t count) { size_t read_size = 0; diff --git a/lib/utils_benchmark.c b/lib/utils_benchmark.c index bae40f60..e472dab8 100644 --- a/lib/utils_benchmark.c +++ b/lib/utils_benchmark.c @@ -231,39 +231,36 @@ out: return r; } -int crypt_benchmark_kdf(struct crypt_device *cd, - const char *kdf, - const char *hash, +int crypt_benchmark_pbkdf(struct crypt_device *cd, + const struct crypt_pbkdf_type *pbkdf, const char *password, size_t password_size, const char *salt, size_t salt_size, - uint64_t *iterations_sec) + size_t volume_key_size, + uint32_t *iterations, + uint32_t *memory) { - int r, key_length = 0; - - if (!iterations_sec) - return -EINVAL; + uint32_t iterations_sec; + int r; r = init_crypto(cd); if (r < 0) return r; - // FIXME: this should be in KDF check API parameters later - if (cd) - key_length = crypt_get_volume_key_size(cd); + if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) { + if (!iterations || memory) + return -EINVAL; - if (key_length == 0) - key_length = DEFAULT_LUKS1_KEYBITS / 8; + r = crypt_pbkdf_check(pbkdf->type, pbkdf->hash, password, password_size, + salt, salt_size, volume_key_size, &iterations_sec); - if (!strncmp(kdf, "pbkdf2", 6)) - r = crypt_pbkdf_check(kdf, hash, password, password_size, - salt, salt_size, key_length, iterations_sec); - else + *iterations = (uint32_t)((uint64_t)iterations_sec * (uint64_t)pbkdf->time_ms / 1000); + if (!r) + log_dbg("PBKDF2 benchmark, hash %s: %u iterations per second (%zu-bits key).", + pbkdf->hash, iterations_sec, volume_key_size * 8); + } else r = -EINVAL; - if (!r) - log_dbg("KDF %s, hash %s: %" PRIu64 " iterations per second (%d-bits key).", - kdf, hash, *iterations_sec, key_length * 8); return r; } diff --git a/lib/utils_crypt.c b/lib/utils_crypt.c index 8021d054..0c9363d3 100644 --- a/lib/utils_crypt.c +++ b/lib/utils_crypt.c @@ -106,6 +106,29 @@ int crypt_parse_hash_integrity_mode(const char *s, char *integrity) return 0; } +int crypt_parse_pbkdf(const char *s, const char **pbkdf) +{ + const char *tmp = NULL; + + if (!s) + return -EINVAL; + + if (!strcasecmp(s, CRYPT_KDF_PBKDF2)) + tmp = CRYPT_KDF_PBKDF2; + else if (!strcasecmp(s, CRYPT_KDF_ARGON2I)) + tmp = CRYPT_KDF_ARGON2I; + else if (!strcasecmp(s, CRYPT_KDF_ARGON2ID)) + tmp = CRYPT_KDF_ARGON2ID; + + if (!tmp) + return -EINVAL; + + if (pbkdf) + *pbkdf = tmp; + + return 0; +} + /* * Replacement for memset(s, 0, n) on stack that can be optimized out * Also used in safe allocations for explicit memory wipe. diff --git a/lib/utils_crypt.h b/lib/utils_crypt.h index bb4187a3..37ece874 100644 --- a/lib/utils_crypt.h +++ b/lib/utils_crypt.h @@ -34,6 +34,7 @@ struct crypt_device; int crypt_parse_name_and_mode(const char *s, char *cipher, int *key_nums, char *cipher_mode); int crypt_parse_hash_integrity_mode(const char *s, char *integrity); +int crypt_parse_pbkdf(const char *s, const char **pbkdf); void *crypt_safe_alloc(size_t size); void crypt_safe_free(void *data); diff --git a/src/cryptsetup.c b/src/cryptsetup.c index d900e424..be10b2e0 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -529,18 +529,28 @@ out: return r; } -static int action_benchmark_kdf(const char *hash) +static int action_benchmark_kdf(const char *kdf, const char *hash, size_t key_size) { - uint64_t kdf_iters; int r; + if (!strcmp(kdf, CRYPT_KDF_PBKDF2)) { + const struct crypt_pbkdf_type pbkdf = { + .type = CRYPT_KDF_PBKDF2, + .hash = hash, + .time_ms = 1000, + }; + uint32_t kdf_iters; - r = crypt_benchmark_kdf(NULL, "pbkdf2", hash, "foo", 3, "bar", 3, - &kdf_iters); - if (r < 0) - log_std("PBKDF2-%-9s N/A\n", hash); - else - log_std("PBKDF2-%-9s %7" PRIu64 " iterations per second for %d-bit key\n", - hash, kdf_iters, DEFAULT_LUKS1_KEYBITS); + r = crypt_benchmark_pbkdf(NULL, &pbkdf, "foo", 3, "bar", 3, key_size, + &kdf_iters, NULL); + if (r < 0) + log_std("PBKDF2-%-9s N/A\n", hash); + else + log_std("PBKDF2-%-9s %7u iterations per second for %zu-bit key\n", + hash, kdf_iters, key_size * 8); + } else { + log_err("Unknown PBKDF '%s'\n", kdf); + r = -EINVAL; + } return r; } @@ -594,15 +604,13 @@ static int action_benchmark(void) }; char cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; double enc_mbr = 0, dec_mbr = 0; - int key_size = (opt_key_size ?: DEFAULT_PLAIN_KEYBITS); + int key_size = (opt_key_size ?: DEFAULT_PLAIN_KEYBITS) / 8; int iv_size = 16, skipped = 0; char *c; int i, r; log_std(_("# Tests are approximate using memory only (no storage IO).\n")); - if (opt_hash) { - r = action_benchmark_kdf(opt_hash); - } else if (opt_cipher) { + if (opt_cipher) { r = crypt_parse_name_and_mode(opt_cipher, cipher, NULL, cipher_mode); if (r < 0) { log_err(_("No known cipher specification pattern detected.\n")); @@ -621,17 +629,17 @@ static int action_benchmark(void) iv_size = 0; r = benchmark_cipher_loop(cipher, cipher_mode, - key_size / 8, iv_size, + key_size, iv_size, &enc_mbr, &dec_mbr); if (!r) { log_std(N_("# Algorithm | Key | Encryption | Decryption\n")); log_std("%11s-%s %4db %6.1f MiB/s %6.1f MiB/s\n", - cipher, cipher_mode, key_size, enc_mbr, dec_mbr); + cipher, cipher_mode, key_size*8, enc_mbr, dec_mbr); } else if (r == -ENOENT) log_err(_("Cipher %s is not available.\n"), opt_cipher); } else { for (i = 0; bkdfs[i]; i++) { - r = action_benchmark_kdf(bkdfs[i]); + r = action_benchmark_kdf(CRYPT_KDF_PBKDF2, bkdfs[i], key_size); check_signal(&r); if (r == -EINTR) break;