commit 2e6a3df756fbc08e462cd6db64f2979795cc1f83
parent 6bb655df0bb1bb3e4f19a27de88a79bdac1ab5b7
Author: boyska <piuttosto@logorroici.org>
Date:   Sat,  4 Aug 2012 18:34:10 +0200
Add KDF support #82
Include pbkdf2 tools inside tomb
It also supports parameters (itertime).
Diffstat:
14 files changed, 504 insertions(+), 38 deletions(-)
diff --git a/KEY_SPECIFICATIONS.txt b/KEY_SPECIFICATIONS.txt
@@ -0,0 +1,36 @@
+Overview
+=========
+
+
+What's a key?
+It basicly is a gpg simmetrically encrypted, ascii-armored file.
+It's encryption key is a function (see below, on KDF section) of your tomb
+passphrase.
+
+
+Layout
+======
+
+Before coming to the gpg part, there could be some "header" lines specifying
+metatada. They're done like this:
+_FIELD_params_params_and_more_params_
+
+where FIELD should be the description for the header.
+Pay much attention to the fact that there should ONLY be ASCII characters there,
+to avoid encoding issues and whatever. Needs something more? Use base64encode.
+(Of course, you're free to pack params into a single field, base64encoding
+whatever you want).
+And every header field should be in only one line.
+
+KDF
+===
+
+Key Derivation Functions, are functions which will make your key stronger
+spending some CPU time: the basic idea is that you have to compute that function
+just once in a while, but an attacker that wants to bruteforce has to compute it
+for every passphrase he's checking. This will make the bruteforce much more
+expensive.
+
+The header line format is _KDF_$method_$params_$params_... where $method is the
+method we are using (ie: scrypt) and params is something that it needs (ie:
+salt).
diff --git a/Makefile.am b/Makefile.am
@@ -1 +1 @@
-SUBDIRS = src share doc
+SUBDIRS = src src/kdf share doc
diff --git a/configure.ac b/configure.ac
@@ -89,6 +89,7 @@ dnl ---------------------------------------------------------------
 
 PKG_CHECK_MODULES(GTK2, [gtk+-2.0 >= 2.16], :,
   AC_MSG_ERROR([*** Gtk+2 >=2.16 development files not found!]))
+AM_PATH_LIBGCRYPT([1.5.0], :,  AC_MSG_ERROR([gcrypt development files not found]))
 AC_SUBST([GTK2_CFLAGS])
 AC_SUBST([GTK2_LIBS])
 
@@ -130,6 +131,7 @@ dnl alphabetic order on dir/subdir, but Makefile sorts before everything
 AC_CONFIG_FILES([
 Makefile
 src/Makefile
+src/kdf/Makefile
 doc/Makefile
 share/Makefile
 ])
diff --git a/doc/tomb.1 b/doc/tomb.1
@@ -111,7 +111,7 @@ the size of the new \fIfile\fR to be created, in megabytes.
 .IP "-k \fI<keyfile>\fR"
 When opening a  tomb, this option can be used  to specify the location
 of the  key to use. Keys  are created with  the same name of  the tomb
-file adding a '.gpg' suffix,  but can be later renamed and transported
+file adding a '.key' suffix,  but can be later renamed and transported
 on other media. When a key is  not found, the program asks to insert a
 USB storage device and it will look for the key file inside it.
 If \fI<keyfile>\fR is "-" (dash), it will read stdin
@@ -124,6 +124,31 @@ tomb create -s 100 tombname -k /media/usb/tombname
 to put the key on a usb pendrive
 
 .B
+.IP "--kdf \fI<method>\fR"
+This will specify the KDF method to use for the tomb we're creating.
+Please note that no stable release of tomb supports KDF; if you use it,
+your tomb might be unusable with an older version of tomb.
+
+You can specify parameters with --kdf=method:param. That is, for example,
+\fI--kdf=pbkdf2:2.5\fR will use pbkdf2 with an itertime of 2.5 seconds
+
+Supported methods are: pbkdf2, null
+
+.B pbkdf2
+is probably the most used kdf in security applications, so it's a good choice.
+It accepts one parameter, that is the seconds it will take on this computer to
+derive the key. The default is 1.
+
+.B null
+is just the same as not using --kdf at all: it will stick to the "classic"
+behaviour
+
+.B
+.IP "--kdf \fI<method>\fR"
+This will specify the KDF method to use for the tomb we're creating.
+Please note that no stable release of tomb supports KDF; if you use it,
+your tomb might be unusable with an older version of tomb.
+.B
 .IP "-n"
 Skip processing of post-hooks and bind-hooks if found inside the tomb.
 See the \fIHOOKS\fR section in this manual for more information.
diff --git a/src/kdf/.gitignore b/src/kdf/.gitignore
@@ -0,0 +1,4 @@
+tomb-kdf-pbkdf2
+tomb-kdf-pbkdf2-gensalt
+tomb-kdf-pbkdf2-getiter
+tomb-utils-hexencode
diff --git a/src/kdf/Makefile.am b/src/kdf/Makefile.am
@@ -0,0 +1,13 @@
+bin_PROGRAMS = tomb-kdf-pbkdf2 tomb-kdf-pbkdf2-gensalt tomb-kdf-pbkdf2-getiter hexencode
+tomb_kdf_pbkdf2_SOURCES = pbkdf2/pbkdf2.c
+tomb_kdf_pbkdf2_CFLAGS = $(LIBGCRYPT_CFLAGS)
+tomb_kdf_pbkdf2_LDADD = $(LIBGCRYPT_LIBS)
+
+tomb_kdf_pbkdf2_gensalt_SOURCES = pbkdf2/gen_salt.c
+
+tomb_kdf_pbkdf2_getiter_SOURCES = pbkdf2/benchmark.c
+tomb_kdf_pbkdf2_getiter_CFLAGS = $(LIBGCRYPT_CFLAGS)
+tomb_kdf_pbkdf2_getiter_LDADD = $(LIBGCRYPT_LIBS)
+
+hexencode_SOURCES = hexencode.c
+
diff --git a/src/kdf/README b/src/kdf/README
@@ -0,0 +1,20 @@
+PLANS
+------
+
+While this can be useful for general purpose, it specially fits tomb, and it's designed for easy integration and compilation.
+
+Binary name will then be:
+tomb-kdf-${algo}
+tomb-kdf-${algo}-gensalt
+tomb-kdf-${algo}-getiter
+
+hexencode (or similar utils, should they be developed), go with:
+tomb-utils-hexencode
+
+Base64 vs hexencode
+-------------------
+
+While base64 is easier to use (shell command, more compact), pbkdf2 use hex
+in its specifications.
+This could be solved with an option (-x for hex, defaults to base64)
+
diff --git a/src/kdf/hexencode.c b/src/kdf/hexencode.c
@@ -0,0 +1,49 @@
+/*
+ * A simple utility that reads from stdin and output the hexencoding (on a single line) of the input
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+static int decode_mode = 0;
+int main(int argc, char *argv[]) {
+	char c;
+	char buf[3];
+	int read_bytes;
+	int opt;
+	static struct option long_options[] =
+	{
+		{"decode", no_argument, &decode_mode, 1},
+		{"encode", no_argument, &decode_mode, 0},
+		{0,0,0,0}
+	};
+	int option_index = 0;
+
+	while(1) {
+		option_index = 0;
+		opt = getopt_long(argc, argv, "", long_options, &option_index);
+		if(opt == -1)
+			break;
+		switch(opt) {
+			case 0:
+				break;
+			case '?':
+				return 127;
+			default:
+				abort();
+		}
+	}
+	if(decode_mode == 0) {
+		while(( c = (char)getchar() ) != EOF)
+			printf("%02x", c);
+		return 0;
+	} else {
+		while( (read_bytes=fread(buf, sizeof(char), 2, stdin)) != 0) {
+			if(read_bytes == 1) buf[1]='\0';
+			sscanf(buf, "%x", &c);
+			printf("%c", c);
+		}
+		return 0;
+	}
+}
diff --git a/src/kdf/pbkdf2/benchmark.c b/src/kdf/pbkdf2/benchmark.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/time.h>
+
+#include <gcrypt.h>
+
+static long bench(int ic) {
+	char *pass = "mypass";
+	unsigned char *salt = "abcdefghijklmno";
+	int salt_len = strlen(salt);
+	int result_len = 64;
+	unsigned char *result = calloc(result_len, sizeof(char));
+	struct timeval start, end;
+	long microtime;
+
+	gettimeofday(&start, NULL);
+	gcry_kdf_derive( pass, strlen(pass), GCRY_KDF_PBKDF2, GCRY_MD_SHA1, salt, salt_len, ic, result_len, result);
+	gettimeofday(&end, NULL);
+	microtime = 1000000*end.tv_sec+end.tv_usec - (1000000*start.tv_sec+start.tv_usec);
+
+	return (long)microtime;
+}
+int main(int argc, char *argv[])
+{
+	long desired_time = 1000000;
+	long microtime;
+	int ic=100;
+	int tries=0;
+	if(argc >= 2)
+		sscanf(argv[1], "%ld", &desired_time);
+	if (!gcry_check_version ("1.5.0")) {
+		fputs ("libgcrypt version mismatch\n", stderr);
+		exit (2);
+	}
+	/* Allocate a pool of 16k secure memory.  This make the secure memory
+	available and also drops privileges where needed.  */
+	gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+	/* It is now okay to let Libgcrypt complain when there was/is
+	a problem with the secure memory. */
+	gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
+	/* Tell Libgcrypt that initialization has completed. */
+	gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+
+	microtime = bench(ic);
+	while( abs(desired_time-microtime) > (desired_time/10) /*little difference */ 
+			&& tries++ <= 5) {
+		float ratio = (float)desired_time/microtime;
+		if(ratio > 1000) ratio=1000.0;
+		ic*=ratio;
+		if(ic<1) ic=1;
+		microtime = bench(ic);
+	} 
+	printf("%d\n", ic);
+	return 0;
+
+}
diff --git a/src/kdf/pbkdf2/gen_salt.c b/src/kdf/pbkdf2/gen_salt.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void print_hex(unsigned char *buf, int len)
+{
+  int i;
+
+  for(i=0;i<len;i++){
+    printf("%02x", buf[i]);
+  }
+}
+
+int main(int argc, char **argv) {
+	int len=32;
+	int res;
+	unsigned char *buf;
+	FILE *rand;
+	if(argc>=2) {
+		if(sscanf(argv[1], "%d", &len) != 1) {
+			fprintf(stderr, "Error: len must be an integer\n");
+			return 1;
+		}
+	}
+	buf = calloc(len, sizeof(char));
+	memset(buf, 9, len);
+	rand = fopen("/dev/random", "r");
+	res = fread(buf, sizeof(char), len, rand);
+	if( res != len) {
+		fprintf(stderr, "Error reading /dev/random: %d != %d, \n", res, len);
+		fclose(rand);
+		free(buf);
+		return 2;
+	}
+	fclose(rand);
+	print_hex(buf, len);
+	free(buf);
+	return 0;
+}
diff --git a/src/kdf/pbkdf2/pbkdf2.c b/src/kdf/pbkdf2/pbkdf2.c
@@ -0,0 +1,144 @@
+/*
+** SYNOPSIS
+**   echo "passphrase" | pbkdf2 salt_hex count > 48_byte_hex_key_and_iv
+**
+** DESCRIPTION
+**
+** Make the "Password-Based Key Derivation Function v2" function found in
+** the openssl library available to the command line, as it is not available
+** for use from the "openssl" command.  At the time of writing the "openssl"
+** command only encrypts using the older, 'fast' pbkdf1.5 method.
+**
+** The 'salt_hex' is the salt to be used, as a hexadecimal string. Typically
+** this is 8 bytes (64 bit), and is an assigned randomly during encryption.
+**
+** The 'count' is iteration count used to make the calculation of the key
+** from the passphrase longer so as to take 1/2 to 2 seconds to generate.
+** This complexity prevents slows down brute force attacks enormously.
+**
+** The output of the above is a 48 bytes in hexadeximal, which is typically
+** used for 32 byte encryption key KEY and a 16 byte IV as needed by
+** Crypt-AES-256 (or some other encryption method).
+**
+** NOTE: While the "openssl" command can accept a hex encoded 'key' and 'iv'
+** it only does so on the command line, which is insecure.  As such I
+** recommend that the output only be used with API access to the "OpenSSL"
+** cryptography libraries.
+**
+*************
+**
+** Anthony Thyssen   4 November 2009      A.Thyssen@griffith.edu.au
+**
+** Based on a test program "pkcs5.c" found on
+**   http://www.mail-archive.com/openssl-users@openssl.org
+** which uses openssl to perform PBKDF2 (RFC2898) iteritive (slow) password
+** hashing.
+**
+** Build
+**    gcc -o pbkdf2 pbkdf2.c -lcrypto
+**
+*/
+#include <stdio.h>
+#include <string.h>
+
+#include <gcrypt.h>
+
+/* TODO: move print_hex and hex_to_binary to utils.h, with separate compiling */
+void print_hex(unsigned char *buf, int len)
+{
+	int i;
+
+	for(i=0;i<len;i++)
+		printf("%02x", buf[i]);
+	printf("\n");
+}
+
+int hex_to_binary(unsigned char *buf, char *hex)
+{
+	int ret;
+	int count=0;
+	while(*hex) {
+		if( hex[1] ) {
+			ret = sscanf( hex, "%2x", (unsigned int*) buf++ );
+			hex += 2;
+		}
+		else {
+			ret = sscanf( hex++, "%1x", (unsigned int*)buf++ );
+		}
+		count++;
+		if( ret != 1)
+			return -1;
+	}
+	*buf = 0;  // null terminate -- precaution
+	return count;
+}
+
+int main(int argc, char *argv[])
+{
+	char *pass = NULL;
+	unsigned char *salt;
+	int salt_len;                  // salt length in bytes
+	int ic=0;                        // iterative count
+	int result_len;
+	unsigned char *result;       // result (binary - 32+16 chars)
+	int i;
+
+	if ( argc != 4 ) {
+		fprintf(stderr, "usage: %s salt count len <passwd >binary_key_iv\n", argv[0]);
+		exit(10);
+	}
+
+	//TODO: move to base64decode
+	salt=calloc(strlen(argv[1])/2+3, sizeof(char));
+	salt_len=hex_to_binary(salt, argv[1]);
+	if( salt_len <= 0 ) {
+		fprintf(stderr, "Error: %s is not a valid salt (it must be a hexadecimal string)\n", argv[1]);
+		exit(1);
+	}
+
+	if( sscanf(argv[2], "%d", &ic) == 0 || ic<=0) {
+		fprintf(stderr, "Error: count must be a positive integer\n");
+		exit(1);
+	}
+	if( sscanf(argv[3], "%d", &result_len) == 0 || result_len<=0) {
+		fprintf(stderr, "Error: result_len must be a positive integer\n");
+		exit(1);
+	}
+
+	fscanf(stdin, "%ms", &pass);
+	if ( pass[strlen(pass)-1] == '\n' )
+		pass[strlen(pass)-1] = '\0';
+
+	// PBKDF 2
+	result = calloc(result_len, sizeof(unsigned char*));
+	if (!gcry_check_version ("1.5.0")) {
+		fputs ("libgcrypt version mismatch\n", stderr);
+		exit (2);
+	}
+	/* Allocate a pool of 16k secure memory.  This make the secure memory
+	available and also drops privileges where needed.  */
+	gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+	/* It is now okay to let Libgcrypt complain when there was/is
+	a problem with the secure memory. */
+	gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
+	/* Tell Libgcrypt that initialization has completed. */
+	gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+	gcry_kdf_derive( pass, strlen(pass), GCRY_KDF_PBKDF2, GCRY_MD_SHA1, salt, salt_len, ic, result_len, result);
+	print_hex(result, result_len);            // Key + IV   (as hex string)
+
+	//clear and free everything
+	for(i=0; i<result_len;i++)
+		result[i]=0;
+	free(result);
+	for(i=0; i<strlen(pass); i++) //blank
+		pass[i]=0;
+	free(pass);
+	for(i=0; i<strlen(argv[1])/2+3; i++) //blank
+		salt[i]=0;
+	free(salt);
+
+	return(0);
+}
+
+/* vim: set noexpandtab ts=4 sw=4: */
diff --git a/src/kdf/test.sh b/src/kdf/test.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env zsh
+
+error=0
+while read line; do
+	pass=`cut -f1 <<<$line`
+	salt=`cut -f2 <<<$line`
+	iter=`cut -f3 <<<$line`
+	keylen=`cut -f4 <<<$line`
+	expected=`cut -f5 <<<$line`
+	hexsalt=`cut -f6 <<<$line`
+	#TODO: check!
+	derived=`./pbkdf2 $hexsalt $iter $keylen <<<$pass`
+	if [[ $derived != $expected ]]; then
+		echo ./pbkdf2 $hexsalt $iter $keylen "<<<$pass"
+		echo "Expected $expected, got $derived" >&2
+		error=$((error + 1))
+	fi
+done < test.txt
+
+if [[ $error == 1 ]]; then
+	exit $error
+fi
diff --git a/src/kdf/test.txt b/src/kdf/test.txt
Binary files differ.
diff --git a/src/tomb b/src/tomb
@@ -166,6 +166,17 @@ check_bin() {
     # resize suite check bin!
     which e2fsck > /dev/null || die "Cannot find e2fsck. Please install it." 1
     which resize2fs > /dev/null || die "Cannot find resize2fs. Please install it." 1
+    
+    if which tomb-kdf-pbkdf2 &> /dev/null; then
+        KDF_PBKDF2="tomb-kdf-pbkdf2"
+    else
+        local our_pbkdf2
+        our_pbkdf2="$(dirname $(readlink -f $TOMBEXEC))/kdf/tomb-kdf-pbkdf2"
+        if which $our_pbkdf2 &> /dev/null; then
+            KDF_PBKDF2=$our_pbkdf2
+        fi
+    fi
+
 }
 
 # }}}
@@ -732,15 +743,15 @@ create_tomb() {
 
     # here user is prompted for key password
     for c in 1 2 3; do
-	# 3 tries to write two times a matching password
-	tombpass=`exec_as_user ${TOMBEXEC} askpass "Secure key for ${tombname}"`
-	tombpasstmp=$tombpass
-	tombpass=`exec_as_user ${TOMBEXEC} askpass "Secure key for ${tombname} (again)"`
-	if [ "$tombpasstmp" = "$tombpass" ]; then
-	    break;
-	fi
-	unset tombpasstmp
-	unset tombpass
+        # 3 tries to write two times a matching password
+        tombpass=`exec_as_user ${TOMBEXEC} askpass "Secure key for ${tombname}"`
+        tombpasstmp=$tombpass
+        tombpass=`exec_as_user ${TOMBEXEC} askpass "Secure key for ${tombname} (again)"`
+        if [ "$tombpasstmp" = "$tombpass" ]; then
+            break;
+        fi
+        unset tombpasstmp
+        unset tombpass
     done
 
     if [ -z $tombpass ]; then
@@ -751,9 +762,36 @@ create_tomb() {
     fi
 
 
-    gpg \
-	--openpgp --batch --no-options --no-tty --passphrase-fd 0 2>/dev/null \
-	-o "${tombkey}" -c -a ${keytmp}/tomb.tmp <<< ${tombpass}
+    _verbose "KDF method chosen is: '`option_value --kdf`'"
+    kdf_method=$(cut -d: -f1 <<<`option_value --kdf` )
+    case $kdf_method in
+        pbkdf2)
+#one parameter: iter time in seconds
+            seconds=$(cut -d: -f2 -s <<<`option_value --kdf`)
+            if [[ -z $seconds ]]; then
+                seconds=1
+            fi
+            local -i microseconds
+            microseconds=$((seconds*1000000))
+            _verbose "Microseconds: $microseconds"
+            pbkdf2_salt=`${KDF_PBKDF2}-gensalt`
+            pbkdf2_iter=`${KDF_PBKDF2}-getiter $microseconds`
+            tombpass=`${KDF_PBKDF2} $pbkdf2_salt $pbkdf2_iter 64 <<<${tombpass}` #64bytes=512bits is the key length (huge!)
+            header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n"
+            ;;
+        ""|null)
+        
+            header=""
+            ;;
+        *)
+            _warning "KDF method non recognized"
+            return 1
+            header=""
+            ;;
+    esac
+    ( echo -n $header; gpg \
+        --openpgp --batch --no-options --no-tty --passphrase-fd 0 2>/dev/null \
+        -o - -c -a ${keytmp}/tomb.tmp <<< ${tombpass} ) > $tombkey
 
     unset tombpass
     chown ${_uid}:${_gid} ${tombkey}
@@ -924,31 +962,46 @@ mount_tomb() {
 
     _warning "Password is required for key ${keyname}"
     for c in 1 2 3; do
-	if [ $c = 1 ]; then
-	    tombpass=`exec_as_user ${TOMBEXEC} askpass "Open tomb ${keyname}"`
-	else
-	    tombpass=`exec_as_user ${TOMBEXEC} askpass "Open tomb $keyname (retry $c)"`
-	fi
-	(gpg --batch --passphrase-fd 0 --no-tty --no-options \
-            -d "${tombkey}" 2> /dev/null <<< ${tombpass} ) \
-	    | cryptsetup --key-file - luksOpen ${nstloop} ${mapper}
-	unset tombpass
+        if [ $c = 1 ]; then
+            tombpass=`exec_as_user ${TOMBEXEC} askpass "Open tomb ${keyname}"`
+        else
+            tombpass=`exec_as_user ${TOMBEXEC} askpass "Open tomb $keyname (retry $c)"`
+        fi
+#TODO: read the first line: if it looks like a KDF, do KDF
+        firstline=`head -n1 < $tombkey`
+        if [[ $firstline =~ '^_KDF_' ]]; then
+            _verbose "KDF: `cut -d_ -f 3 <<<$firstline`"
+            case `cut -d_ -f 3 <<<$firstline` in
+                pbkdf2sha1)
+                    pbkdf2_param=`cut -d_ -f 4- <<<$firstline | tr '_' ' '`
+                    tombpass=$(${KDF_PBKDF2} ${=pbkdf2_param} 2> /dev/null <<<$tombpass)
+                    ;;
+                *)
+                    _failure "No suitable program for KDF `cut -f 3 <<<$firstline`"
+                    return 1
+                    ;;
+            esac
+        fi
+        (gpg --batch --passphrase-fd 0 --no-tty --no-options \
+                -d "${tombkey}" 2> /dev/null <<< ${tombpass} ) \
+            | cryptsetup --key-file - luksOpen ${nstloop} ${mapper}
+        unset tombpass
 
-	# if key was from stdin delete temp file and dir
-	if [ $tombkeydir ]; then
-	    ${=WIPE} ${tombkey}
-	    rmdir $tombkeydir
-	fi
+        # if key was from stdin delete temp file and dir
+        if [ $tombkeydir ]; then
+            ${=WIPE} ${tombkey}
+            rmdir $tombkeydir
+        fi
 
-	# if key was from stdin delete temp file and dir
-	if [ $tombkeydir ]; then
-	    ${WIPE[@]} ${tombkey}
-	    rmdir $tombkeydir
-	fi
+        # if key was from stdin delete temp file and dir
+        if [ $tombkeydir ]; then
+            ${WIPE[@]} ${tombkey}
+            rmdir $tombkeydir
+        fi
 
-	if [ -r /dev/mapper/${mapper} ]; then
-	    break;  # password was correct
-	fi
+        if [ -r /dev/mapper/${mapper} ]; then
+            break;  # password was correct
+        fi
     done
 
     if ! [ -r /dev/mapper/${mapper} ]; then
@@ -1679,7 +1732,7 @@ main() {
     subcommands_opts[__default]=""
     subcommands_opts[open]="f n -nohook=n k: -key=k  U: -uid=U G: -gid=G o: -mount-options=o -ignore-swap"
     subcommands_opts[mount]=${subcommands_opts[open]}
-    subcommands_opts[create]="f s: -size=s -force k: -key=k U: -uid=U G: -gid=G -ignore-swap"
+    subcommands_opts[create]="f s: -size=s -force k: -key=k U: -uid=U G: -gid=G -ignore-swap -kdf:" 
     subcommands_opts[passwd]="f -ignore-swap"
     subcommands_opts[close]=""
     subcommands_opts[help]=""
@@ -1756,7 +1809,7 @@ main() {
 	    continue #it shouldnt be appended to PARAM
 	elif [[ $arg[1] == '-'  ]]; then
 	    if [[ $ok == 0 ]]; then
-		die "unrecognized option $arg" 127
+		die "unrecognized option $arg for subcommand $subcommand" 127
 	    fi
 	fi
 	PARAM+=$arg