nls.h \
libcryptsetup.h \
utils.c \
+ utils_benchmark.c \
utils_crypt.c \
utils_crypt.h \
utils_loop.c \
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)
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
*
* @param cd crypt device handle
crypt_volume_key_verify;
crypt_status;
crypt_dump;
+ crypt_benchmark;
crypt_get_cipher;
crypt_get_cipher_mode;
crypt_get_uuid;
return cd->device;
}
-static int init_crypto(struct crypt_device *ctx)
+int init_crypto(struct crypt_device *ctx)
{
int r;
--- /dev/null
+/*
+ * 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;
+}
.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"
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);
{ "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>") },
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;
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));