Commit dfe77be7 authored by Milan Broz's avatar Milan Broz

* Implement --use-random and --use-urandom for luksFormat to allow setting of...

* 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.

git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@354 36d66b0a-2a48-0410-832c-cd162a569da5
parent c3f38654
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-)
......@@ -145,6 +145,12 @@ AC_SUBST([LIBGCRYPT_STATIC_LIBS])
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])
......
......@@ -44,6 +44,7 @@ libcryptsetup_la_SOURCES = \
backends.c \
libdevmapper.c \
volumekey.c \
random.c \
gcrypt.c
include_HEADERS = libcryptsetup.h
......
......@@ -14,6 +14,10 @@ int init_crypto(struct crypt_device *ctx)
{
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;
......
......@@ -27,6 +27,8 @@
#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;
......@@ -49,11 +51,11 @@ struct volume_key {
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);
......@@ -122,4 +124,10 @@ void get_topology_alignment(const char *device,
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 */
......@@ -109,6 +109,27 @@ void crypt_set_password_retry(struct crypt_device *cd, int tries);
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
*
......
......@@ -37,6 +37,9 @@ CRYPTSETUP_1.0 {
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;
......
/*
* 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();
}
......@@ -18,6 +18,7 @@ struct crypt_device {
uint64_t iteration_time;
int tries;
int password_verify;
int rng_type;
/* used in CRYPT_LUKS1 */
struct luks_phdr hdr;
......@@ -981,6 +982,7 @@ int crypt_init(struct crypt_device **cd, const char *device)
h->iteration_time = 1000;
h->password_verify = 0;
h->tries = 3;
h->rng_type = crypt_random_default_key_rng();
*cd = h;
return 0;
}
......@@ -1077,7 +1079,7 @@ static int _crypt_format_luks1(struct crypt_device *cd,
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;
......@@ -1911,6 +1913,24 @@ void crypt_set_password_verify(struct crypt_device *cd, int password_verify)
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);
......
......@@ -11,7 +11,6 @@
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/mman.h>
#include <sys/resource.h>
......
......@@ -23,8 +23,6 @@
#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);
......@@ -48,7 +46,7 @@ void crypt_free_volume_key(struct volume_key *vk)
}
}
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;
......@@ -57,11 +55,10 @@ struct volume_key *crypt_generate_volume_key(unsigned keylength)
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;
}
......@@ -9,9 +9,7 @@ libluks_la_SOURCES = \
pbkdf.c \
keymanage.c \
keyencryption.c \
random.c \
pbkdf.h \
random.h \
af.h \
luks.h
......
......@@ -26,7 +26,7 @@
#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)
{
......@@ -99,7 +99,7 @@ int AF_split(char *src, char *dst, size_t blocksize, unsigned int blocknumbers,
/* 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);
......
......@@ -33,7 +33,6 @@
#include "luks.h"
#include "af.h"
#include "pbkdf.h"
#include "random.h"
#include <uuid/uuid.h>
#include <../lib/internal.h>
......@@ -429,7 +428,7 @@ int LUKS_generate_phdr(struct luks_phdr *header,
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;
......@@ -522,7 +521,8 @@ int LUKS_set_key(const char *device, unsigned int keyIndex,
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
......@@ -723,9 +723,9 @@ static int wipe(const char *device, unsigned int from, unsigned int to)
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) {
......
/*
* 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;
}
}
#ifndef INCLUDED_CRYPTSETUP_LUKS_RANDOM_H
#define INCLUDED_CRYPTSETUP_LUKS_RANDOM_H
#include <stddef.h>
int getRandom(char *buf, size_t len);
#endif
......@@ -43,7 +43,7 @@ These are valid LUKS actions:
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>
......@@ -179,6 +179,13 @@ Use pre-generated master key stored in file. For \fIluksFormat\fR it allows LUKS
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
......@@ -254,6 +261,20 @@ The available combinations of ciphers, modes, hashes and key sizes depend on ker
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
......@@ -9,5 +10,4 @@ luks/af.c
luks/keyencryption.c
luks/keymanage.c
luks/pbkdf.c
luks/random.c
src/cryptsetup.c
......@@ -37,6 +37,8 @@ static int opt_timeout = 0;
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;
......@@ -377,6 +379,11 @@ static int action_luksFormat(int arg)
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)
......@@ -677,9 +684,10 @@ static void help(poptContext popt_context, enum poptCallbackReason reason,
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);
......@@ -749,6 +757,8 @@ int main(int argc, char **argv)
{ "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;
......@@ -822,6 +832,13 @@ int main(int argc, char **argv)
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 */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment