2010-10-27 Milan Broz <mbroz@redhat.com>
* Rewrite cryptsetup luksFormat, luksOpen, luksAddKey to use new API
to allow adding new features.
+ * Implement --use-random and --use-urandom for luksFormat to allow
+ setting of RNG for volume key generator.
+ * Add crypt_set_rng_type() and crypt_get_rng_type() to API.
2010-10-17 Milan Broz <mbroz@redhat.com>
* Add crypt_get_device_name() to API (get underlying device name).
AC_PREREQ([2.67])
-AC_INIT([cryptsetup],[1.1.3])
+AC_INIT([cryptsetup],[1.2.0])
dnl library version from <major>.<minor>.<release>[-<suffix>]
LIBCRYPTSETUP_VERSION=$(echo $PACKAGE_VERSION | cut -f1 -d-)
AC_SUBST([LIBCRYPTSETUP_VERSION])
AC_SUBST([LIBCRYPTSETUP_VERSION_INFO])
+dnl ==========================================================================
+AC_ARG_ENABLE([dev-random], AS_HELP_STRING([--enable-dev-random],
+[use blocking /dev/random by default for key generator (otherwise use /dev/urandom)]),
+[default_rng=/dev/random], [default_rng=/dev/urandom])
+AC_DEFINE_UNQUOTED(DEFAULT_RNG, ["$default_rng"], [default RNG type for key generator])
+
dnl ==========================================================================
AC_DEFUN([CS_DEFINE],
[AC_DEFINE_UNQUOTED(DEFAULT_[]m4_translit([$1], [-a-z], [_A-Z]), [$2], [$3])
backends.c \
libdevmapper.c \
volumekey.c \
+ random.c \
gcrypt.c
include_HEADERS = libcryptsetup.h
{
int r;
+ r = crypt_random_init(ctx);
+ if (r < 0)
+ goto fail;
+
if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) {
if (!gcry_check_version (GCRYPT_REQ_VERSION)) {
r = -ENOSYS;
#define at_least(a, b) ({ __typeof__(a) __at_least = (a); (__at_least >= (b))?__at_least:(b); })
+struct crypt_device;
+
struct hash_type {
char *name;
void *private;
size_t keylength;
char key[];
};
+
struct volume_key *crypt_alloc_volume_key(unsigned keylength, const char *key);
-struct volume_key *crypt_generate_volume_key(unsigned keylength);
-void crypt_free_volume_key(struct volume_key *mk);
+struct volume_key *crypt_generate_volume_key(struct crypt_device *cd, unsigned keylength);
+void crypt_free_volume_key(struct volume_key *vk);
-struct crypt_device;
int crypt_confirm(struct crypt_device *cd, const char *msg);
void set_error_va(const char *fmt, va_list va);
unsigned long *alignment_offset, /* bytes */
unsigned long default_alignment);
+enum { CRYPT_RND_NORMAL = 0, CRYPT_RND_KEY = 1 };
+int crypt_random_init(struct crypt_device *ctx);
+int crypt_random_get(struct crypt_device *ctx, char *buf, size_t len, int quality);
+void crypt_random_exit(void);
+int crypt_random_default_key_rng(void);
+
#endif /* INTERNAL_H */
void crypt_set_iterarion_time(struct crypt_device *cd, uint64_t iteration_time_ms);
void crypt_set_password_verify(struct crypt_device *cd, int password_verify);
+/**
+ * Set which RNG (random number generator) is used for generating long term key
+ * @cd - crypt device handle
+ * @rng_type - kernel random number generator to use
+ *
+ * CRYPT_RNG_URANDOM - use /dev/urandom
+ * CRYPT_RNG_RANDOM - use /dev/random (waits if no entropy in system)
+ */
+#define CRYPT_RNG_URANDOM 0
+#define CRYPT_RNG_RANDOM 1
+void crypt_set_rng_type(struct crypt_device *cd, int rng_type);
+
+/**
+ * Get which RNG (random number generator) is used for generating long term key
+ *
+ * Returns RNG type on success or negative errno value otherwise.
+ *
+ * @cd - crypt device handle
+ */
+int crypt_get_rng_type(struct crypt_device *cd);
+
/**
* Helper to lock/unlock memory to avoid swap sensitive data to disk
*
crypt_get_volume_key_size;
crypt_get_device_name;
+ crypt_set_rng_type;
+ crypt_get_rng_type;
+
crypt_keyslot_status;
crypt_get_error;
crypt_get_dir;
--- /dev/null
+/*
+ * cryptsetup kernel RNG access functions
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "libcryptsetup.h"
+#include "internal.h"
+
+static int random_initialised = 0;
+
+#define URANDOM_DEVICE "/dev/urandom"
+static int urandom_fd = -1;
+
+#define RANDOM_DEVICE "/dev/random"
+static int random_fd = -1;
+
+/* Read random chunk - gathered data usually appears with this granularity */
+#define RANDOM_DEVICE_CHUNK 8
+
+/* Timeout after the warning is printed when there is random data (entropy) */
+#define RANDOM_DEVICE_TIMEOUT 5
+
+/* URANDOM_DEVICE access */
+static int _get_urandom(struct crypt_device *ctx, char *buf, size_t len)
+{
+ int r;
+ size_t old_len = len;
+ char *old_buf = buf;
+
+ assert(urandom_fd != -1);
+
+ while(len) {
+ r = read(urandom_fd, buf, len);
+ if (r == -1 && errno != EINTR)
+ return -EINVAL;
+ len -= r;
+ buf += r;
+ }
+
+ assert(len == 0);
+ assert((size_t)(buf - old_buf) == old_len);
+
+ return 0;
+}
+
+static void _get_random_progress(struct crypt_device *ctx, int warn,
+ size_t expected_len, size_t read_len)
+{
+ if (warn)
+ log_std(ctx,
+ _("System is out of entropy while generating volume key.\n"
+ "Please move mouse or type some text in another window "
+ "to gather some random events.\n"));
+
+ log_std(ctx, _("Generating key (%d%% done).\n"),
+ (int)((expected_len - read_len) * 100 / expected_len));
+}
+
+/* RANDOM_DEVICE access */
+static int _get_random(struct crypt_device *ctx, char *buf, size_t len)
+{
+ int r, warn_once = 1;
+ size_t n, old_len = len;
+ char *old_buf = buf;
+ fd_set fds;
+ struct timeval tv;
+
+ assert(random_fd != -1);
+
+ while (len) {
+ FD_ZERO(&fds);
+ FD_SET(random_fd, &fds);
+
+ tv.tv_sec = RANDOM_DEVICE_TIMEOUT;
+ tv.tv_usec = 0;
+
+ r = select(random_fd + 1, &fds, NULL, NULL, &tv);
+ if(r == -1)
+ return -EINVAL;
+
+ if(!r) {
+ _get_random_progress(ctx, warn_once, old_len, len);
+ warn_once = 0;
+ continue;
+ }
+
+ do {
+ n = RANDOM_DEVICE_CHUNK;
+ if (len < RANDOM_DEVICE_CHUNK)
+ n = len;
+
+ r = read(random_fd, buf, n);
+
+ if (r == -1 && errno == EINTR)
+ continue;
+
+ /* bogus read? */
+ if(r > (int)n)
+ return -EINVAL;
+
+ /* random device is opened with O_NONBLOCK, EAGAIN is expected */
+ if (r == -1 && (errno != EAGAIN || errno != EWOULDBLOCK))
+ return -EINVAL;
+
+ if (r > 0) {
+ len -= r;
+ buf += r;
+ }
+ } while (len && r > 0);
+ }
+
+ assert(len == 0);
+ assert((size_t)(buf - old_buf) == old_len);
+
+ if (!warn_once)
+ _get_random_progress(ctx, 0, old_len, len);
+
+ return 0;
+}
+/* Initialisation of both RNG file descriptors is mandatory */
+int crypt_random_init(struct crypt_device *ctx)
+{
+ /* Used for CRYPT_RND_NORMAL */
+ if(urandom_fd == -1)
+ urandom_fd = open(URANDOM_DEVICE, O_RDONLY);
+ if(urandom_fd == -1)
+ goto fail;
+
+ /* Used for CRYPT_RND_KEY */
+ if(random_fd == -1)
+ random_fd = open(RANDOM_DEVICE, O_RDONLY | O_NONBLOCK);
+ if(random_fd == -1)
+ goto fail;
+
+ random_initialised = 1;
+ return 0;
+fail:
+ crypt_random_exit();
+ log_err(ctx, _("Fatal error during RNG initialisation.\n"));
+ return -ENOSYS;
+}
+
+int crypt_random_get(struct crypt_device *ctx, char *buf, size_t len, int quality)
+{
+ int status, rng_type;
+
+ switch(quality) {
+ case CRYPT_RND_NORMAL:
+ status = _get_urandom(ctx, buf, len);
+ break;
+ case CRYPT_RND_KEY:
+ rng_type = ctx ? crypt_get_rng_type(ctx) :
+ crypt_random_default_key_rng();
+ switch (rng_type) {
+ case CRYPT_RNG_URANDOM:
+ status = _get_urandom(ctx, buf, len);
+ break;
+ case CRYPT_RNG_RANDOM:
+ status = _get_random(ctx, buf, len);
+ break;
+ default:
+ abort();
+ }
+ break;
+ default:
+ log_err(ctx, _("Unknown RNG quality requested.\n"));
+ return -EINVAL;
+ }
+
+ if (status)
+ log_err(ctx, _("Error %d reading from RNG: %s\n"),
+ errno, strerror(errno));
+
+ return status;
+}
+
+void crypt_random_exit()
+{
+ random_initialised = 0;
+
+ if(random_fd != -1) {
+ (void)close(random_fd);
+ random_fd = -1;
+ }
+
+ if(urandom_fd != -1) {
+ (void)close(urandom_fd);
+ urandom_fd = -1;
+ }
+}
+
+int crypt_random_default_key_rng()
+{
+ if (!strcmp(DEFAULT_RNG, RANDOM_DEVICE))
+ return CRYPT_RNG_RANDOM;
+
+ if (!strcmp(DEFAULT_RNG, URANDOM_DEVICE))
+ return CRYPT_RNG_URANDOM;
+
+ /* RNG misconfiguration is fatal */
+ abort();
+}
uint64_t iteration_time;
int tries;
int password_verify;
+ int rng_type;
/* used in CRYPT_LUKS1 */
struct luks_phdr hdr;
h->iteration_time = 1000;
h->password_verify = 0;
h->tries = 3;
+ h->rng_type = crypt_random_default_key_rng();
*cd = h;
return 0;
}
cd->volume_key = crypt_alloc_volume_key(volume_key_size,
volume_key);
else
- cd->volume_key = crypt_generate_volume_key(volume_key_size);
+ cd->volume_key = crypt_generate_volume_key(cd, volume_key_size);
if(!cd->volume_key)
return -ENOMEM;
cd->password_verify = password_verify ? 1 : 0;
}
+void crypt_set_rng_type(struct crypt_device *cd, int rng_type)
+{
+ switch (rng_type) {
+ case CRYPT_RNG_URANDOM:
+ case CRYPT_RNG_RANDOM:
+ log_dbg("RNG set to %d (%s).", rng_type, rng_type ? "random" : "urandom");
+ cd->rng_type = rng_type;
+ }
+}
+
+int crypt_get_rng_type(struct crypt_device *cd)
+{
+ if (!cd)
+ return -EINVAL;
+
+ return cd->rng_type;
+}
+
int crypt_memory_lock(struct crypt_device *cd, int lock)
{
return lock ? crypt_memlock_inc(cd) : crypt_memlock_dec(cd);
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
-#include <termios.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include "internal.h"
-int getRandom(char *buf, size_t len);
-
struct volume_key *crypt_alloc_volume_key(unsigned keylength, const char *key)
{
struct volume_key *vk = malloc(sizeof(*vk) + keylength);
}
}
-struct volume_key *crypt_generate_volume_key(unsigned keylength)
+struct volume_key *crypt_generate_volume_key(struct crypt_device *cd, unsigned keylength)
{
int r;
struct volume_key *vk;
if (!vk)
return NULL;
- r = getRandom(vk->key, keylength);
+ r = crypt_random_get(cd, vk->key, keylength, CRYPT_RND_KEY);
if(r < 0) {
crypt_free_volume_key(vk);
return NULL;
}
return vk;
}
-
pbkdf.c \
keymanage.c \
keyencryption.c \
- random.c \
pbkdf.h \
- random.h \
af.h \
luks.h
#include <netinet/in.h>
#include <errno.h>
#include <gcrypt.h>
-#include "random.h"
+#include <../lib/internal.h>
static void XORblock(char const *src1, char const *src2, char *dst, size_t n)
{
/* process everything except the last block */
for(i=0; i<blocknumbers-1; i++) {
- r = getRandom(dst+(blocksize*i),blocksize);
+ r = crypt_random_get(NULL, dst+(blocksize*i), blocksize, CRYPT_RND_NORMAL);
if(r < 0) goto out;
XORblock(dst+(blocksize*i),bufblock,bufblock,blocksize);
#include "luks.h"
#include "af.h"
#include "pbkdf.h"
-#include "random.h"
#include <uuid/uuid.h>
#include <../lib/internal.h>
header->version, header->hashSpec ,header->cipherName, header->cipherMode,
header->keyBytes);
- r = getRandom(header->mkDigestSalt,LUKS_SALTSIZE);
+ r = crypt_random_get(ctx, header->mkDigestSalt, LUKS_SALTSIZE, CRYPT_RND_NORMAL);
if(r < 0) {
log_err(ctx, _("Cannot create LUKS header: reading random salt failed.\n"));
return r;
log_dbg("Key slot %d use %d password iterations.", keyIndex, hdr->keyblock[keyIndex].passwordIterations);
- r = getRandom(hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE);
+ r = crypt_random_get(ctx, hdr->keyblock[keyIndex].passwordSalt,
+ LUKS_SALTSIZE, CRYPT_RND_NORMAL);
if(r < 0) return r;
// assert((vk->keylength % TWOFISH_BLOCKSIZE) == 0); FIXME
if(!buffer) return -ENOMEM;
for(i = 0; i < 39; ++i) {
- if (i >= 0 && i < 5) getRandom(buffer, bufLen);
+ if (i >= 0 && i < 5) crypt_random_get(NULL, buffer, bufLen, CRYPT_RND_NORMAL);
else if(i >= 5 && i < 32) wipeSpecial(buffer, bufLen, i - 5);
- else if(i >= 32 && i < 38) getRandom(buffer, bufLen);
+ else if(i >= 32 && i < 38) crypt_random_get(NULL, buffer, bufLen, CRYPT_RND_NORMAL);
else if(i >= 38 && i < 39) memset(buffer, 0xFF, bufLen);
if(write_lseek_blockwise(devfd, buffer, bufLen, from * SECTOR_SIZE) < 0) {
+++ /dev/null
-/*
- * Random supply helper
- * Copyright 2004, Clemens Fruhwirth <clemens@endorphin.org>
- *
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-
-static int randomfd = -1;
-
-int openRandom() {
- if(randomfd == -1)
- randomfd = open("/dev/urandom", O_RDONLY);
- return randomfd;
-}
-
-/* This method leaks a file descriptor that can be obtained by calling
- closeRandom */
-int getRandom(char *buf, size_t len)
-{
- if(openRandom() == -1) {
- perror("getRandom:");
- return -EINVAL;
- }
- while(len) {
- int r;
- r = read(randomfd,buf,len);
- if (-1 == r && errno != -EINTR) {
- perror("read: "); return -EINVAL;
- }
- len-= r; buf += r;
- }
- return 0;
-}
-
-void closeRandom() {
- if(randomfd != -1) {
- close(randomfd);
- randomfd = -1;
- }
-}
+++ /dev/null
-#ifndef INCLUDED_CRYPTSETUP_LUKS_RANDOM_H
-#define INCLUDED_CRYPTSETUP_LUKS_RANDOM_H
-
-#include <stddef.h>
-
-int getRandom(char *buf, size_t len);
-
-#endif
initializes a LUKS partition and sets the initial key, either via prompting or via <key file>.
\fB<options>\fR can be [\-\-cipher, \-\-verify-passphrase, \-\-key-size, \-\-key-slot,
-\-\-key-file (takes precedence over optional second argument)].
+\-\-key-file (takes precedence over optional second argument), \-\-use-random | \-\-use-urandom].
.PP
\fIluksOpen\fR <device> <name>
For \fIluksAddKey\fR it allows adding new passphrase with only master key knowledge.
.TP
+.B "\-\-use-random"
+.TP
+.B "\-\-use-urandom"
+For \fIluksFormat\fR it defines which kernel random number generator will be used for long-term key (volume key).
+
+See \fBNOTES ON RNG\fR for more information. Use \fIcryptsetup \-\-help\fR to show default RNG.
+.TP
.B "\-\-key-slot, \-S"
For LUKS operations that add key material, this options allows to you specify which key slot is selected for the new key. This option can be used for \fIluksFormat\fR and \fIluksAddKey\fR.
.TP
For \-\-hash option all algorithms supported by gcrypt library are available.
.SH NOTES ON PASSWORDS
Mathematics can't be bribed. Make sure you keep your passwords safe. There are a few nice tricks for constructing a fallback, when suddenly out of (or after being) blue, your brain refuses to cooperate. These fallbacks are possible with LUKS, as it's only possible with LUKS to have multiple passwords.
+.SH NOTES ON RNG
+Random Number Generator (RNG) used in cryptsetup always uses kernel RNG without
+any modifications or additions to data stream procudes by kernel (like internal
+random pool operations or mixing with the other random sources).
+
+There are two types of randomness cryptsetup/LUKS needs. One type (which always
+uses /dev/urandom) is used for salt, AF splitter and for wiping removed
+keyslot.
+
+Second type is used for volume (master) key. You can switch between
+using /dev/random and /dev/urandom here, see \fP--use-random\fR and \fP--use-urandom\fR
+options. Using /dev/random on system without enough entropy sources
+can cause \fPluksFormat\fR to block until the requested amount of random data is gathered.
+See \fPurandom(4)\fR for more information.
.SH AUTHORS
cryptsetup is written by Christophe Saout <christophe@saout.de>
.br
lib/backends.c
lib/gcrypt.c
lib/libdevmapper.c
+lib/random.c
lib/setup.c
lib/utils.c
lib/utils_crypt.c
luks/keyencryption.c
luks/keymanage.c
luks/pbkdf.c
-luks/random.c
src/cryptsetup.c
static int opt_tries = 3;
static int opt_align_payload = 0;
static int opt_non_exclusive = 0;
+static int opt_random = 0;
+static int opt_urandom = 0;
static const char **action_argv;
static int action_argc;
if (opt_iteration_time)
crypt_set_iterarion_time(cd, opt_iteration_time);
+ if (opt_random)
+ crypt_set_rng_type(cd, CRYPT_RNG_RANDOM);
+ else if (opt_urandom)
+ crypt_set_rng_type(cd, CRYPT_RNG_URANDOM);
+
if (opt_master_key_file) {
r = _read_mk(opt_master_key_file, &key, keysize);
if (r < 0)
log_std(_("\nDefault compiled-in device cipher parameters:\n"
"\tplain: %s, Key: %d bits, Password hashing: %s\n"
- "\tLUKS1: %s, Key: %d bits, LUKS header hashing: %s\n"),
+ "\tLUKS1: %s, Key: %d bits, LUKS header hashing: %s, RNG: %s\n"),
DEFAULT_CIPHER(PLAIN), DEFAULT_PLAIN_KEYBITS, DEFAULT_PLAIN_HASH,
- DEFAULT_CIPHER(LUKS1), DEFAULT_LUKS1_KEYBITS, DEFAULT_LUKS1_HASH);
+ DEFAULT_CIPHER(LUKS1), DEFAULT_LUKS1_KEYBITS, DEFAULT_LUKS1_HASH,
+ DEFAULT_RNG);
exit(0);
} else
usage(popt_context, 0, NULL, NULL);
{ "align-payload", '\0', POPT_ARG_INT, &opt_align_payload, 0, N_("Align payload at <n> sector boundaries - for luksFormat"), N_("SECTORS") },
{ "non-exclusive", '\0', POPT_ARG_NONE, &opt_non_exclusive, 0, N_("(Obsoleted, see man page.)"), NULL },
{ "header-backup-file",'\0', POPT_ARG_STRING, &opt_header_backup_file, 0, N_("File with LUKS header and keyslots backup."), NULL },
+ { "use-random", '\0', POPT_ARG_NONE, &opt_random, 0, N_("Use /dev/random for generating volume key."), NULL },
+ { "use-urandom", '\0', POPT_ARG_NONE, &opt_urandom, 0, N_("Use /dev/urandom for generating volume key."), NULL },
POPT_TABLEEND
};
poptContext popt_context;
usage(popt_context, 1, _("Unknown action."),
poptGetInvocationName(popt_context));
+ if (opt_random && opt_urandom)
+ usage(popt_context, 1, _("Only one of --use-[u]random options is allowed."),
+ poptGetInvocationName(popt_context));
+ if ((opt_random || opt_urandom) && strcmp(aname, "luksFormat"))
+ usage(popt_context, 1, _("Option --use-[u]random is allowed only for luksFormat."),
+ poptGetInvocationName(popt_context));
+
action_argc = 0;
action_argv = poptGetArgs(popt_context);
/* Make return values of poptGetArgs more consistent in case of remaining argc = 0 */