From 14e085f70eb977f09de3fbebb81c9cea21c82328 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Fri, 1 Mar 2019 20:53:12 +0100 Subject: [PATCH] Move cipher performance check to crypto backend. --- lib/crypto_backend/Makemodule.am | 3 +- lib/crypto_backend/cipher_check.c | 157 +++++++++++++++++++++ lib/crypto_backend/crypto_backend.h | 5 + lib/utils_benchmark.c | 209 ++++------------------------ 4 files changed, 193 insertions(+), 181 deletions(-) create mode 100644 lib/crypto_backend/cipher_check.c diff --git a/lib/crypto_backend/Makemodule.am b/lib/crypto_backend/Makemodule.am index 0d0ab0c3..f33cd456 100644 --- a/lib/crypto_backend/Makemodule.am +++ b/lib/crypto_backend/Makemodule.am @@ -10,7 +10,8 @@ libcrypto_backend_la_SOURCES = \ lib/crypto_backend/pbkdf_check.c \ lib/crypto_backend/crc32.c \ lib/crypto_backend/argon2_generic.c \ - lib/crypto_backend/cipher_generic.c + lib/crypto_backend/cipher_generic.c \ + lib/crypto_backend/cipher_check.c if CRYPTO_BACKEND_GCRYPT libcrypto_backend_la_SOURCES += lib/crypto_backend/crypto_gcrypt.c diff --git a/lib/crypto_backend/cipher_check.c b/lib/crypto_backend/cipher_check.c new file mode 100644 index 00000000..e2fe4f87 --- /dev/null +++ b/lib/crypto_backend/cipher_check.c @@ -0,0 +1,157 @@ +/* + * Cipher performance check + * + * Copyright (C) 2018-2019 Red Hat, Inc. All rights reserved. + * Copyright (C) 2018-2019 Milan Broz + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include "crypto_backend_internal.h" + +/* + * This is not simulating storage, so using disk block causes extreme overhead. + * Let's use some fixed block size where results are more reliable... + */ +#define CIPHER_BLOCK_BYTES 65536 + +/* + * If the measured value is lower, encrypted buffer is probably too small + * and calculated values are not reliable. + */ +#define CIPHER_TIME_MIN_MS 0.001 + +/* + * The whole test depends on Linux kernel usermode crypto API for now. + * (The same implementations are used in dm-crypt though.) + */ + +static int time_ms(struct timespec *start, struct timespec *end, double *ms) +{ + double start_ms, end_ms; + + start_ms = start->tv_sec * 1000.0 + start->tv_nsec / (1000.0 * 1000); + end_ms = end->tv_sec * 1000.0 + end->tv_nsec / (1000.0 * 1000); + + *ms = end_ms - start_ms; + return 0; +} + +static int cipher_perf_one(const char *name, const char *mode, char *buffer, size_t buffer_size, + const char *key, size_t key_size, const char *iv, size_t iv_size, int enc) +{ + struct crypt_cipher_kernel cipher; + size_t done = 0, block = CIPHER_BLOCK_BYTES; + int r; + + if (buffer_size < block) + block = buffer_size; + + r = crypt_cipher_init_kernel(&cipher, name, mode, key, key_size); + if (r < 0) + return r; + + while (done < buffer_size) { + if ((done + block) > buffer_size) + block = buffer_size - done; + + if (enc) + r = crypt_cipher_encrypt_kernel(&cipher, &buffer[done], &buffer[done], + block, iv, iv_size); + else + r = crypt_cipher_decrypt_kernel(&cipher, &buffer[done], &buffer[done], + block, iv, iv_size); + if (r < 0) + break; + + done += block; + } + + crypt_cipher_destroy_kernel(&cipher); + + return r; +} +static int cipher_measure(const char *name, const char *mode, char *buffer, size_t buffer_size, + const char *key, size_t key_size, const char *iv, size_t iv_size, + int encrypt, double *ms) +{ + struct timespec start, end; + int r; + + /* + * Using getrusage would be better here but the precision + * is not adequate, so better stick with CLOCK_MONOTONIC + */ + if (clock_gettime(CLOCK_MONOTONIC_RAW, &start) < 0) + return -EINVAL; + + r = cipher_perf_one(name, mode, buffer, buffer_size, key, key_size, iv, iv_size, encrypt); + if (r < 0) + return r; + + if (clock_gettime(CLOCK_MONOTONIC_RAW, &end) < 0) + return -EINVAL; + + r = time_ms(&start, &end, ms); + if (r < 0) + return r; + + if (*ms < CIPHER_TIME_MIN_MS) + return -ERANGE; + + return 0; +} + +static double speed_mbs(unsigned long bytes, double ms) +{ + double speed = bytes, s = ms / 1000.; + + return speed / (1024 * 1024) / s; +} + +int crypt_cipher_perf_kernel(const char *name, const char *mode, char *buffer, size_t buffer_size, + const char *key, size_t key_size, const char *iv, size_t iv_size, + double *encryption_mbs, double *decryption_mbs) +{ + double ms_enc, ms_dec, ms; + int r, repeat_enc, repeat_dec; + + ms_enc = 0.0; + repeat_enc = 1; + while (ms_enc < 1000.0) { + r = cipher_measure(name, mode, buffer, buffer_size, key, key_size, iv, iv_size, 1, &ms); + if (r < 0) + return r; + ms_enc += ms; + repeat_enc++; + } + + ms_dec = 0.0; + repeat_dec = 1; + while (ms_dec < 1000.0) { + r = cipher_measure(name, mode, buffer, buffer_size, key, key_size, iv, iv_size, 0, &ms); + if (r < 0) + return r; + ms_dec += ms; + repeat_dec++; + } + + *encryption_mbs = speed_mbs(buffer_size * repeat_enc, ms_enc); + *decryption_mbs = speed_mbs(buffer_size * repeat_dec, ms_dec); + + return 0; +} diff --git a/lib/crypto_backend/crypto_backend.h b/lib/crypto_backend/crypto_backend.h index d3e1960d..94fb50b6 100644 --- a/lib/crypto_backend/crypto_backend.h +++ b/lib/crypto_backend/crypto_backend.h @@ -96,6 +96,11 @@ int crypt_cipher_decrypt(struct crypt_cipher *ctx, const char *in, char *out, size_t length, const char *iv, size_t iv_length); +/* Benchmark of kernel cipher performance */ +int crypt_cipher_perf_kernel(const char *name, const char *mode, char *buffer, size_t buffer_size, + const char *key, size_t key_size, const char *iv, size_t iv_size, + double *encryption_mbs, double *decryption_mbs); + /* Check availability of a cipher (in kernel only) */ int crypt_cipher_check_kernel(const char *name, const char *mode, const char *integrity, size_t key_length); diff --git a/lib/utils_benchmark.c b/lib/utils_benchmark.c index 2822095c..56f28553 100644 --- a/lib/utils_benchmark.c +++ b/lib/utils_benchmark.c @@ -21,167 +21,9 @@ #include #include -#include #include "internal.h" -/* - * This is not simulating storage, so using disk block causes extreme overhead. - * Let's use some fixed block size where results are more reliable... - */ -#define CIPHER_BLOCK_BYTES 65536 - -/* - * If the measured value is lower, encrypted buffer is probably too small - * and calculated values are not reliable. - */ -#define CIPHER_TIME_MIN_MS 0.001 - -/* - * The whole test depends on Linux kernel usermode crypto API for now. - * (The same implementations are used in dm-crypt though.) - */ - -struct cipher_perf { - char name[32]; - char mode[32]; - char *key; - size_t key_length; - char *iv; - size_t iv_length; - size_t buffer_size; -}; - -static int time_ms(struct timespec *start, struct timespec *end, double *ms) -{ - double start_ms, end_ms; - - start_ms = start->tv_sec * 1000.0 + start->tv_nsec / (1000.0 * 1000); - end_ms = end->tv_sec * 1000.0 + end->tv_nsec / (1000.0 * 1000); - - *ms = end_ms - start_ms; - return 0; -} - -static int cipher_perf_one(struct crypt_device *cd, - struct cipher_perf *cp, char *buf, - size_t buf_size, int enc) -{ - struct crypt_cipher *cipher = NULL; - size_t done = 0, block = CIPHER_BLOCK_BYTES; - int r; - - if (buf_size < block) - block = buf_size; - - r = crypt_cipher_init(&cipher, cp->name, cp->mode, cp->key, cp->key_length); - if (r < 0) { - log_dbg(cd, "Cannot initialise cipher %s, mode %s.", cp->name, cp->mode); - return r; - } - - while (done < buf_size) { - if ((done + block) > buf_size) - block = buf_size - done; - - if (enc) - r = crypt_cipher_encrypt(cipher, &buf[done], &buf[done], - block, cp->iv, cp->iv_length); - else - r = crypt_cipher_decrypt(cipher, &buf[done], &buf[done], - block, cp->iv, cp->iv_length); - if (r < 0) - break; - - done += block; - } - - crypt_cipher_destroy(cipher); - - return r; -} -static int cipher_measure(struct crypt_device *cd, - struct cipher_perf *cp, char *buf, - size_t buf_size, int encrypt, double *ms) -{ - struct timespec start, end; - int r; - - /* - * Using getrusage would be better here but the precision - * is not adequate, so better stick with CLOCK_MONOTONIC - */ - if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) - return -EINVAL; - - r = cipher_perf_one(cd, cp, buf, buf_size, encrypt); - if (r < 0) - return r; - - if (clock_gettime(CLOCK_MONOTONIC, &end) < 0) - return -EINVAL; - - r = time_ms(&start, &end, ms); - if (r < 0) - return r; - - if (*ms < CIPHER_TIME_MIN_MS) { - log_dbg(cd, "Measured cipher runtime (%1.6f) is too low.", *ms); - return -ERANGE; - } - - return 0; -} - -static double speed_mbs(unsigned long bytes, double ms) -{ - double speed = bytes, s = ms / 1000.; - - return speed / (1024 * 1024) / s; -} - -static int cipher_perf(struct crypt_device *cd, struct cipher_perf *cp, - double *encryption_mbs, double *decryption_mbs) -{ - double ms_enc, ms_dec, ms; - int r, repeat_enc, repeat_dec; - void *buf = NULL; - - if (posix_memalign(&buf, crypt_getpagesize(), cp->buffer_size)) - return -ENOMEM; - - ms_enc = 0.0; - repeat_enc = 1; - while (ms_enc < 1000.0) { - r = cipher_measure(cd, cp, buf, cp->buffer_size, 1, &ms); - if (r < 0) { - free(buf); - return r; - } - ms_enc += ms; - repeat_enc++; - } - - ms_dec = 0.0; - repeat_dec = 1; - while (ms_dec < 1000.0) { - r = cipher_measure(cd, cp, buf, cp->buffer_size, 0, &ms); - if (r < 0) { - free(buf); - return r; - } - ms_dec += ms; - repeat_dec++; - } - - free(buf); - - *encryption_mbs = speed_mbs(cp->buffer_size * repeat_enc, ms_enc); - *decryption_mbs = speed_mbs(cp->buffer_size * repeat_dec, ms_dec); - - return 0; -} - int crypt_benchmark(struct crypt_device *cd, const char *cipher, const char *cipher_mode, @@ -191,12 +33,8 @@ int crypt_benchmark(struct crypt_device *cd, double *encryption_mbs, double *decryption_mbs) { - struct cipher_perf cp = { - .key_length = volume_key_size, - .iv_length = iv_size, - .buffer_size = buffer_size, - }; - char *c; + void *buffer = NULL; + char *iv = NULL, *key = NULL, mode[MAX_CIPHER_LEN], *c; int r; if (!cipher || !cipher_mode || !volume_key_size || !encryption_mbs || !decryption_mbs) @@ -207,29 +45,40 @@ int crypt_benchmark(struct crypt_device *cd, return r; r = -ENOMEM; - if (iv_size) { - cp.iv = malloc(iv_size); - if (!cp.iv) - goto out; - crypt_random_get(cd, cp.iv, iv_size, CRYPT_RND_NORMAL); - } - - cp.key = malloc(volume_key_size); - if (!cp.key) + if (posix_memalign(&buffer, crypt_getpagesize(), buffer_size)) goto out; - crypt_random_get(cd, cp.key, volume_key_size, CRYPT_RND_NORMAL); - strncpy(cp.name, cipher, sizeof(cp.name)-1); - strncpy(cp.mode, cipher_mode, sizeof(cp.mode)-1); + if (iv_size) { + iv = malloc(iv_size); + if (!iv) + goto out; + crypt_random_get(cd, iv, iv_size, CRYPT_RND_NORMAL); + } + key = malloc(volume_key_size); + if (!key) + goto out; + + crypt_random_get(cd, key, volume_key_size, CRYPT_RND_NORMAL); + + strncpy(mode, cipher_mode, sizeof(mode)-1); /* Ignore IV generator */ - if ((c = strchr(cp.mode, '-'))) + if ((c = strchr(mode, '-'))) *c = '\0'; - r = cipher_perf(cd, &cp, encryption_mbs, decryption_mbs); + r = crypt_cipher_perf_kernel(cipher, cipher_mode, buffer, buffer_size, key, volume_key_size, + iv, iv_size, encryption_mbs, decryption_mbs); + + if (r == -ERANGE) + log_dbg(cd, "Measured cipher runtime is too low."); + else if (r == -ENOTSUP || r == -ENOENT) + log_dbg(cd, "Cannot initialise cipher %s, mode %s.", cipher, cipher_mode); + out: - free(cp.key); - free(cp.iv); + free(buffer); + free(key); + free(iv); + return r; }