mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-05 16:00:05 +01:00
Add simple cipher benchmarking.
This commit is contained in:
@@ -51,6 +51,7 @@ libcryptsetup_la_SOURCES = \
|
||||
nls.h \
|
||||
libcryptsetup.h \
|
||||
utils.c \
|
||||
utils_benchmark.c \
|
||||
utils_crypt.c \
|
||||
utils_crypt.h \
|
||||
utils_loop.c \
|
||||
|
||||
@@ -100,7 +100,7 @@ ssize_t read_blockwise(int fd, int bsize, void *_buf, size_t count);
|
||||
ssize_t write_lseek_blockwise(int fd, int bsize, char *buf, size_t count, off_t offset);
|
||||
|
||||
unsigned crypt_getpagesize(void);
|
||||
|
||||
int init_crypto(struct crypt_device *ctx);
|
||||
|
||||
void logger(struct crypt_device *cd, int class, const char *file, int line, const char *format, ...);
|
||||
#define log_dbg(x...) logger(NULL, CRYPT_LOG_DEBUG, __FILE__, __LINE__, x)
|
||||
|
||||
@@ -849,6 +849,29 @@ crypt_status_info crypt_status(struct crypt_device *cd, const char *name);
|
||||
*/
|
||||
int crypt_dump(struct crypt_device *cd);
|
||||
|
||||
/**
|
||||
* Informational benchmark for ciphers
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param cipher (e.g. "aes")
|
||||
* @param cipher_mode (e.g. "xts"), IV generator is ignored
|
||||
* @param volume_key_size size of volume key in bytes
|
||||
* @param iv_size size of IV in bytes
|
||||
* @param buffer_size size of encryption buffer in bytes used in test
|
||||
* @param encryption_mbs measured encryption speed in MiB/s
|
||||
* @param decryption_mbs measured decryption speed in MiB/s
|
||||
*
|
||||
* @return @e 0 on success or negative errno value otherwise.
|
||||
*/
|
||||
int crypt_benchmark(struct crypt_device *cd,
|
||||
const char *cipher,
|
||||
const char *cipher_mode,
|
||||
size_t volume_key_size,
|
||||
size_t iv_size,
|
||||
size_t buffer_size,
|
||||
double *encryption_mbs,
|
||||
double *decryption_mbs);
|
||||
|
||||
/**
|
||||
* Get cipher used in device
|
||||
*
|
||||
|
||||
@@ -39,6 +39,7 @@ CRYPTSETUP_1.0 {
|
||||
crypt_volume_key_verify;
|
||||
crypt_status;
|
||||
crypt_dump;
|
||||
crypt_benchmark;
|
||||
crypt_get_cipher;
|
||||
crypt_get_cipher_mode;
|
||||
crypt_get_uuid;
|
||||
|
||||
@@ -168,7 +168,7 @@ struct device *crypt_data_device(struct crypt_device *cd)
|
||||
return cd->device;
|
||||
}
|
||||
|
||||
static int init_crypto(struct crypt_device *ctx)
|
||||
int init_crypto(struct crypt_device *ctx)
|
||||
{
|
||||
int r;
|
||||
|
||||
|
||||
217
lib/utils_benchmark.c
Normal file
217
lib/utils_benchmark.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* libcryptsetup - cryptsetup library, cipher bechmark
|
||||
*
|
||||
* Copyright (C) 2012, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2012, Milan Broz
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#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
|
||||
|
||||
/*
|
||||
* 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 long time_ms(struct rusage *start, struct rusage *end)
|
||||
{
|
||||
long ms = 0;
|
||||
|
||||
/* For kernel backend, we need to measure only tim in kernel.
|
||||
ms = (end->ru_utime.tv_sec - start->ru_utime.tv_sec) * 1000;
|
||||
ms += (end->ru_utime.tv_usec - start->ru_utime.tv_usec) / 1000;
|
||||
*/
|
||||
|
||||
ms += (end->ru_stime.tv_sec - start->ru_stime.tv_sec) * 1000;
|
||||
ms += (end->ru_stime.tv_usec - start->ru_stime.tv_usec) / 1000;
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
static int cipher_perf_one(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("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 long cipher_measure(struct cipher_perf *cp, char *buf,
|
||||
size_t buf_size, int encrypt)
|
||||
{
|
||||
struct rusage rstart, rend;
|
||||
int r;
|
||||
|
||||
if (getrusage(RUSAGE_SELF, &rstart) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
r = cipher_perf_one(cp, buf, buf_size, encrypt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (getrusage(RUSAGE_SELF, &rend) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return time_ms(&rstart, &rend);
|
||||
}
|
||||
|
||||
static double speed_mbs(unsigned long bytes, unsigned long ms)
|
||||
{
|
||||
double speed = bytes, s = ms / 1000.;
|
||||
|
||||
return speed / (1024 * 1024) / s;
|
||||
}
|
||||
|
||||
static int cipher_perf(struct cipher_perf *cp,
|
||||
double *encryption_mbs, double *decryption_mbs)
|
||||
{
|
||||
long ms_enc, ms_dec, ms;
|
||||
int repeat_enc, repeat_dec;
|
||||
void *buf = NULL;
|
||||
|
||||
if (posix_memalign(&buf, crypt_getpagesize(), cp->buffer_size))
|
||||
return -ENOMEM;
|
||||
|
||||
ms_enc = 0;
|
||||
repeat_enc = 1;
|
||||
while (ms_enc < 1000) {
|
||||
ms = cipher_measure(cp, buf, cp->buffer_size, 1);
|
||||
if (ms < 0) {
|
||||
free(buf);
|
||||
return (int)ms;
|
||||
}
|
||||
ms_enc += ms;
|
||||
repeat_enc++;
|
||||
}
|
||||
|
||||
ms_dec = 0;
|
||||
repeat_dec = 1;
|
||||
while (ms_dec < 1000) {
|
||||
ms = cipher_measure(cp, buf, cp->buffer_size, 0);
|
||||
if (ms < 0) {
|
||||
free(buf);
|
||||
return (int)ms;
|
||||
}
|
||||
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,
|
||||
size_t volume_key_size,
|
||||
size_t iv_size,
|
||||
size_t buffer_size,
|
||||
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;
|
||||
int r;
|
||||
|
||||
if (!cipher || !cipher_mode || !volume_key_size)
|
||||
return -EINVAL;
|
||||
|
||||
r = init_crypto(cd);
|
||||
if (r < 0)
|
||||
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)
|
||||
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);
|
||||
|
||||
/* Ignore IV generator */
|
||||
if ((c = strchr(cp.mode, '-')))
|
||||
*c = '\0';
|
||||
|
||||
r = cipher_perf(&cp, encryption_mbs, decryption_mbs);
|
||||
out:
|
||||
free(cp.key);
|
||||
free(cp.iv);
|
||||
return r;
|
||||
}
|
||||
@@ -362,6 +362,23 @@ Identical to \fIremove\fR.
|
||||
.PP
|
||||
See also section 7 of the FAQ and \fBhttp://loop-aes.sourceforge.net\fR
|
||||
for more information regarding loop-AES.
|
||||
.SH MISCELLANEOUS
|
||||
.PP
|
||||
\fIbenchmark\fR <options>
|
||||
.IP
|
||||
Benchmarks ciphers. Without parameters it tries to measure few common
|
||||
configurations.
|
||||
|
||||
To benchmark other ciphers or modes, you need to specify \fB\-\-cipher\fR
|
||||
and \fB\-\-key-size\fR options.
|
||||
|
||||
\fBNOTE:\fR This benchmark is using memory only and is only informative.
|
||||
You cannot directly predict real storage encryption speed from it.
|
||||
|
||||
This benchmark requires kernel userspace crypto API interface to be available
|
||||
(kernel af_alg and af_skcipher modules, introduced in Linux kernel 2.6.38).
|
||||
|
||||
\fB<options>\fR can be [\-\-cipher, \-\-key-size].
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B "\-\-verbose, \-v"
|
||||
|
||||
@@ -60,6 +60,7 @@ static int action_create(int arg);
|
||||
static int action_remove(int arg);
|
||||
static int action_resize(int arg);
|
||||
static int action_status(int arg);
|
||||
static int action_benchmark(int arg);
|
||||
static int action_luksFormat(int arg);
|
||||
static int action_luksOpen(int arg);
|
||||
static int action_luksAddKey(int arg);
|
||||
@@ -89,6 +90,7 @@ static struct action_type {
|
||||
{ "remove", action_remove, 0, 1, 1, N_("<name>"), N_("remove device") },
|
||||
{ "resize", action_resize, 0, 1, 1, N_("<name>"), N_("resize active device") },
|
||||
{ "status", action_status, 0, 1, 0, N_("<name>"), N_("show device status") },
|
||||
{ "benchmark", action_benchmark, 0, 0, 0, N_("<name>"), N_("benchmark cipher") },
|
||||
{ "repair", action_luksRepair, 0, 1, 1, N_("<device>"), N_("try to repair on-disk metadata") },
|
||||
{ "luksFormat", action_luksFormat, 0, 1, 1, N_("<device> [<new key file>]"), N_("formats a LUKS device") },
|
||||
{ "luksOpen", action_luksOpen, 0, 2, 1, N_("<device> <name> "), N_("open LUKS device as mapping <name>") },
|
||||
@@ -346,6 +348,82 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int action_benchmark(int arg __attribute__((unused)))
|
||||
{
|
||||
static struct {
|
||||
char *cipher;
|
||||
char *mode;
|
||||
size_t key_size;
|
||||
size_t iv_size;
|
||||
} bciphers[] = {
|
||||
{ "aes", "cbc", 16, 16 },
|
||||
{ "serpent", "cbc", 16, 16 },
|
||||
{ "twofish", "cbc", 16, 16 },
|
||||
{ "aes", "cbc", 32, 16 },
|
||||
{ "serpent", "cbc", 32, 16 },
|
||||
{ "twofish", "cbc", 32, 16 },
|
||||
{ "aes", "xts", 32, 16 },
|
||||
{ "serpent", "xts", 32, 16 },
|
||||
{ "twofish", "xts", 32, 16 },
|
||||
{ "aes", "xts", 64, 16 },
|
||||
{ "serpent", "xts", 64, 16 },
|
||||
{ "twofish", "xts", 64, 16 },
|
||||
{ NULL, NULL, 0, 0 }
|
||||
};
|
||||
char *header = "# Tests are approximate using memory only (no storage IO).\n"
|
||||
"# Algorithm | Key | Encryption | Decryption\n";
|
||||
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 iv_size = 16;
|
||||
int buffer_size = 1024 * 1024;
|
||||
char *c;
|
||||
int i, r;
|
||||
|
||||
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"));
|
||||
return r;
|
||||
}
|
||||
if ((c = strchr(cipher_mode, '-')))
|
||||
*c = '\0';
|
||||
|
||||
/* FIXME: not really clever :) */
|
||||
if (strstr(cipher, "des"))
|
||||
iv_size = 8;
|
||||
|
||||
r = crypt_benchmark(NULL, cipher, cipher_mode,
|
||||
key_size / 8, iv_size, buffer_size,
|
||||
&enc_mbr, &dec_mbr);
|
||||
if (!r) {
|
||||
log_std("%s", header);
|
||||
strncat(cipher, "-", MAX_CIPHER_LEN);
|
||||
strncat(cipher, cipher_mode, MAX_CIPHER_LEN);
|
||||
log_std("%11s %4db %5.1f MiB/s %5.1f MiB/s\n",
|
||||
cipher, key_size, enc_mbr, dec_mbr);
|
||||
} else
|
||||
log_err(_("Cannot benchmark %s.\n"), cipher);
|
||||
} else {
|
||||
log_std("%s", header);
|
||||
for (i = 0; bciphers[i].cipher; i++) {
|
||||
r = crypt_benchmark(NULL, bciphers[i].cipher, bciphers[i].mode,
|
||||
bciphers[i].key_size, bciphers[i].iv_size,
|
||||
buffer_size, &enc_mbr, &dec_mbr);
|
||||
snprintf(cipher, MAX_CIPHER_LEN, "%s-%s",
|
||||
bciphers[i].cipher, bciphers[i].mode);
|
||||
if (!r)
|
||||
log_std("%11s %4db %5.1f MiB/s %5.1f MiB/s\n",
|
||||
cipher, bciphers[i].key_size*8, enc_mbr, dec_mbr);
|
||||
else
|
||||
log_std("%11s %4db %12s %12s\n", cipher,
|
||||
bciphers[i].key_size*8, _("N/A"), _("N/A"));
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _read_mk(const char *file, char **key, int keysize)
|
||||
{
|
||||
int fd;
|
||||
@@ -1221,9 +1299,10 @@ int main(int argc, const char **argv)
|
||||
if (opt_key_size &&
|
||||
strcmp(aname, "luksFormat") &&
|
||||
strcmp(aname, "create") &&
|
||||
strcmp(aname, "loopaesOpen"))
|
||||
strcmp(aname, "loopaesOpen") &&
|
||||
strcmp(aname, "benchmark"))
|
||||
usage(popt_context, EXIT_FAILURE,
|
||||
_("Option --key-size is allowed only for luksFormat, create and loopaesOpen.\n"
|
||||
_("Option --key-size is allowed only for luksFormat, create, loopaesOpen and benchmark.\n"
|
||||
"To limit read from keyfile use --keyfile-size=(bytes)."),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user