2 * libcryptsetup - cryptsetup library, cipher benchmark
4 * Copyright (C) 2012-2020 Red Hat, Inc. All rights reserved.
5 * Copyright (C) 2012-2020 Milan Broz
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 int crypt_benchmark(struct crypt_device *cd,
29 const char *cipher_mode,
30 size_t volume_key_size,
33 double *encryption_mbs,
34 double *decryption_mbs)
37 char *iv = NULL, *key = NULL, mode[MAX_CIPHER_LEN], *c;
40 if (!cipher || !cipher_mode || !volume_key_size || !encryption_mbs || !decryption_mbs)
48 if (posix_memalign(&buffer, crypt_getpagesize(), buffer_size))
51 r = crypt_cipher_ivsize(cipher, cipher_mode);
52 if (r >= 0 && iv_size != (size_t)r) {
53 log_dbg(cd, "IV length for benchmark adjusted to %i bytes (requested %zu).", r, iv_size);
61 crypt_random_get(cd, iv, iv_size, CRYPT_RND_NORMAL);
64 key = malloc(volume_key_size);
68 crypt_random_get(cd, key, volume_key_size, CRYPT_RND_NORMAL);
70 strncpy(mode, cipher_mode, sizeof(mode)-1);
71 /* Ignore IV generator */
72 if ((c = strchr(mode, '-')))
75 r = crypt_cipher_perf_kernel(cipher, cipher_mode, buffer, buffer_size, key, volume_key_size,
76 iv, iv_size, encryption_mbs, decryption_mbs);
79 log_dbg(cd, "Measured cipher runtime is too low.");
81 log_dbg(cd, "Cannot initialize cipher %s, mode %s, key size %zu, IV size %zu.",
82 cipher, cipher_mode, volume_key_size, iv_size);
91 int crypt_benchmark_pbkdf(struct crypt_device *cd,
92 struct crypt_pbkdf_type *pbkdf,
97 size_t volume_key_size,
98 int (*progress)(uint32_t time_ms, void *usrptr),
104 if (!pbkdf || (!password && password_size))
111 kdf_opt = !strcmp(pbkdf->type, CRYPT_KDF_PBKDF2) ? pbkdf->hash : "";
113 log_dbg(cd, "Running %s(%s) benchmark.", pbkdf->type, kdf_opt);
115 r = crypt_pbkdf_perf(pbkdf->type, pbkdf->hash, password, password_size,
116 salt, salt_size, volume_key_size, pbkdf->time_ms,
117 pbkdf->max_memory_kb, pbkdf->parallel_threads,
118 &pbkdf->iterations, &pbkdf->max_memory_kb, progress, usrptr);
121 log_dbg(cd, "Benchmark returns %s(%s) %u iterations, %u memory, %u threads (for %zu-bits key).",
122 pbkdf->type, kdf_opt, pbkdf->iterations, pbkdf->max_memory_kb,
123 pbkdf->parallel_threads, volume_key_size * 8);
127 struct benchmark_usrptr {
128 struct crypt_device *cd;
129 struct crypt_pbkdf_type *pbkdf;
132 static int benchmark_callback(uint32_t time_ms, void *usrptr)
134 struct benchmark_usrptr *u = usrptr;
136 log_dbg(u->cd, "PBKDF benchmark: memory cost = %u, iterations = %u, "
137 "threads = %u (took %u ms)", u->pbkdf->max_memory_kb,
138 u->pbkdf->iterations, u->pbkdf->parallel_threads, time_ms);
144 * Used in internal places to benchmark crypt_device context PBKDF.
145 * Once requested parameters are benchmarked, iterations attribute is set,
146 * and the benchmarked values can be reused.
147 * Note that memory cost can be changed after benchmark (if used).
148 * NOTE: You need to check that you are benchmarking for the same key size.
150 int crypt_benchmark_pbkdf_internal(struct crypt_device *cd,
151 struct crypt_pbkdf_type *pbkdf,
152 size_t volume_key_size)
154 struct crypt_pbkdf_limits pbkdf_limits;
158 struct benchmark_usrptr u = {
163 r = crypt_pbkdf_get_limits(pbkdf->type, &pbkdf_limits);
167 if (pbkdf->flags & CRYPT_PBKDF_NO_BENCHMARK) {
168 if (pbkdf->iterations) {
169 log_dbg(cd, "Reusing PBKDF values (no benchmark flag is set).");
172 log_err(cd, _("PBKDF benchmark disabled but iterations not set."));
176 /* For PBKDF2 run benchmark always. Also note it depends on volume_key_size! */
177 if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
179 * For PBKDF2 it is enough to run benchmark for only 1 second
180 * and interpolate final iterations value from it.
182 ms_tmp = pbkdf->time_ms;
183 pbkdf->time_ms = 1000;
184 pbkdf->parallel_threads = 0; /* N/A in PBKDF2 */
185 pbkdf->max_memory_kb = 0; /* N/A in PBKDF2 */
187 r = crypt_benchmark_pbkdf(cd, pbkdf, "foo", 3, "bar", 3,
188 volume_key_size, &benchmark_callback, &u);
189 pbkdf->time_ms = ms_tmp;
191 log_err(cd, _("Not compatible PBKDF2 options (using hash algorithm %s)."),
196 PBKDF2_tmp = ((double)pbkdf->iterations * pbkdf->time_ms / 1000.);
197 if (PBKDF2_tmp > (double)UINT32_MAX)
199 pbkdf->iterations = at_least((uint32_t)PBKDF2_tmp, pbkdf_limits.min_iterations);
201 /* Already benchmarked */
202 if (pbkdf->iterations) {
203 log_dbg(cd, "Reusing PBKDF values.");
207 r = crypt_benchmark_pbkdf(cd, pbkdf, "foo", 3,
208 "0123456789abcdef0123456789abcdef", 32,
209 volume_key_size, &benchmark_callback, &u);
211 log_err(cd, _("Not compatible PBKDF options."));