C sesame-setup implementation

W. Michael Petullo mike at flyn.org
Tue Dec 21 21:08:46 PST 2004


I wrote a patch for the sesame code in CVS that adds a C implementation
of sesame-setup.  I used OpenSSL's libraries for the crypto because this
is what I am familiar with.  Cryptsetup is also required.  It seems
to work.

Here are the steps one may take to initialize a device for use with
sesame-setup:

1.  Print out an encrypted random key (you will be prompted for a passphrase):

echo -n `dd if=/dev/urandom bs=1c count=128`SESAME0 | openssl enc -aes-256-ecb | xxd -p -c 256

NOTE: The above command line sometime creates less than 135 key bytes.
One may need to run it a few times to get 128 + SESAME0 = 135 key bytes.
You should be able to check with somethine like this (output should
be 128):

echo -n 53616c7465645f5faa47ebfa21a4fa0bad1c0ca31f39e146901d7cd83534bb2c30b83d3470a1e2f3591bd6854539eb2a0d1bd095d2f73695301c8282e739a9ed3c5ad6d991bfb07a0b9e10a381432ecfcb9c6dd0ef9c3c5aa0b540bddd3ffc1b4395fb3e3d564b5d6b70333e6e58e83771add2cd5b07e1efd6ee14f5ea20bf874a63d32accea8b16f3c589d5772061eb528ee379c27afa3f29dcd081dcc38fc3 | xxd -r -p | openssl enc -d -aes-256-ecb | sed 's/SESAME0$//g' | wc -c

2.  Place this encrypted key in a file named "cryptheader."  Everything
should be identical to the following except for the enc_key field,
which will be the result of step one:

version = '0'
uuid = '0123-4567-89ab-cdef'
block_key_cipher = 'aes'
block_key_sha1 = 'FIXME'
enc_key_cipher = 'aes-256-ecb'
enc_key = '53616c7465645f5fbbb762c0ce401215307903ac9c745462e28e37ab1f5721fd68fd0fc40e506e599b779b4ed668cc5df25d4a871d92a61cf2961e43d0532c520bcb203c323355cd55b6717083b8055a10bd682ac08d8ddeb27cf6af927dbfc814d7f133c346deff5235d8e714158d555bbd62df682001cd3611eaedb5d16a562fbc14d6feffad23573bf3af153f166146f9ec7a7fc1a30f07299260652e6eeb'

3.  Create a dm-crypt device for the device you wish to encrypt, using
the field named enc_key from step two (when prompted, enter the passphrase
from step one):

echo -n <enc_key> | xxd -r -p | openssl enc -d -aes-256-ecb | sed 's/SESAME0$//g' | cryptsetup -s 128 -c aes create foo <device>

4.  Create an encrypted filesystem on the device:

mkfs.ext2 /dev/mapper/foo

5.  Install the header from step two:

cryptsetup remove foo
dd if=/dev/zero of=<device> bs=1c count=512
dd if=cryptheader of=<device> bs=1c count=512

6.  Try sesame-setup:

echo <passphrase from step one> | sesame-setup -v <device>
mount /dev/mapper/sesame_crypto_0123-4567-89ab-cdef /mnt/foo

Here is the patch:

===============================================================================
diff -u --recursive sesame-vanilla/configure.in sesame/configure.in
--- sesame-vanilla/configure.in	2004-12-17 10:56:52.000000000 -0600
+++ sesame/configure.in	2004-12-21 20:40:55.427719336 -0600
@@ -103,6 +103,14 @@
 
 #pkg_modules="hal >= 0.4.0, hal-storage >= 0.4.0, openssl >= 0.9.7a"
 #PKG_CHECK_MODULES(PACKAGE, [$pkg_modules])
+AC_CHECK_LIB(crypto, EVP_DecryptInit_ex)
+AC_CHECK_LIB(ssl, SSL_load_error_strings)
+
+AC_PATH_PROG(CRYPTSETUP, cryptsetup, no)
+if test x"$CRYPTSETUP" = xno; then
+        AC_MSG_ERROR([cryptsetup executable not found in your path])
+fi
+AC_SUBST(CRYPTSETUP)
 
 AS_AC_EXPAND(LOCALSTATEDIR, $localstatedir)
 AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
Only in sesame: depcomp
Only in sesame: INSTALL
Only in sesame: install-sh
diff -u --recursive sesame-vanilla/tools/Makefile.am sesame/tools/Makefile.am
--- sesame-vanilla/tools/Makefile.am	2004-12-17 11:02:42.000000000 -0600
+++ sesame/tools/Makefile.am	2004-12-21 20:41:52.966972040 -0600
@@ -1,9 +1,9 @@
 
 INCLUDES = \
+	-DCRYPTSETUP=\""$(CRYPTSETUP)\"" \
 	-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
 	-DPACKAGE_BIN_DIR=\""$(bindir)"\" \
-	-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
-	@PACKAGE_CFLAGS@
+	-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\"
 
 sbin_PROGRAMS = sesame-setup
 
Only in sesame/tools: Makefile.in
diff -u --recursive sesame-vanilla/tools/sesame-setup.c sesame/tools/sesame-setup.c
--- sesame-vanilla/tools/sesame-setup.c	2004-12-17 10:56:52.000000000 -0600
+++ sesame/tools/sesame-setup.c	2004-12-21 20:58:06.612955352 -0600
@@ -1,46 +1,406 @@
-
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <ctype.h>
+#include <assert.h>
+#include <limits.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+#ifndef EVP_MAX_BLOCK_LENGTH
+#define EVP_MAX_BLOCK_LENGTH 32	/* some older openssl versions need this */
+#endif
 
 #include "../libsesame/libsesame.h"
 
-int 
-main (int argc, char *argv[])
+#define MAGICTAIL "SESAME0"
+
+int verbose = 0;
+char *device = NULL;
+
+static void print_usage(const int exitcode, const char *error,
+			const char *more)
 {
-	char md_file[] = "/dev/sda1";
-	int fd_metadata;
+	if (error)
+		assert(more);
+
+	fprintf(stderr, "sesame-setup [options] device\n\n"
+		"-h, --help\n"
+		"	print a list of options\n\n"
+		"-v, --verbose\n" "	verbose display of messages\n\n");
+	if (error)
+		fprintf(stderr, "%s: %s\n", error, more);
+
+	exit(exitcode);
+}
+
+static void sslerror(const char *msg)
+{
+	assert(msg);
+
+	unsigned long err = ERR_get_error();
+	if (err != 0)
+		fprintf(stderr, "%s: %s", msg,
+			ERR_error_string(err, NULL));
+}
+
+static int hash_authtok(const char *data, const EVP_CIPHER * const cipher,
+			const char *const authtok,
+			unsigned char *const hash, unsigned char *const iv)
+{
+	const EVP_MD *md;
+	unsigned char salt[PKCS5_SALT_LEN];
+	char magic[sizeof "Salted__" - 1];
+
+	assert(data != NULL);
+	assert(cipher != NULL);	/* FIXME: is cipher is valid OpenSSL cipher? */
+	assert(authtok != NULL);
+	assert(hash != NULL);	/* FIXME: check hash is big enough? */
+	assert(iv != NULL);	/* FIXME: check iv is big enough? */
+
+	if (memcmp(data, "Salted__", sizeof "Salted__" - 1) != 0) {
+		fprintf(stderr, "magic string Salted__ not in stream\n");
+		return 0;
+	}
+	memcpy(salt, data + sizeof "Salted__" - 1, PKCS5_SALT_LEN);
+	md = EVP_md5();
+	if (EVP_BytesToKey
+	    (cipher, md, salt, authtok, strlen(authtok), 1,
+	     hash, iv) <= 0) {
+		fprintf(stderr, "failed to hash passphrase");
+		return 0;
+	}
+
+	return 1;
+}
+
+static int
+decrypt(char *const out, size_t * const out_len,
+	const char *const in, const size_t in_len,
+	const char *const cipher_name, const char *const authtok)
+{
+	int ret = 1;
+	int segment_len;
+	size_t data_len;
+	char *data;
+	unsigned char hashed_authtok[EVP_MAX_KEY_LENGTH];
+	unsigned char iv[EVP_MAX_IV_LENGTH];
+	const EVP_CIPHER *cipher;
+	EVP_CIPHER_CTX ctx;
+
+	assert(out != NULL);
+	assert(out_len != NULL);
+	assert(cipher_name != NULL);
+	assert(in != NULL);
+	assert(authtok != NULL);
+
+	memset(out, 0x00, BUFSIZ + EVP_MAX_BLOCK_LENGTH);
+	OpenSSL_add_all_ciphers();
+	EVP_CIPHER_CTX_init(&ctx);
+	SSL_load_error_strings();
+	if (!(cipher = EVP_get_cipherbyname(cipher_name))) {
+		fprintf(stderr, "error getting cipher \"%s\"\n", cipher);
+		ret = 0;
+		goto _return;
+	}
+	if (hash_authtok(in, cipher, authtok, hashed_authtok, iv) == 0) {
+		ret = 0;
+		goto _return;
+	}
+	if (EVP_DecryptInit_ex(&ctx, cipher, NULL, hashed_authtok, iv) ==
+	    0) {
+		sslerror("failed to initialize decryption code");
+		ret = 0;
+		goto _return;
+	}
+	data = in + (sizeof "Salted__" - 1) + PKCS5_SALT_LEN;
+	data_len = in_len - (sizeof "Salted__" - 1) - PKCS5_SALT_LEN;
+	/* assumes plaintexts is always <= ciphertext + EVP_MAX_BLOCK_LEN in length
+	 * OpenSSL's documentation seems to promise this */
+	if (EVP_DecryptUpdate
+	    (&ctx, out, &segment_len, data, data_len) == 0) {
+		sslerror("failed to decrypt key");
+		ret = 0;
+		goto _return;
+	}
+	*out_len = segment_len;
+	if (EVP_DecryptFinal_ex(&ctx, &out[*out_len], &segment_len) == 0) {
+		sslerror
+		    ("bad pad on end of encrypted file (wrong algorithm or key size?)");
+		ret = 0;
+		goto _return;
+	}
+	*out_len += segment_len;
+      _return:
+	if (EVP_CIPHER_CTX_cleanup(&ctx) == 0) {
+		sslerror("error cleaning up cipher context");
+		ret = 0;
+	}
+
+	ERR_free_strings();
+	/* out_len is unsigned */
+	assert(ret == 0 || *out_len <= BUFSIZ + EVP_MAX_BLOCK_LENGTH);
+
+	return ret;
+}
+
+static unsigned char *decode(char *data)
+{
+	size_t i;
+	unsigned char *decoded =
+	    (char *) malloc((strlen(data) / 2) * sizeof(char));
+	if (decoded == NULL)
+		return NULL;
+	for (i = 0; i < strlen(data); i += 2) {
+		decoded[i / 2] =
+		    isdigit(data[i]) ? (data[i] - 48) << 4 : (data[i] -
+							      87) << 4;
+		decoded[i / 2] +=
+		    isdigit(data[i + 1]) ? data[i + 1] - 48 : data[i + 1] -
+		    87;
+	}
+	return decoded;
+}
+
+static char *strip_cr(char *s)
+{
+	int len;
+
+	assert(s);
+
+	len = strlen(s);
+	s[len - 1] = s[len - 1] == '\n' ? 0x00 : s[len - 1];
+
+	return s;
+}
+
+static int read_key(char *buf, int size)
+{
+	int fnval = 1;
+
+	assert(buf);
+	assert(size > 0);
+
+	if (fgets(buf, size, stdin) == NULL) {
+		fnval = 0;
+		goto _return;
+	}
+
+	strip_cr(buf);
+
+      _return:
+	return fnval;
+}
+
+static void msg(const char *format, ...)
+{
+	assert(format != NULL);
+
+	if (verbose) {
+		/* Used to log issues that cause pam_mount to fail. */
+		va_list args;
+
+		va_start(args, format);
+		vfprintf(stdout, format, args);
+		va_end(args);
+	}
+}
+
+static int run_cryptsetup(const char *block_key_cipher, const char *device,
+			  const char *uuid, const char *key,
+			  const int key_len)
+{
+	pid_t child;
+	int fnval = 1, pipefd[2], child_exit;
+	char dmname[PATH_MAX + 1], *key_len_str;
+
+	assert(block_key_cipher != NULL);
+	assert(device != NULL);
+	assert(uuid != NULL);
+	assert(key != NULL);
+	assert(key_len > 0);
+
+	if (asprintf(&key_len_str, "%d", key_len) == -1) {
+		fprintf(stderr, "Failed to allocate memory, err=%s\n",
+			strerror(errno));
+		fnval = 0;
+		goto _return_no_free;
+	}
+
+	strcpy(dmname, "sesame_crypto_");
+	strncat(dmname, uuid, sizeof dmname - strlen(dmname));
+
+	if (pipe(pipefd) == -1) {
+		fprintf(stderr, "Failed to create pipe, err=%s\n",
+			strerror(errno));
+		fnval = 0;
+		goto _return;
+	}
+
+	child = fork();
+
+	if (child < 0) {
+		fprintf(stderr, "Failed to fork, err=%s\n",
+			strerror(errno));
+		fnval = 0;
+		goto _return;
+	} else if (child == 0) {
+		close(0);
+		dup(pipefd[0]);
+		close(pipefd[0]);
+		close(pipefd[1]);
+		execl(CRYPTSETUP, "cryptsetup", "-s", key_len_str, "-c",
+		      block_key_cipher, "create", dmname, device, NULL);
+		fprintf(stderr, "Failed to execute %s, err=%s\n",
+			CRYPTSETUP, strerror(errno));
+		exit(EXIT_FAILURE);
+	} else {
+		close(pipefd[0]);
+		write(pipefd[1], key, key_len);
+		close(pipefd[1]);
+		waitpid(child, &child_exit, 0);
+		fnval = !WEXITSTATUS(child_exit);
+		goto _return;
+	}
+
+      _return:
+	free(key_len_str);
+      _return_no_free:
+	return fnval;
+}
+
+int main(int argc, char *argv[])
+{
+	int c, opt_index = 0;
+	char passphrase[BUFSIZ + 1];
+	char dec_key[BUFSIZ + EVP_MAX_KEY_LENGTH];
+	int dec_key_len, real_dec_key_len;
 	char buf[1024];
+	int fd_metadata;
+	char *uuid, *enc_key, *enc_key_cipher, *block_key_cipher,
+	    *block_key_sha1;
+	struct option opts[] = {
+		{"help", 0, 0, 'h'},
+		{"verbose", 0, 0, 'v'},
+		{0, 0, 0, 0}
+	};
+
+	while ((c = getopt_long(argc, argv, "hv", opts, &opt_index)) >= 0) {
+		switch (c) {
+		case 'h':
+			print_usage(EXIT_SUCCESS, NULL, NULL);
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			print_usage(EXIT_FAILURE, NULL, NULL);
+		}
+	}
+
+	if (argv[optind] == NULL)
+		print_usage(EXIT_FAILURE, NULL, NULL);
+	device = argv[optind];
+
 	SesameMetaData *md;
 
-	fd_metadata = open (md_file, O_RDONLY);
+	msg("opening %s\n", device);
+	fd_metadata = open(device, O_RDONLY);
 	if (fd_metadata == -1) {
-		printf ("Cannot open %s, err=%s\n", md_file, strerror (errno));
+		fprintf(stderr, "Cannot open %s, err=%s\n", device,
+			strerror(errno));
 		goto error;
 	}
-	if (read (fd_metadata, buf, sizeof (buf)) == -1) {
-		printf ("Cannot read from %s, err=%s\n", 
-			md_file, strerror (errno));
+
+	msg("reading from %s\n", device);
+	if (read(fd_metadata, buf, sizeof(buf)) == -1) {
+		fprintf(stderr, "Cannot read from %s, err=%s\n",
+			device, strerror(errno));
 		goto error1;
 	}
 
-	md = sesame_get_metadata_from_buf (buf);
+	msg("parsing metadata\n");
+	md = sesame_get_metadata_from_buf(buf);
 	if (md == NULL) {
-		printf ("Cannot not parse metadata\n");
+		fprintf(stderr, "Cannot not parse metadata\n");
+		goto error1;
+	}
+
+	msg("getting uuid\n");
+	uuid = sesame_get(md, "uuid");
+	if (uuid == NULL) {
+		fprintf(stderr, "Cannot read uuid from %s\n", device);
+		goto error1;
+	}
+
+	msg("getting enc_key\n");
+	enc_key = sesame_get(md, "enc_key");
+	if (enc_key == NULL) {
+		fprintf(stderr, "Cannot read enc_key from %s\n", device);
 		goto error1;
 	}
 
-	printf ("uuid    = %s\n", sesame_get (md, "uuid"));
-	printf ("enc_key = %s\n", sesame_get (md, "enc_key"));
+	msg("getting enc_key_cipher\n");
+	enc_key_cipher = sesame_get(md, "enc_key_cipher");
+	if (enc_key == NULL) {
+		fprintf(stderr, "Cannot read enc_key_cipher from %s\n",
+			device);
+		goto error1;
+	}
+
+	msg("getting block_key_cipher\n");
+	block_key_cipher = sesame_get(md, "block_key_cipher");
+	if (block_key_cipher == NULL) {
+		fprintf(stderr, "Cannot read block_key_cipher from %s\n",
+			device);
+		goto error1;
+	}
+
+	msg("getting block_key_sha1\n");
+	block_key_sha1 = sesame_get(md, "block_key_sha1");
+	if (block_key_sha1 == NULL) {
+		fprintf(stderr, "Cannot read block_key_sha1 from %s\n",
+			device);
+		goto error1;
+	}
+
+	if (read_key(passphrase, BUFSIZ) == 0) {
+		fprintf(stderr, "Could not read key\n");
+		goto error1;
+	}
+
+	msg("decrypting key using passphrase\n");
+	if (decrypt
+	    (dec_key, &dec_key_len, decode(enc_key), strlen(enc_key) / 2,
+	     enc_key_cipher, passphrase) == 0) {
+		fprintf(stderr, "Cannot decrypt key\n");
+		goto error1;
+	}
+
+	msg("checking for MAGICTAIL tail in key\n");
+	real_dec_key_len = dec_key_len - strlen(MAGICTAIL);
+	if (memcmp
+	    (dec_key + real_dec_key_len, MAGICTAIL, strlen(MAGICTAIL))) {
+		fprintf(stderr, "Key does not end in %s\n", MAGICTAIL);
+		goto error1;
+	}
+
+	msg("executing cryptsetup\n");
+	if (run_cryptsetup(block_key_cipher, device, uuid, dec_key,
+			   real_dec_key_len) == 0) {
+		goto error1;
+	}
 
-	sesame_free (md);
+	msg("freeing memory\n");
+	sesame_free(md);
 
-error1:
-	close (fd_metadata);
-error:
+      error1:
+	close(fd_metadata);
+      error:
 	return 0;
 }
===============================================================================

-- 
Mike

:wq
_______________________________________________
hal mailing list
hal at lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/hal



More information about the Hal mailing list