diff --git a/lib/crypto_backend/crypto_backend.h b/lib/crypto_backend/crypto_backend.h index ac7b7565..80a08b57 100644 --- a/lib/crypto_backend/crypto_backend.h +++ b/lib/crypto_backend/crypto_backend.h @@ -67,7 +67,8 @@ int crypt_pbkdf_perf(const char *kdf, const char *hash, const char *salt, size_t salt_size, size_t volume_key_size, uint32_t time_ms, uint32_t max_memory_kb, uint32_t parallel_threads, - uint32_t *iterations_out, uint32_t *memory_out); + uint32_t *iterations_out, uint32_t *memory_out, + int (*progress)(long time_ms, void *usrptr), void *usrptr); #if USE_INTERNAL_PBKDF2 /* internal PBKDF2 implementation */ diff --git a/lib/crypto_backend/pbkdf_check.c b/lib/crypto_backend/pbkdf_check.c index 6f5aba5d..f6bb2114 100644 --- a/lib/crypto_backend/pbkdf_check.c +++ b/lib/crypto_backend/pbkdf_check.c @@ -2,6 +2,7 @@ * PBKDF performance check * Copyright (C) 2012-2017, Red Hat, Inc. All rights reserved. * Copyright (C) 2012-2017, Milan Broz + * Copyright (C) 2016-2017, Ondrej Mosnacek * * This file is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -26,15 +27,6 @@ #include #include "crypto_backend.h" -//#define BENCH_DEBUG - -#ifdef BENCH_DEBUG -#include /* FIXME: debug */ -#define bench_log(args...) fprintf(stderr, args) -#else -#define bench_log(args...) -#endif - #define BENCH_MIN_MS 250 #define BENCH_MIN_MS_FAST 10 #define BENCH_PERCENT_ATLEAST 95 @@ -123,7 +115,8 @@ static int measure_argon2(const char *kdf, const char *password, size_t password static int crypt_argon2_check(const char *kdf, const char *password, size_t password_length, const char *salt, size_t salt_length, size_t key_length, uint32_t min_t_cost, uint32_t max_m_cost, uint32_t parallel, - int target_ms, uint32_t *out_t_cost, uint32_t *out_m_cost) + int target_ms, uint32_t *out_t_cost, uint32_t *out_m_cost, + int (*progress)(long time_ms, void *usrptr), void *usrptr) { int r = 0; char *key = NULL; @@ -132,7 +125,6 @@ static int crypt_argon2_check(const char *kdf, const char *password, size_t pass long ms; long ms_atleast = (long)target_ms * BENCH_PERCENT_ATLEAST / 100; long ms_atmost = (long)target_ms * BENCH_PERCENT_ATMOST / 100; - struct timespec tstart, tend; if (key_length <= 0 || target_ms <= 0) return -EINVAL; @@ -140,8 +132,6 @@ static int crypt_argon2_check(const char *kdf, const char *password, size_t pass if (max_m_cost < min_m_cost) return -EINVAL; - clock_gettime(CLOCK_MONOTONIC, &tstart); - key = malloc(key_length); if (!key) return -ENOMEM; @@ -154,12 +144,17 @@ static int crypt_argon2_check(const char *kdf, const char *password, size_t pass r = measure_argon2(kdf, password, password_length, salt, salt_length, key, key_length, t_cost, m_cost, parallel, BENCH_SAMPLES_FAST, BENCH_MIN_MS, &ms); + if (!r) { + /* Update parameters to actual measurement */ + *out_t_cost = t_cost; + *out_m_cost = m_cost; + if (progress && progress(ms, usrptr)) + r = -EINTR; + } + if (r < 0) goto out; - bench_log("Pre-initial parameters: t_cost = %lu; m_cost = %lu; ms = %lu\n", - (long unsigned)t_cost, (long unsigned)m_cost, ms); - if (ms >= BENCH_MIN_MS) break; @@ -188,8 +183,6 @@ static int crypt_argon2_check(const char *kdf, const char *password, size_t pass } } } - bench_log("Initial parameters: t_cost = %lu; m_cost = %lu; ms = %lu\n", - (long unsigned)t_cost, (long unsigned)m_cost, ms); /* * 2. Use the params obtained in (1.) to estimate the target params. @@ -221,22 +214,19 @@ static int crypt_argon2_check(const char *kdf, const char *password, size_t pass r = measure_argon2(kdf, password, password_length, salt, salt_length, key, key_length, t_cost, m_cost, parallel, BENCH_SAMPLES_SLOW, ms_atleast, &ms); + + if (!r) { + /* Update parameters to actual measurement */ + *out_t_cost = t_cost; + *out_m_cost = m_cost; + if (progress && progress(ms, usrptr)) + r = -EINTR; + } + if (r < 0) goto out; - bench_log("Candidate parameters: t_cost = %lu; m_cost = %lu; ms = %lu\n", - (long unsigned)t_cost, (long unsigned)m_cost, ms); - } while(ms < ms_atleast || ms > ms_atmost); - - bench_log("Accepted parameters: t_cost = %lu; m_cost = %lu\n", - (long unsigned)t_cost, (long unsigned)m_cost); - - clock_gettime(CLOCK_MONOTONIC, &tend); - - bench_log("Benchmark took: %ld ms\n", timespec_ms(&tstart, &tend)); - - *out_t_cost = t_cost; - *out_m_cost = m_cost; + } while (ms < ms_atleast || ms > ms_atmost); out: if (key) { crypt_backend_memzero(key, key_length); @@ -249,7 +239,9 @@ out: static 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, uint32_t *iter_secs) + size_t key_length, uint32_t *iter_secs, int target_ms, + int (*progress)(long time_ms, void *usrptr), void *usrptr) + { struct rusage rstart, rend; int r = 0, step = 0; @@ -273,6 +265,7 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash, r = crypt_pbkdf(kdf, hash, password, password_length, salt, salt_length, key, key_length, iterations, 0, 0); + if (r < 0) goto out; @@ -282,6 +275,14 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash, } ms = time_ms(&rstart, &rend); + + *iter_secs = (uint32_t)((uint64_t)iterations * (uint64_t)target_ms / (uint64_t)ms); + + if (progress && progress(ms, usrptr)) { + r = -EINTR; + goto out; + } + if (ms > 500) break; @@ -299,9 +300,6 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash, goto out; } } - - if (iter_secs) - *iter_secs = (iterations * 1000) / ms; out: if (key) { crypt_backend_memzero(key, key_length); @@ -317,31 +315,27 @@ int crypt_pbkdf_perf(const char *kdf, const char *hash, const char *salt, size_t salt_size, size_t volume_key_size, uint32_t time_ms, uint32_t max_memory_kb, uint32_t parallel_threads, - uint32_t *iterations_out, uint32_t *memory_out) + uint32_t *iterations_out, uint32_t *memory_out, + int (*progress)(long time_ms, void *usrptr), void *usrptr) { - uint32_t iterations_sec; - int r; + int r = -EINVAL; - if (!kdf) + if (!kdf || !iterations_out || !memory_out) return -EINVAL; - if (!strcmp(kdf, "pbkdf2")) { - if (!iterations_out || memory_out) - return -EINVAL; + *memory_out = 0; + *iterations_out = 0; + if (!strcmp(kdf, "pbkdf2")) r = crypt_pbkdf_check(kdf, hash, password, password_size, - salt, salt_size, volume_key_size, &iterations_sec); - - *iterations_out = (uint32_t)((uint64_t)iterations_sec * (uint64_t)time_ms / 1000); - } else if (!strncmp(kdf, "argon2", 6)) { - if (!iterations_out || !memory_out) - return -EINVAL; - - r = crypt_argon2_check(kdf, password, password_size, salt, salt_size, - volume_key_size, ARGON2_MIN_T_COST, max_memory_kb, - parallel_threads, time_ms, iterations_out, memory_out); - } else - r = -EINVAL; + salt, salt_size, volume_key_size, + iterations_out, time_ms, progress, usrptr); + else if (!strncmp(kdf, "argon2", 6)) + r = crypt_argon2_check(kdf, password, password_size, + salt, salt_size, volume_key_size, + ARGON2_MIN_T_COST, max_memory_kb, + parallel_threads, time_ms, iterations_out, + memory_out, progress, usrptr); return r; } diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index d98095f8..902e55db 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -1110,14 +1110,14 @@ int crypt_benchmark(struct crypt_device *cd, * @return @e 0 on success or negative errno value otherwise. */ int crypt_benchmark_pbkdf(struct crypt_device *cd, - const struct crypt_pbkdf_type *pbkdf, + struct crypt_pbkdf_type *pbkdf, const char *password, size_t password_size, const char *salt, size_t salt_size, size_t volume_key_size, - uint32_t *iterations, - uint32_t *memory); + int (*progress)(long time_ms, void *usrptr), + void *usrptr); /** @} */ diff --git a/lib/luks1/keymanage.c b/lib/luks1/keymanage.c index 63a73f0c..12b156a3 100644 --- a/lib/luks1/keymanage.c +++ b/lib/luks1/keymanage.c @@ -727,7 +727,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, size_t blocksPerStripeSet, currentSector; int r; uuid_t partitionUuid; - const struct crypt_pbkdf_type pbkdf = { + struct crypt_pbkdf_type pbkdf = { .type = CRYPT_KDF_PBKDF2, .hash = hashSpec, .time_ms = 1000, @@ -785,7 +785,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, } r = crypt_benchmark_pbkdf(ctx, &pbkdf, "foo", 3, "bar", 3, vk->keylength, - PBKDF2_per_sec, NULL); + NULL, NULL); if (r < 0) { log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"), header->hashSpec); @@ -793,6 +793,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, } /* Compute master key digest */ + *PBKDF2_per_sec = pbkdf.time_ms; iteration_time_ms /= 8; header->mkDigestIterations = at_least((uint32_t)(*PBKDF2_per_sec/1024) * iteration_time_ms, LUKS_MKD_ITERATIONS_MIN); @@ -864,7 +865,7 @@ int LUKS_set_key(unsigned int keyIndex, char *AfKey = NULL; size_t AFEKSize; double PBKDF2_temp; - const struct crypt_pbkdf_type pbkdf = { + struct crypt_pbkdf_type pbkdf = { .type = CRYPT_KDF_PBKDF2, .hash = hdr->hashSpec, .time_ms = 1000, @@ -887,7 +888,7 @@ int LUKS_set_key(unsigned int keyIndex, log_dbg("Calculating data for key slot %d", keyIndex); r = crypt_benchmark_pbkdf(ctx, &pbkdf, "foo", 3, "bar", 3, vk->keylength, - PBKDF2_per_sec, NULL); + NULL, NULL); if (r < 0) { log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"), hdr->hashSpec); @@ -897,6 +898,7 @@ int LUKS_set_key(unsigned int keyIndex, /* * Final iteration count is at least LUKS_SLOT_ITERATIONS_MIN */ + *PBKDF2_per_sec = pbkdf.time_ms; 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, diff --git a/lib/utils_benchmark.c b/lib/utils_benchmark.c index 47fbb1a3..4086f48f 100644 --- a/lib/utils_benchmark.c +++ b/lib/utils_benchmark.c @@ -232,14 +232,14 @@ out: } int crypt_benchmark_pbkdf(struct crypt_device *cd, - const struct crypt_pbkdf_type *pbkdf, + struct crypt_pbkdf_type *pbkdf, const char *password, size_t password_size, const char *salt, size_t salt_size, size_t volume_key_size, - uint32_t *iterations, - uint32_t *memory) + int (*progress)(long time_ms, void *usrptr), + void *usrptr) { int r; @@ -248,7 +248,7 @@ int crypt_benchmark_pbkdf(struct crypt_device *cd, return r; /* Hack to not print hash for argon, it is used also for AF later.*/ - if (pbkdf->hash && !memory) + if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) log_dbg("Running %s-%s benchmark.", pbkdf->type, pbkdf->hash); else log_dbg("Running %s benchmark.", pbkdf->type); @@ -256,11 +256,11 @@ int crypt_benchmark_pbkdf(struct crypt_device *cd, r = crypt_pbkdf_perf(pbkdf->type, pbkdf->hash, password, password_size, salt, salt_size, volume_key_size, pbkdf->time_ms, pbkdf->max_memory_kb, pbkdf->parallel_threads, - iterations, memory); + &pbkdf->time_ms, &pbkdf->max_memory_kb, progress, usrptr); if (!r) log_dbg(" %u iterations, %u memory (for %zu-bits key).", - iterations ? *iterations : 0, memory ? *memory : 0, + pbkdf->time_ms, pbkdf->max_memory_kb, volume_key_size * 8); return r; } diff --git a/src/cryptsetup.c b/src/cryptsetup.c index 1f49a1aa..e3e746ad 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -532,43 +532,57 @@ out: return r; } +static int benchmark_callback(long time_ms, void *usrptr) +{ + struct crypt_pbkdf_type *pbkdf = usrptr; + int r = 0; + + check_signal(&r); + if (r) + log_err("Benchmark interrupted.\n"); + else + log_dbg("PBKDF benchmark: memory cost = %u, iterations = %u, " + "threads = %u (took %ld ms)", pbkdf->max_memory_kb, + pbkdf->time_ms, pbkdf->parallel_threads, time_ms); + return r; +} + static int action_benchmark_kdf(const char *kdf, const char *hash, size_t key_size) { int r; if (!strcmp(kdf, CRYPT_KDF_PBKDF2)) { - const struct crypt_pbkdf_type pbkdf = { + struct crypt_pbkdf_type pbkdf = { .type = CRYPT_KDF_PBKDF2, .hash = hash, .time_ms = 1000, }; - uint32_t kdf_iters; r = crypt_benchmark_pbkdf(NULL, &pbkdf, "foo", 3, "bar", 3, key_size, - &kdf_iters, NULL); + &benchmark_callback, &pbkdf); 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); + hash, pbkdf.time_ms, key_size * 8); } else { - const struct crypt_pbkdf_type pbkdf = { + struct crypt_pbkdf_type pbkdf = { .type = kdf, .time_ms = opt_iteration_time ?: 800, .max_memory_kb = opt_pbkdf_memory, .parallel_threads = opt_pbkdf_parallel, }; - uint32_t iters, memory; r = crypt_benchmark_pbkdf(NULL, &pbkdf, "foo", 3, - "barbarbarbarbarbar", 18, key_size, &iters, &memory); + "barbarbarbarbarbar", 18, key_size, + &benchmark_callback, &pbkdf); if (r < 0) log_std("%-10s N/A\n", kdf); else log_std("%-10s %4u iterations, %5u memory, " "%1u parallel threads (CPUs) for " - "%zu-bit key (%u ms time)\n", kdf, - iters, memory, pbkdf.parallel_threads, - key_size * 8, (unsigned)pbkdf.time_ms); + "%zu-bit key (requested %u ms time)\n", kdf, + pbkdf.time_ms, pbkdf.max_memory_kb, pbkdf.parallel_threads, + key_size * 8, opt_iteration_time ?: 800); } return r;