[Pkg-cryptsetup-devel] Bug#387159: cryptsetup: compulsory hashing in luks format

Dennis Furey dennis at basis.uklinux.net
Tue Sep 12 16:27:57 UTC 2006


Package: cryptsetup
Version: 2:1.0.4~rc2-1
Severity: wishlist


My key is manually generated in binary by a random physical process
and memorized as a hexadecimal number, so I have nothing to gain but
maybe something to lose by hashing it. I have patched cryptsetup to
pay attention to the plain hash option when using LUKS format, and to
refrain from hashing the password in that case, provided that the
password is given as a hexadecimal number of the right length.

I appreciate that using passwords that are secure but difficult to
remember could cause trouble for some people. Other than that, there
should be no breakage for anyone because there was previously no
reason to use the plain hash option for LUKS format. Backward
compatibility can be retained by continuing not to use it. I hope you
like my patch. Feel free to send it upstream.

*** 00list
# no patches
01_terminal_timeout
02_docs_tries
03_fix_build_error
04_luks_plain_hash

*** 04_luks_plain_hash.dpatch
#! /bin/sh /usr/share/dpatch/dpatch-run
## 04_luks_plain_hash.dpatch by  <dennis at basis.uklinux.net>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: No description.

@DPATCH@
diff -urNad cryptsetup-1.0.4~rc2~/debian/initramfs-cryptroot-script cryptsetup-1.0.4~rc2/debian/initramfs-cryptroot-script
--- cryptsetup-1.0.4~rc2~/debian/initramfs-cryptroot-script	2006-09-09 22:20:56.000000000 +0100
+++ cryptsetup-1.0.4~rc2/debian/initramfs-cryptroot-script	2006-09-09 22:25:07.000000000 +0100
@@ -166,7 +166,7 @@
 
 # prepare commands
 if /sbin/cryptsetup isLuks $cryptsource > /dev/null 2>&1; then
-	cryptcreate="/sbin/cryptsetup luksOpen $cryptsource $crypttarget"
+	cryptcreate="/sbin/cryptsetup -h $crypthash luksOpen $cryptsource $crypttarget"
 else
 	cryptcreate="/sbin/cryptsetup -c $cryptcipher -s $cryptsize -h $crypthash create $crypttarget $cryptsource"
 fi
diff -urNad cryptsetup-1.0.4~rc2~/lib/libcryptsetup.h cryptsetup-1.0.4~rc2/lib/libcryptsetup.h
--- cryptsetup-1.0.4~rc2~/lib/libcryptsetup.h	2006-09-09 22:20:55.000000000 +0100
+++ cryptsetup-1.0.4~rc2/lib/libcryptsetup.h	2006-09-09 22:23:30.000000000 +0100
@@ -7,6 +7,7 @@
 #define CRYPT_FLAG_READONLY	        (1 << 1)
 #define	CRYPT_FLAG_VERIFY_IF_POSSIBLE	(1 << 2)
 #define	CRYPT_FLAG_VERIFY_ON_DELKEY	(1 << 3)
+#define	CRYPT_FLAG_ALLOW_PLAIN_HASH	(1 << 4)
 
 struct crypt_options {
 	const char	*name;
diff -urNad cryptsetup-1.0.4~rc2~/lib/setup.c cryptsetup-1.0.4~rc2/lib/setup.c
--- cryptsetup-1.0.4~rc2~/lib/setup.c	2006-09-09 22:23:30.000000000 +0100
+++ cryptsetup-1.0.4~rc2/lib/setup.c	2006-09-09 22:23:30.000000000 +0100
@@ -548,6 +548,7 @@
 	char cipherMode[LUKS_CIPHERMODE_L];
 	int passwordLen;
 	int PBKDF2perSecond;
+	int allow_plain_hash = options->flags & CRYPT_FLAG_ALLOW_PLAIN_HASH;
 	
 	mk.keyLength = options->key_size;
 
@@ -592,7 +593,7 @@
 	if(!password) {
 		r = -EINVAL; goto out;
 	}
-	r = LUKS_set_key(options->device, 0, password, passwordLen, &header, &mk, backend);
+	r = LUKS_set_key(options->device, 0, password, passwordLen, &header, &mk, backend, allow_plain_hash);
 	if(r < 0) goto out; 
 
 	r = 0;
@@ -613,6 +614,7 @@
 	};
 	char *dmCipherSpec;
 	int r, tries = options->tries;
+	int allow_plain_hash = options->flags & CRYPT_FLAG_ALLOW_PLAIN_HASH;
 	
 	r = backend->status(0, &tmp, NULL);
 	if (r >= 0) {
@@ -638,7 +640,7 @@
 	if(!password) {
 		r = -EINVAL; goto out;
 	}
-	if(LUKS_open_any_key(options->device, password, passwordLen, &hdr, &mk, backend) < 0) {
+	if(LUKS_open_any_key(options->device, password, passwordLen, &hdr, &mk, backend, allow_plain_hash) < 0) {
 		set_error("No key available with this passphrase.\n");
 		r = -EPERM; goto out1;
 	}
@@ -694,6 +696,7 @@
 		.flags = options->flags,
 	};
 	int r;
+	int allow_plain_hash = options->flags & CRYPT_FLAG_ALLOW_PLAIN_HASH;
 	
 	r = LUKS_read_phdr(device, &hdr);
 	if(r < 0) return r;
@@ -713,7 +716,7 @@
 	if(!password) {
 		r = -EINVAL; goto out;
 	}
-	if(LUKS_open_any_key(device, password, passwordLen, &hdr, &mk, backend) < 0) {
+	if(LUKS_open_any_key(device, password, passwordLen, &hdr, &mk, backend, allow_plain_hash) < 0) {
 		printf("No key available with this passphrase.\n");
 		r = -EPERM; goto out;
 	}
@@ -727,7 +730,7 @@
 
 	hdr.keyblock[keyIndex].passwordIterations = LUKS_benchmarkt_iterations() * ((float)options->iteration_time / 1000);
 
-    	r = LUKS_set_key(device, keyIndex, password, passwordLen, &hdr, &mk, backend);
+    	r = LUKS_set_key(device, keyIndex, password, passwordLen, &hdr, &mk, backend, allow_plain_hash);
 	if(r < 0) goto out;
 
 	r = 0;
@@ -746,6 +749,7 @@
 	int keyIndex = options->key_slot;
 	int openedIndex;
 	int r;
+	int allow_plain_hash = options->flags & CRYPT_FLAG_ALLOW_PLAIN_HASH;
 	
 	if(options->flags & CRYPT_FLAG_VERIFY_ON_DELKEY) {
 		options->flags &= ~CRYPT_FLAG_VERIFY_ON_DELKEY;
@@ -754,7 +758,7 @@
 		if(!password) {
 			r = -EINVAL; goto out;
 		}
-		openedIndex = LUKS_open_any_key(device, password, passwordLen, &hdr, &mk, backend);
+		openedIndex = LUKS_open_any_key(device, password, passwordLen, &hdr, &mk, backend, allow_plain_hash);
 		if(openedIndex < 0 || keyIndex == openedIndex) {
 			printf("No remaining key available with this passphrase.\n");
 			r = -EPERM; goto out;
diff -urNad cryptsetup-1.0.4~rc2~/luks/keymanage.c cryptsetup-1.0.4~rc2/luks/keymanage.c
--- cryptsetup-1.0.4~rc2~/luks/keymanage.c	2006-09-09 22:20:56.000000000 +0100
+++ cryptsetup-1.0.4~rc2/luks/keymanage.c	2006-09-09 22:23:30.000000000 +0100
@@ -43,6 +43,9 @@
 	(__a - 1) / __b + 1;        \
 })
 
+
+#define DISALLOW_PLAIN_HASH 0
+
 int LUKS_generate_masterkey(struct luks_masterkey *mk)
 {
 	return getRandom(mk->key,mk->keyLength);
@@ -178,7 +181,8 @@
 	PBKDF2_HMAC_SHA1(mk->key,mk->keyLength,
 			 header->mkDigestSalt,LUKS_SALTSIZE,
 			 header->mkDigestIterations,
-			 header->mkDigest,LUKS_DIGESTSIZE);
+			 header->mkDigest,LUKS_DIGESTSIZE,
+			 DISALLOW_PLAIN_HASH);
 
 	currentSector = round_up_modulo(LUKS_PHDR_SIZE, alignSectors);
 	for(i = 0; i < LUKS_NUMKEYS; ++i) {
@@ -199,7 +203,7 @@
 int LUKS_set_key(const char *device, unsigned int keyIndex, 
 		 const char *password, size_t passwordLen, 
 		 struct luks_phdr *hdr, struct luks_masterkey *mk,
-		 struct setup_backend *backend)
+		 struct setup_backend *backend, int allow_plain_hash)
 {
 	char derivedKey[hdr->keyBytes];
 	char *AfKey;
@@ -223,7 +227,7 @@
 	PBKDF2_HMAC_SHA1(password,passwordLen,
 			 hdr->keyblock[keyIndex].passwordSalt,LUKS_SALTSIZE,
 			 hdr->keyblock[keyIndex].passwordIterations,
-			 derivedKey, hdr->keyBytes);
+			 derivedKey, hdr->keyBytes, allow_plain_hash);
 	/*
 	 * AF splitting, the masterkey stored in mk->key is splitted to AfMK
 	 */
@@ -265,7 +269,8 @@
 		  size_t passwordLen, 
 		  struct luks_phdr *hdr, 
 		  struct luks_masterkey *mk,
-		  struct setup_backend *backend)
+		  struct setup_backend *backend,
+		  int allow_plain_hash)
 {
 	char derivedKey[hdr->keyBytes];
 	char *AfKey;
@@ -289,7 +294,7 @@
 	PBKDF2_HMAC_SHA1(password,passwordLen,
 			 hdr->keyblock[keyIndex].passwordSalt,LUKS_SALTSIZE,
 			 hdr->keyblock[keyIndex].passwordIterations,
-			 derivedKey, hdr->keyBytes);
+			 derivedKey, hdr->keyBytes, allow_plain_hash);
 
 	r = LUKS_decrypt_from_storage(AfKey,
 				      AFEKSize,
@@ -310,7 +315,7 @@
 	PBKDF2_HMAC_SHA1(mk->key,mk->keyLength,
 			 hdr->mkDigestSalt,LUKS_SALTSIZE,
 			 hdr->mkDigestIterations,
-			 checkHashBuf,LUKS_DIGESTSIZE);
+			 checkHashBuf,LUKS_DIGESTSIZE,DISALLOW_PLAIN_HASH);
 
 	r = (memcmp(checkHashBuf,hdr->mkDigest, LUKS_DIGESTSIZE) == 0)?0:-EPERM;
 out:
@@ -323,7 +328,8 @@
 		      size_t passwordLen,
 		      struct luks_phdr *hdr, 
 		      struct luks_masterkey *mk,
-		      struct setup_backend *backend)
+		      struct setup_backend *backend,
+		      int allow_plain_hash)
 {
 	unsigned int i;
 	int r;
@@ -334,7 +340,7 @@
 
 	mk->keyLength = hdr->keyBytes;
 	for(i=0; i<LUKS_NUMKEYS; i++) {
-		if(LUKS_open_key(device, i, password, passwordLen, hdr, mk, backend) == 0)
+      		if(LUKS_open_key(device, i, password, passwordLen, hdr, mk, backend, allow_plain_hash) == 0)
 			break;
 	}
 	if(i!=LUKS_NUMKEYS) printf("key slot %d unlocked.\n",i);
diff -urNad cryptsetup-1.0.4~rc2~/luks/luks.h cryptsetup-1.0.4~rc2/luks/luks.h
--- cryptsetup-1.0.4~rc2~/luks/luks.h	2006-09-09 22:20:56.000000000 +0100
+++ cryptsetup-1.0.4~rc2/luks/luks.h	2006-09-09 22:23:30.000000000 +0100
@@ -95,8 +95,9 @@
 					const char *password, 
 					size_t passwordLen, 
 					struct luks_phdr *hdr, 
-					struct luks_masterkey *mk,
-					struct setup_backend *backend);
+					struct luks_masterkey *mk,		
+					struct setup_backend *backend,
+					int allow_plain_hash);
 
 int LUKS_open_key(const char *device, 
 					unsigned int keyIndex, 
@@ -104,14 +105,16 @@
 					size_t passwordLen, 
 					struct luks_phdr *hdr, 
 					struct luks_masterkey *mk,
-					struct setup_backend *backend);
+					struct setup_backend *backend,
+					int allow_plain_hash);
 
 int LUKS_open_any_key(const char *device, 
 					const char *password, 
 					size_t passwordLen, 
 					struct luks_phdr *hdr, 
 					struct luks_masterkey *mk,
-					struct setup_backend *backend);
+					struct setup_backend *backend,
+					int allow_plain_hash);
 
 int LUKS_del_key(const char *device, unsigned int keyIndex);
 int LUKS_is_last_keyslot(const char *device, unsigned int keyIndex);
diff -urNad cryptsetup-1.0.4~rc2~/luks/pbkdf.c cryptsetup-1.0.4~rc2/luks/pbkdf.c
--- cryptsetup-1.0.4~rc2~/luks/pbkdf.c	2006-09-09 22:20:56.000000000 +0100
+++ cryptsetup-1.0.4~rc2/luks/pbkdf.c	2006-09-09 22:23:30.000000000 +0100
@@ -20,16 +20,65 @@
 #include <errno.h>
 #include <signal.h>
 #include <sys/time.h>
+#include <ctype.h>
+#include <stdio.h>
 
 #include "hmac_sha1.h"
 #include "XORblock.h"
 
+
+
+static int is_hex_format(const char *password, size_t passwordLen, size_t dKeyLen)
+
+/* returns a non-zero value iff the password contains only the
+   characters 0..9 and a..f, and its length in characters is twice the
+   key length in bytes */
+{
+        int i,h;
+
+        i = 0;
+        h = (passwordLen == (dKeyLen << 1));
+        while (h ? (i++ < passwordLen) : 0) {
+                h = (isdigit(*password) ? 1 : (isxdigit(*password) ? islower(*password) : 0));
+                password++;
+        }
+        return h;
+}
+
+
+
+
+
+static void directly_transcribe(const char *password, size_t passwordLen, char *dKey)
+
+/* generates the derived key from the password assumed to be in the
+   above specified hex format by transcribing it to binary with no
+   hashing */
+
+{
+  int i;
+
+#define nybble(n)                    (isdigit(n) ? (n - '0') : (10 + n - 'a'))
+
+        for (i = 0; i < (passwordLen >> 1); i++) {
+                dKey[i] = (nybble(password[i << 1]) << 4) + nybble(password[(i << 1) + 1]);
+        }
+#ifdef LUKS_DEBUG
+        fprintf(stderr, "hashing bypassed for hex format key of %d bits\n", passwordLen << 2);
+#endif
+}
+
+
+
+
+
+
 static unsigned int *__PBKDF2_global_j;
 static unsigned int __PBKDF2_performance=0;
 
 void PBKDF2_HMAC_SHA1(const char *password, size_t passwordLen, 
 		      const char *salt, size_t saltLen, unsigned int iterations, 
-		      char *dKey, size_t dKeyLen)
+		      char *dKey, size_t dKeyLen, int allow_plain_hash)
 {
 	uint32_t i=1;
 	unsigned int j;
@@ -39,6 +88,17 @@
 	char F_buf[SHA1_DIGEST_SIZE];
 	hmac_ctx templateCtx;
 
+
+
+        /* Don't hash passwords that are already given in hexadecimal format */
+        if (allow_plain_hash ? is_hex_format(password, passwordLen, dKeyLen) : 0) {
+                directly_transcribe (password, passwordLen, dKey);
+                return;
+        }
+
+
+
+
 	/* We need a global pointer for signal handlers */
 	__PBKDF2_global_j = &j;
 
@@ -106,7 +166,7 @@
 
 	PBKDF2_HMAC_SHA1("foo", 3,
 			 "bar", 3, ~(0U),
-			 &buf, 1);
+			 &buf, 1, 0);
 	
 	return __PBKDF2_performance;
 }
diff -urNad cryptsetup-1.0.4~rc2~/luks/pbkdf.h cryptsetup-1.0.4~rc2/luks/pbkdf.h
--- cryptsetup-1.0.4~rc2~/luks/pbkdf.h	2006-09-09 22:20:56.000000000 +0100
+++ cryptsetup-1.0.4~rc2/luks/pbkdf.h	2006-09-09 22:23:30.000000000 +0100
@@ -7,7 +7,7 @@
 
 void PBKDF2_HMAC_SHA1(const char *password, size_t passwordLen, 
 		      const char *salt, size_t saltLen, unsigned int iterations, 
-		      char *dKey, size_t dKeyLen);
+		      char *dKey, size_t dKeyLen, int allow_plain_hash);
 
 unsigned int PBKDF2_performance_check();
 
diff -urNad cryptsetup-1.0.4~rc2~/man/cryptsetup.8 cryptsetup-1.0.4~rc2/man/cryptsetup.8
--- cryptsetup-1.0.4~rc2~/man/cryptsetup.8	2006-09-09 22:23:30.000000000 +0100
+++ cryptsetup-1.0.4~rc2/man/cryptsetup.8	2006-09-09 22:23:30.000000000 +0100
@@ -1,4 +1,4 @@
-.TH CRYPTSETUP "8" "March 2005" "cryptsetup 1.0.3" "Maintainance Commands"
+.TH CRYPTSETUP "9" "September 2006" "cryptsetup 1.0.4" "Maintainance Commands"
 .SH NAME
 cryptsetup - setup cryptographic volumes for dm-crypt (including LUKS extension)
 .SH SYNOPSIS
@@ -79,7 +79,7 @@
 .SH OPTIONS
 .TP
 .B "\-\-hash, \-h"
-specifies hash to use for password hashing. This option is only relevant for the "create" action. The hash string is passed to libgcrypt, so all hashes accepted by gcrypt are supported. Default is "ripemd160".
+specifies hash to use for password hashing. This option is only relevant for the actions of "create", "luksFormat", "luksOpen", "luksDelKey", and "luksAddKey". The hash string is passed to libgcrypt, so all hashes accepted by gcrypt are supported, but for the luks actions, any hash other than "plain" is ignored. Default is "ripemd160".
 .TP
 .B "\-\-cipher, \-c"
 set cipher specification string. Usually, this is "aes-cbc-plain". For pre-2.6.10 kernels, use "aes-plain" as they don't understand the new cipher spec strings. To use ESSIV, use "aes-cbc-essiv:sha256".
@@ -131,8 +131,10 @@
 \fIFrom a key file\fR: It will be cropped to the size given by \-s. If there is insufficient key material in the key file, cryptsetup will quit with an error.
 .SH NOTES ON PASSWORD PROCESSING FOR LUKS
 Password processing is totally different for LUKS. LUKS uses PBKDF2 to protect against dictionary attacks (see RFC 2898). 
-LUKS will always use SHA1 in HMAC mode, and no other mode is supported at the moment. 
-Hence, \-h is ignored.
+LUKS will always use SHA1 in HMAC mode, unless a "plain" hash is specified. For plain hashing, the password must be a
+hexadecimal number containing only the digits 0 through 9 and the lower case letters a through f, and the length of the
+password must be 1/4th of the keysize in bits (e.g., 64 letters or digits for a 256 bit key). A password of any other form
+will be hashed regardless. Plain hashing with luks is currently specific to the Debian package of cryptsetup.
 
 LUKS will always do an exhaustive password reading. Hence, password can not be read from /dev/random, /dev/zero or any other stream, that does not terminate.
 
@@ -146,6 +148,7 @@
 cryptsetup is written by Christophe Saout <christophe at saout.de>
 .br
 LUKS extensions, and man page by Clemens Fruhwirth <clemens at endorphin.org>
+with patches by Dennis Furey <dennis at basis.uklinux.net>
 .SH "REPORTING BUGS"
 Report bugs to <dm-crypt at saout.de>.
 .SH COPYRIGHT
diff -urNad cryptsetup-1.0.4~rc2~/src/cryptsetup.c cryptsetup-1.0.4~rc2/src/cryptsetup.c
--- cryptsetup-1.0.4~rc2~/src/cryptsetup.c	2006-09-09 22:20:55.000000000 +0100
+++ cryptsetup-1.0.4~rc2/src/cryptsetup.c	2006-09-09 22:23:30.000000000 +0100
@@ -223,6 +223,8 @@
 
 	int r = 0; char *msg = NULL;
 
+	if(opt_hash && strcmp(opt_hash, "plain") == 0)
+		options.flags |= CRYPT_FLAG_ALLOW_PLAIN_HASH;
 	if(asprintf(&msg, _("This will overwrite data on %s irrevocably."), options.device) == -1) {
 		fputs(_("memory allocation error in action_luksFormat"), stderr);
 	} else {
@@ -246,6 +248,8 @@
 
 	opt_verbose = 1;
 	options.flags = 0;
+	if(opt_hash && strcmp(opt_hash, "plain") == 0)
+		options.flags |= CRYPT_FLAG_ALLOW_PLAIN_HASH;
 	if (opt_readonly)
 		options.flags |= CRYPT_FLAG_READONLY;
 	r = crypt_luksOpen(&options);
@@ -265,6 +269,8 @@
 	int r; 
 
 	opt_verbose = 1;
+	if(opt_hash && strcmp(opt_hash, "plain") == 0)
+		options.flags |= CRYPT_FLAG_ALLOW_PLAIN_HASH;
 	if(LUKS_is_last_keyslot(options.device, options.key_slot) && 
 	   !yesDialog(_("This is the last keyslot. Device will become unusable after purging this key."))) {
 		r = -EINVAL;
@@ -288,6 +294,8 @@
 	int r; 
 
 	opt_verbose = 1;
+	if(opt_hash && strcmp(opt_hash, "plain") == 0)
+		options.flags |= CRYPT_FLAG_ALLOW_PLAIN_HASH;
 	r = crypt_luksAddKey(&options);
 	show_status(-r);
 	return r;


-- System Information:
Debian Release: testing/unstable
  APT prefers unstable
  APT policy: (500, 'unstable')
Architecture: i386 (i686)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.17-2-686
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)

Versions of packages cryptsetup depends on:
ii  dmsetup                      2:1.02.08-1 The Linux Kernel Device Mapper use
ii  libc6                        2.3.6.ds1-4 GNU C Library: Shared libraries
ii  libdevmapper1.02             2:1.02.08-1 The Linux Kernel Device Mapper use
ii  libgcrypt11                  1.2.3-2     LGPL Crypto library - runtime libr
ii  libgpg-error0                1.2-1       library for common error values an
ii  libpopt0                     1.10-3      lib for parsing cmdline parameters
ii  libuuid1                     1.39-1      universally unique id library

cryptsetup recommends no packages.

-- no debconf information




More information about the Pkg-cryptsetup-devel mailing list