changeset 0:73af139d1a94

Initial revision
author Guido Berhoerster <guido+sencrypt@berhoerster.name>
date Tue, 28 Jan 2014 19:23:16 +0100
parents
children f0ceb0ad20e7
files Makefile README deps.sed err.c err.h sencrypt.c
diffstat 6 files changed, 1134 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Tue Jan 28 19:23:16 2014 +0100
@@ -0,0 +1,92 @@
+#
+# Copyright (C) 2014 Guido Berhoerster <guido+sencrypt@berhoerster.name>
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+PACKAGE =	sencrypt
+VERSION =	1
+DISTNAME :=	$(PACKAGE)-$(VERSION)
+DECRYPT_ALIAS =	sdecrypt
+
+COMPILE.c =	$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) $(TARGET_ARCH) -c
+# gcc, clang, icc
+MAKEDEPEND.c =	$(CC) -MM $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS)
+# Sun/Solaris Studio
+#MAKEDEPEND.c =	$(CC) -xM1 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS)
+# X makedepend
+#MAKEDEPEND.c =	makedepend -f- -Y -- $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) --
+LINK.c =	$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) $(LDFLAGS) $(XLDFLAGS) $(TARGET_ARCH)
+LINK.o =	$(CC) $(LDFLAGS) $(XLDFLAGS) $(TARGET_ARCH)
+INSTALL :=	install
+INSTALL.exec :=	$(INSTALL) -D -m 0755
+INSTALL.data :=	$(INSTALL) -D -m 0644
+PAX :=		pax
+GZIP :=		gzip
+SED :=		sed
+
+DESTDIR ?=
+prefix ?=	/usr/local
+bindir ?=	$(prefix)/bin
+datadir ?=	$(prefix)/share
+
+HAVE_ERR_H ?=	1
+
+OBJS =		sencrypt.o
+
+ifeq ($(HAVE_ERR_H),0)
+  OBJS +=	err.o
+endif
+
+.DEFAULT_TARGET = all
+
+.PHONY: all clean clobber dist install
+
+all: $(PACKAGE)
+
+$(PACKAGE): XCPPFLAGS :=	-DOPENSSL_LOAD_CONF
+ifeq ($(HAVE_ERR_H),1)
+    $(PACKAGE): XCPPFLAGS +=	-DHAVE_ERR_H
+endif
+$(PACKAGE): XCFLAGS :=	$(shell getconf LFS_CFLAGS)
+$(PACKAGE): LDLIBS :=	-lcrypto
+$(PACKAGE): XLDFLAGS :=	$(shell getconf LFS_LDFLAGS)
+$(PACKAGE): $(OBJS)
+	$(LINK.o) $^ $(LDLIBS) -o $@
+
+%.o: %.c
+	$(MAKEDEPEND.c) $< | $(SED) -f deps.sed >$*.d
+	$(COMPILE.c) -o $@ $<
+
+install:
+	$(INSTALL.exec) $(PACKAGE) "$(DESTDIR)$(bindir)/$(PACKAGE)"
+	ln -f $(PACKAGE) "$(DESTDIR)$(bindir)/$(DECRYPT_ALIAS)"
+
+clean:
+	rm -f $(PACKAGE) $(OBJS)
+
+clobber: clean
+	rm -f $(patsubst %.o,%.d,$(OBJS))
+
+dist: clobber
+	$(PAX) -w -x ustar -s ',.*/\..*,,' -s ',./[^/]*\.tar\.gz,,' \
+	    -s ',\./,$(DISTNAME)/,' . | $(GZIP) > $(DISTNAME).tar.gz
+
+-include $(patsubst %.o,%.d,$(OBJS))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README	Tue Jan 28 19:23:16 2014 +0100
@@ -0,0 +1,48 @@
+sencrypt
+========
+
+Description
+-----------
+
+sencrypt and sdecrypt are utilities for encrypting and decrypting data with the
+AES, DES, 3DES, and RC4 algorithms. It can read keys from files or ask for a
+passphrase and use that together with a salt to derive a key using the PBKDF2
+key derivation function.
+
+sencrypt and sdecrypt are portable and compatible reimplementations of the
+encrypt and decrypt utilities in Solaris/Illumos-based operating systems.
+
+Build Instructions
+------------------
+
+sencrypt requires a POSIX:2004 compatible operating system, and needs GNU make,
+GNU or BSD install, and the OpenSSL library to be installed. It has been tested
+on Linux distributions, FreeBSD, Solaris and Illumos-derived distributions,
+UnixWare, and OpenServer.
+
+License
+-------
+
+Except otherwise noted, all files are Copyright (C) 2014 Guido Berhoerster and
+distributed under the following license terms:
+
+Copyright (C) 2014 Guido Berhoerster <guido+sencrypt@berhoerster.name>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deps.sed	Tue Jan 28 19:23:16 2014 +0100
@@ -0,0 +1,26 @@
+/^[^:]\{1,\}:.*\\$/{
+    h
+    s/\([^:]\{1,\}:\).*/\1/
+    x
+    s/[^:]\{1,\}://
+}
+/\\$/,/^$/bgen
+/\\$/,/[^\\]$/{
+:gen
+    s/[[:blank:]]*\\$//
+    s/^[[:blank:]]*//
+    G
+    s/\(.*\)\n\(.*\)/\2 \1/
+}
+/^[^:]\{1,\}:[[:blank:]]*$/d
+/^[^:]\{1,\}\.o:/{
+    s/[[:blank:]]*[^[:blank:]]\{1,\}\.[cC][[:blank:]]*/ /g
+    s/[[:blank:]]*[^[:blank:]]\{1,\}\.[cC]$//g
+    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cc[[:blank:]]*/ /g
+    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cc$//g
+    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cpp[[:blank:]]*/ /g
+    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cpp$//g
+    /^[^:]\{1,\}:[[:blank:]]*$/d
+    s/^\([^:]\{1,\}\)\.o[[:blank:]]*:[[:blank:]]*\(.*\)/\1.d: $(wildcard \2)\
+&/
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/err.c	Tue Jan 28 19:23:16 2014 +0100
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 Guido Berhoerster <guido+sencrypt@berhoerster.name>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "err.h"
+
+static char __progname[] = "unknown";
+
+void
+err(int eval, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vwarn(fmt, args);
+	va_end(args);
+
+	exit(eval);
+}
+
+void
+errx(int eval, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vwarnx(fmt, args);
+	va_end(args);
+
+	exit(eval);
+}
+
+void
+warn(const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vwarn(fmt, args);
+	va_end(args);
+}
+
+void
+warnx(const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vwarnx(fmt, args);
+	va_end(args);
+}
+
+void
+verr(int eval, const char *fmt, va_list args)
+{
+	vwarn(fmt, args);
+
+	exit(eval);
+}
+
+void
+verrx(int eval, const char *fmt, va_list args)
+{
+	vwarnx(fmt, args);
+
+	exit(eval);
+}
+
+void
+vwarn(const char *fmt, va_list args)
+{
+	int old_errno = errno;
+
+	fprintf(stderr, "%s: ", __progname);
+	if (fmt != NULL) {
+		vfprintf(stderr, fmt, args);
+		fprintf(stderr, ": ");
+	}
+	fprintf(stderr, "%s\n", strerror(old_errno));
+	errno = old_errno;
+}
+
+void
+vwarnx(const char *fmt, va_list args)
+{
+	fprintf(stderr, "%s: ", __progname);
+	if (fmt != NULL) {
+		vfprintf(stderr, fmt, args);
+	}
+	fputc('\n', stderr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/err.h	Tue Jan 28 19:23:16 2014 +0100
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 Guido Berhoerster <guido+sencrypt@berhoerster.name>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef ERR_H
+#define ERR_H
+
+#include <stdarg.h>
+
+void err(int, const char *, ...);
+void errx(int, const char *, ...);
+void warn(const char *, ...);
+void warnx(const char *, ...);
+void verr(int, const char *, va_list);
+void verrx(int, const char *, va_list);
+void vwarn(const char *, va_list);
+void vwarnx(const char *, va_list);
+
+#endif /* ERR_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sencrypt.c	Tue Jan 28 19:23:16 2014 +0100
@@ -0,0 +1,815 @@
+/*
+ * Copyright (C) 2011 Guido Berhoerster <guido+sencrypt@berhoerster.name>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#define	_XOPEN_SOURCE	600
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <limits.h>
+#include <libgen.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <openssl/conf.h>
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+#ifdef HAVE_ERR_H
+#include <err.h>
+#else
+#include "err.h"
+#endif /* HAVE_ERR_H */
+
+#define	MAX(a, b)	(((a) > (b)) ? (a) : (b))
+
+#define	EXIT_USAGE	2
+
+#define	SENCRYPT_FORMAT_VERSION	1
+#define	PBKDF2_ITERATIONS	1000
+#define	SALT_LEN		16
+#define	BUFFER_SIZE		(16 * 1024)
+#define	MAX_PASSWORD_LEN	256
+
+enum {
+	CMD_SENCRYPT,
+	CMD_SDECRYPT
+};
+
+static void
+openssl_warn(void) {
+	unsigned long	errcode;
+
+	while ((errcode = ERR_get_error()) != 0) {
+		warnx("%s", ERR_error_string(errcode, NULL));
+	}
+}
+
+static size_t
+read_keyfile(const char *filename, unsigned char *key, size_t key_size_max)
+{
+	size_t		keyfile_size = 0;
+	FILE		*fp = NULL;
+	struct		stat statbuf;
+
+	fp = fopen(filename, "r");
+	if (fp == NULL) {
+		warn("could not open key file \"%s\"", filename);
+		goto out;
+	}
+
+	if (fstat(fileno(fp), &statbuf) == -1) {
+		warn("could not stat key file \"%s\"", filename);
+		goto out;
+	}
+
+	if (!S_ISREG(statbuf.st_mode)) {
+		warnx("key file \"%s\" is not a regular file", filename);
+		goto out;
+	}
+
+	if ((uintmax_t)statbuf.st_size > SIZE_MAX) {
+		warnx("key file \"%s\" is too large", filename);
+		goto out;
+	}
+	keyfile_size = (size_t)statbuf.st_size;
+	if ((keyfile_size > key_size_max) ||
+	    (keyfile_size == 0)) {
+		warnx("invalid key size");
+		goto out;
+	}
+
+	if (fread(key, 1, keyfile_size, fp) != keyfile_size) {
+		warnx("could not read key file \"%s\"", filename);
+		goto out;
+	}
+
+out:
+	if (fp != NULL) {
+		fclose(fp);
+	}
+
+	return (keyfile_size);
+}
+
+static int
+find_algorithm(const char *algo_name, const EVP_CIPHER **cipher_ptr,
+    size_t *key_len_ptr)
+{
+	int	retval = 0;
+	const EVP_CIPHER	*cipher = NULL;
+	size_t	key_len = *key_len_ptr;
+
+	if (strcmp(algo_name, "aes") == 0) {
+		switch (key_len) {
+		case 0:
+			key_len = 16;
+		case 16:
+			cipher = EVP_aes_128_cbc();
+			break;
+		case 24:
+			cipher = EVP_aes_192_cbc();
+			break;
+		case 32:
+			cipher = EVP_aes_256_cbc();
+			break;
+		default:
+			warnx("invalid key length %zu", key_len);
+			retval = -1;
+		}
+	} else if (strcmp(algo_name, "arcfour") == 0) {
+		if (key_len == 0) {
+			key_len = 16;
+			cipher = EVP_rc4();
+		} else if (key_len <= EVP_MAX_KEY_LENGTH) {
+			/*
+			 * for RC4 keys are not used verbatim but dervied using
+			 * PBKDF2 with a hardcoded key length of 128 bit
+			 */
+			key_len = 16;
+			cipher = EVP_rc4();
+		} else {
+			warnx("invalid key length %zu", key_len);
+			retval = -1;
+		}
+	} else if (strcmp(algo_name, "des") == 0) {
+		if (key_len == 0) {
+			key_len = 8;
+			cipher = EVP_des_cbc();
+		} else if (key_len == 8) {
+			cipher = EVP_des_cbc();
+		} else {
+			warnx("invalid key length %zu", key_len);
+			retval = -1;
+		}
+	} else if (strcmp(algo_name, "3des") == 0) {
+		if (key_len == 0) {
+			key_len = 24;
+			cipher = EVP_des_ede3_cbc();
+		} else if (key_len == 24) {
+			cipher = EVP_des_ede3_cbc();
+		} else {
+			warnx("invalid key length %zu", key_len);
+			retval = -1;
+		}
+	} else {
+		warnx("unknown algorithm \"%s\"", algo_name);
+		retval = -1;
+	}
+
+	*cipher_ptr = cipher;
+	*key_len_ptr = key_len;
+
+	return (retval);
+}
+
+static int
+read_header(BIO *bio_in, uint32_t *iterations, unsigned char *iv, int iv_len,
+    unsigned char *salt, int salt_len)
+{
+	int		read_len;
+	uint32_t	version;
+	int		retval = 0;
+
+	read_len = BIO_read(bio_in, &version, sizeof (version));
+	if (read_len != sizeof (version)) {
+		warnx("failed to read version from input file");
+		if (read_len < 0) {
+			openssl_warn();
+		}
+		retval = -1;
+		goto out;
+	}
+	version = htonl(version);
+	if (version != SENCRYPT_FORMAT_VERSION) {
+		warnx("unknown format version %d", version);
+		retval = -1;
+		goto out;
+	}
+
+	read_len = BIO_read(bio_in, iterations, sizeof (*iterations));
+	if (read_len != sizeof (*iterations)) {
+		warnx("failed to read iterations from input file");
+		if (read_len < 0) {
+			openssl_warn();
+		}
+		retval = -1;
+		goto out;
+	}
+	*iterations = htonl(*iterations);
+	if ((*iterations == 0) || ((sizeof (int) <= sizeof (uint32_t)) &&
+	    (*iterations > INT_MAX))) {
+		warnx("invalid number of iterations");
+		retval = -1;
+		goto out;
+	}
+
+	if (iv_len > 0) {
+		read_len = BIO_read(bio_in, iv, iv_len);
+		if (read_len != iv_len) {
+			warnx("failed to read IV from input file");
+			if (read_len < 0) {
+				openssl_warn();
+			}
+			retval = -1;
+			goto out;
+		}
+	}
+
+	read_len = BIO_read(bio_in, salt, salt_len);
+	if (read_len != salt_len) {
+		warnx("failed to read salt from input file");
+		if (read_len < 0) {
+			openssl_warn();
+		}
+		retval = -1;
+		goto out;
+	}
+
+out:
+	return (retval);
+}
+
+static int
+sencrypt(const EVP_CIPHER *cipher, BIO *bio_in, BIO *bio_out,
+    const unsigned char *key, size_t key_len, const unsigned char *iv,
+    const unsigned char *salt)
+{
+	int		retval = 0;
+	uint32_t	version;
+	uint32_t	iterations;
+	int		iv_len;
+	int		write_len;
+	int		read_len;
+	BIO		*bio_cipher = NULL;
+	char		*buf = NULL;
+	EVP_CIPHER_CTX	*cipher_ctx;
+
+	/* set up cipher filter */
+	bio_cipher = BIO_new(BIO_f_cipher());
+	BIO_set_cipher(bio_cipher, cipher, NULL, NULL, 1);
+	BIO_get_cipher_ctx(bio_cipher, &cipher_ctx);
+	if (EVP_CIPHER_CTX_set_key_length(cipher_ctx, (int)key_len) != 1) {
+		warnx("failed to set key length");
+		openssl_warn();
+		retval = 1;
+		goto out;
+	}
+	if (EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, iv, 1) != 1) {
+		warnx("failed to initialize cipher");
+		openssl_warn();
+		retval = 1;
+		goto out;
+	}
+	BIO_push(bio_cipher, bio_out);
+
+	/* write header */
+	version = htonl(SENCRYPT_FORMAT_VERSION);
+	write_len = BIO_write(bio_out, &version, sizeof (version));
+	if (write_len != sizeof (version)) {
+		warnx("failed to write version to output file");
+		if (write_len < 0) {
+			openssl_warn();
+		}
+		retval = 1;
+		goto out;
+	}
+
+	iterations = htonl(PBKDF2_ITERATIONS);
+	write_len = BIO_write(bio_out, &iterations, sizeof (iterations));
+	if (write_len != sizeof (iterations)) {
+		warnx("failed to write iterations to output file");
+		if (write_len < 0) {
+			openssl_warn();
+		}
+		retval = 1;
+		goto out;
+	}
+
+	iv_len = EVP_CIPHER_iv_length(cipher);
+	if (iv_len > 0) {
+		write_len = BIO_write(bio_out, iv, iv_len);
+		if (write_len != iv_len) {
+			warnx("failed to write IV to output file");
+			if (write_len < 0) {
+				openssl_warn();
+			}
+			retval = 1;
+			goto out;
+		}
+	}
+
+	write_len = BIO_write(bio_out, salt, SALT_LEN);
+	if (write_len != SALT_LEN) {
+		warnx("failed to write salt to output file");
+		if (write_len < 0) {
+			openssl_warn();
+		}
+		retval = 1;
+		goto out;
+	}
+
+	if (BIO_flush(bio_out) < 1) {
+		warnx("failed to flush output file");
+		openssl_warn();
+		retval = 1;
+		goto out;
+	}
+
+	buf = malloc(BUFFER_SIZE);
+	if (buf == NULL) {
+		warn(NULL);
+		retval = 1;
+		goto out;
+	}
+
+	/* encrypt data */
+	while ((read_len = BIO_read(bio_in, buf, BUFFER_SIZE)) > 0) {
+		if ((write_len = BIO_write(bio_cipher, buf, read_len)) !=
+		    read_len) {
+			warnx("failed to write to output file");
+			if (write_len < 0) {
+				openssl_warn();
+			}
+			retval = 1;
+			goto out;
+		}
+	}
+	if (read_len < 0) {
+		warnx("failed to read from input file");
+		openssl_warn();
+		retval = 1;
+		goto out;
+	}
+
+	if (BIO_flush(bio_cipher) < 1) {
+		warnx("failed to flush output file");
+		openssl_warn();
+		retval = 1;
+		goto out;
+	}
+
+out:
+	free(buf);
+
+	if (bio_cipher != NULL) {
+		BIO_pop(bio_cipher);
+		BIO_free(bio_cipher);
+	}
+
+	return (retval);
+}
+
+static int
+sdecrypt(const EVP_CIPHER *cipher, BIO *bio_in, BIO *bio_out,
+    const unsigned char *key, size_t key_len, const unsigned char *iv)
+{
+	int		read_len;
+	BIO		*bio_cipher = NULL;
+	int		write_len;
+	char		*buf = NULL;
+	EVP_CIPHER_CTX	*cipher_ctx;
+	int		retval = 0;
+
+	buf = malloc(BUFFER_SIZE);
+	if (buf == NULL) {
+		warn(NULL);
+		retval = 1;
+		goto out;
+	}
+
+	/* set up cipher filter */
+	bio_cipher = BIO_new(BIO_f_cipher());
+	BIO_set_cipher(bio_cipher, cipher, NULL, NULL, 0);
+	BIO_get_cipher_ctx(bio_cipher, &cipher_ctx);
+	if (EVP_CIPHER_CTX_set_key_length(cipher_ctx, (int)key_len) != 1) {
+		warnx("failed to set key length");
+		openssl_warn();
+		retval = 1;
+		goto out;
+	}
+	if (EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, iv, 0) != 1) {
+		warnx("failed to initialize cipher");
+		openssl_warn();
+		retval = 1;
+		goto out;
+	}
+	BIO_push(bio_cipher, bio_in);
+
+	/* decrypt data */
+	while ((read_len = BIO_read(bio_cipher, buf, BUFFER_SIZE)) > 0) {
+		if ((write_len = BIO_write(bio_out, buf, read_len)) !=
+		    read_len) {
+			warnx("failed to write to to output file");
+			if (write_len < 0) {
+				openssl_warn();
+			}
+			retval = 1;
+			goto out;
+		}
+	}
+	if (read_len < 0) {
+		warnx("failed to read from input file");
+		openssl_warn();
+		retval = 1;
+		goto out;
+	}
+
+	if (BIO_flush(bio_out) < 1) {
+		warnx("failed to flush output file");
+		openssl_warn();
+		retval = 1;
+		goto out;
+	}
+
+	if (BIO_get_cipher_status(bio_cipher) == 0) {
+		warnx("decryption failed");
+		openssl_warn();
+		retval = 1;
+		goto out;
+	}
+
+out:
+	free(buf);
+
+	if (bio_cipher != NULL) {
+		BIO_pop(bio_cipher);
+		BIO_free(bio_cipher);
+	}
+
+	return (retval);
+}
+
+static void
+list_algorithms(void)
+{
+	printf("Algorithm       Keysize:  Min   Max (bits)\n"
+	    "------------------------------------------\n");
+	printf("%-15s         %5u %5u\n", "aes", 128, 256);
+	printf("%-15s         %5u %5u\n", "arcfour", 8,
+	    EVP_MAX_KEY_LENGTH * 8);
+	printf("%-15s         %5u %5u\n", "des", 64, 64);
+	printf("%-15s         %5u %5u\n", "3des", 192, 192);
+}
+
+static void
+usage(int cmd)
+{
+	if (cmd == CMD_SENCRYPT) {
+		fprintf(stderr, "usage: sencrypt -l | [-v] -a algorithm "
+		    "[-k key_file] [-i input_file] [-o output_file]\n");
+	} else if (cmd == CMD_SDECRYPT) {
+		fprintf(stderr, "usage: sdecrypt -l | [-v] -a algorithm "
+		    "[-k key_file] [-i input_file] [-o output_file]\n");
+	}
+}
+
+int
+main(int argc, char *argv[])
+{
+	char		*progname;
+	int		cmd;
+	int		c;
+	bool		aflag = false;
+	char		*algo_name = NULL;
+	bool		is_algo_rc4 = false;
+	bool		iflag = false;
+	char		*in_filename = NULL;
+	bool		kflag = false;
+	char		*key_filename = NULL;
+	bool		lflag = false;
+	bool		oflag = false;
+	char		*out_filename = NULL;
+	bool		vflag = false;
+	bool		errflag = false;
+	unsigned char	key[EVP_MAX_KEY_LENGTH];
+	size_t		key_len = 0;
+	size_t		key_file_len;
+	const EVP_CIPHER	*cipher;
+	BIO		*bio_in = NULL;
+	uint32_t	iterations = PBKDF2_ITERATIONS;
+	unsigned char	iv[EVP_MAX_IV_LENGTH];
+	unsigned char	salt[SALT_LEN];
+	BIO		*bio_out = NULL;
+	int		need_tmpfile = 0;
+	FILE		*fp_in;
+	struct stat	statbuf_in;
+	struct stat	statbuf_out;
+	int		fd_tmp = -1;
+	FILE		*fp_tmp = NULL;
+	char		*out_filename_tmp = NULL;
+	char		*out_dir = NULL;
+	char		*tmp_filename = NULL;
+	int		len;
+	mode_t		old_mode;
+	char		pwdata[MAX(MAX_PASSWORD_LEN, EVP_MAX_KEY_LENGTH)];
+	size_t		pwdata_len = 0;
+	int		status = EXIT_SUCCESS;
+
+	/* initialize OpenSSL */
+	OpenSSL_add_all_algorithms();
+	ERR_load_crypto_strings();
+	OPENSSL_config(NULL);
+
+	progname = strrchr(argv[0], '/');
+	progname = (progname != NULL) ? progname + 1 : argv[0];
+	if ((strcmp(progname, "sencrypt") == 0) ||
+	    (strcmp(progname, "encrypt") == 0)) {
+		cmd = CMD_SENCRYPT;
+	} else if ((strcmp(progname, "sdecrypt") == 0) ||
+	    (strcmp(progname, "decrypt") == 0)) {
+		cmd = CMD_SDECRYPT;
+	} else {
+		fprintf(stderr, "invalid command name");
+		status = EXIT_FAILURE;
+		goto out;
+	}
+
+	while (!errflag && (c = getopt(argc, argv, "a:i:k:lo:v")) != -1) {
+		switch (c) {
+		case 'a':
+			aflag = true;
+			algo_name = optarg;
+			is_algo_rc4 = (strcmp(algo_name, "arcfour") == 0);
+			break;
+		case 'i':
+			iflag = true;
+			in_filename = optarg;
+			break;
+		case 'k':
+			kflag = true;
+			key_filename = optarg;
+			break;
+		case 'l':
+			lflag = true;
+			break;
+		case 'o':
+			oflag = true;
+			out_filename = optarg;
+			break;
+		case 'v':
+			vflag = true;
+			break;
+		default:
+			errflag = true;
+		}
+	}
+	if (errflag || (!lflag && !aflag) || (lflag && aflag) ||
+	    (argc > optind)) {
+		usage(cmd);
+		status = EXIT_USAGE;
+		goto out;
+	}
+
+	if (lflag) {
+		list_algorithms();
+		goto out;
+	}
+
+	if (kflag) {
+		key_file_len = read_keyfile(key_filename, key,
+		    (off_t)sizeof (key));
+		if (key_file_len < 1) {
+			status = EXIT_FAILURE;
+			goto out;
+		}
+		key_len = key_file_len;
+	} else {
+		if (EVP_read_pw_string(pwdata, sizeof (pwdata), "Enter key:",
+		    (cmd == CMD_SENCRYPT) ? 1 : 0) != 0) {
+			warnx("could not read passphrase");
+			openssl_warn();
+			status = EXIT_FAILURE;
+			goto out;
+		}
+		pwdata_len = strlen(pwdata);
+		if (pwdata_len < 1) {
+			warnx("invalid passphrase");
+			status = EXIT_FAILURE;
+			goto out;
+		}
+	}
+
+	/* the cipher is determined based on name and length of the key file */
+	if (find_algorithm(algo_name, &cipher, &key_len) == -1) {
+		status = EXIT_FAILURE;
+		goto out;
+	}
+
+	if (iflag) {
+		bio_in = BIO_new_file(in_filename, "r");
+	} else {
+		bio_in = BIO_new_fp(stdin, BIO_NOCLOSE);
+	}
+	if (bio_in == NULL) {
+		warnx("could not open input file");
+		openssl_warn();
+		status = EXIT_FAILURE;
+		goto out;
+	}
+
+	if (cmd == CMD_SENCRYPT) {
+		/* generate random salt and IV */
+		if ((RAND_bytes(salt, sizeof (salt)) == 0) ||
+		    (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) == 0)) {
+			/* not enough entropy or unknown error */
+			warnx("failed to generate random data");
+			status = EXIT_FAILURE;
+			goto out;
+		}
+	} else {
+		read_header(bio_in, &iterations, iv,
+		    EVP_CIPHER_iv_length(cipher), salt, (int)sizeof (salt));
+	}
+
+	/*
+	 * if no keyfile was given or the RC4 cipher is used, derive the key
+	 * from the password and salt
+	 */
+	if (kflag && is_algo_rc4) {
+		memcpy(pwdata, key, key_file_len);
+		pwdata_len = key_file_len;
+	}
+	if (!kflag || is_algo_rc4) {
+		if (PKCS5_PBKDF2_HMAC_SHA1(pwdata, (int)pwdata_len, salt,
+		    sizeof (salt), (int)iterations, (int)key_len, key) == 0) {
+			warnx("failed to generate key");
+			status = EXIT_FAILURE;
+			goto out;
+		}
+	}
+
+	if (oflag) {
+		/*
+		 * if input and output files are identical, create and write the
+		 * output to a temporary file for the output which is then
+		 * renamed to out_filename
+		 */
+		if (iflag) {
+			BIO_get_fp(bio_in, &fp_in);
+			if (fstat(fileno(fp_in), &statbuf_in) == -1) {
+				warn("could not stat input file");
+				status = EXIT_FAILURE;
+				goto out;
+			}
+			if (stat(out_filename, &statbuf_out) == -1) {
+				if (errno != ENOENT) {
+					warn("could not stat output file");
+					status = EXIT_FAILURE;
+					goto out;
+				}
+			} else if ((statbuf_in.st_ino == statbuf_out.st_ino) &&
+				    (statbuf_in.st_dev == statbuf_out.st_dev)) {
+				need_tmpfile = 1;
+			}
+		}
+
+		if (need_tmpfile) {
+			out_filename_tmp = strdup(out_filename);
+			if (out_filename_tmp == NULL) {
+				warn(NULL);
+				status = EXIT_FAILURE;
+				goto out;
+			}
+			out_dir = dirname(out_filename_tmp);
+			len = snprintf(NULL, 0, "%s/sencryptXXXXXX", out_dir);
+			if (len < 0) {
+				warn(NULL);
+				status = EXIT_FAILURE;
+				goto out;
+			}
+			tmp_filename = malloc((size_t)len + 1);
+			if (tmp_filename == NULL) {
+				warn(NULL);
+				status = EXIT_FAILURE;
+				goto out;
+			}
+			if (snprintf(tmp_filename, (size_t)len + 1,
+			    "%s/sencryptXXXXXX", out_dir) != len) {
+				warn(NULL);
+				status = EXIT_FAILURE;
+				goto out;
+			}
+			old_mode = umask(077);
+			fd_tmp = mkstemp(tmp_filename);
+			umask(old_mode);
+			if (fd_tmp == -1) {
+				warn("could not create temporary file");
+				status = EXIT_FAILURE;
+				goto out;
+			}
+			fp_tmp = fdopen(fd_tmp, "w");
+			if (fp_tmp == NULL) {
+				warn("could not open temporary file");
+				status = EXIT_FAILURE;
+				goto out;
+			}
+			fd_tmp = -1;
+			bio_out = BIO_new_fp(fp_tmp, BIO_CLOSE);
+			if (bio_out == NULL) {
+				warnx("could not open temporary file");
+				openssl_warn();
+				status = EXIT_FAILURE;
+				goto out;
+			}
+			fp_tmp = NULL;
+		} else {
+			old_mode = umask(077);
+			bio_out = BIO_new_file(out_filename, "w");
+			umask(old_mode);
+			if (bio_out == NULL) {
+				warnx("could not open output file");
+				openssl_warn();
+				status = EXIT_FAILURE;
+				goto out;
+			}
+		}
+	} else {
+		bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
+		if (bio_out == NULL) {
+			warnx("could not open output file");
+			openssl_warn();
+			status = EXIT_FAILURE;
+			goto out;
+		}
+	}
+
+	if (cmd == CMD_SENCRYPT) {
+		if (sencrypt(cipher, bio_in, bio_out, key, key_len,
+		    iv, salt) == -1) {
+			status = EXIT_FAILURE;
+		}
+	} else {
+		if (sdecrypt(cipher, bio_in, bio_out, key, key_len,
+		    iv) == -1) {
+			status = EXIT_FAILURE;
+		}
+	}
+
+out:
+	OPENSSL_cleanse(pwdata, pwdata_len);
+	OPENSSL_cleanse(key, key_len);
+
+	if (fd_tmp != -1) {
+		close(fd_tmp);
+	}
+
+	if (fp_tmp != NULL) {
+		fclose(fp_tmp);
+	}
+
+	if (bio_in != NULL) {
+		BIO_free_all(bio_in);
+	}
+
+	if (bio_out != NULL) {
+		BIO_free_all(bio_out);
+
+		if (status == 0) {
+			if (need_tmpfile) {
+				if (rename(tmp_filename, out_filename) == -1) {
+					warn("could not create output file");
+					status = EXIT_FAILURE;
+					unlink(tmp_filename);
+				}
+			}
+		} else {
+			if (need_tmpfile) {
+				unlink(tmp_filename);
+			} else if (oflag) {
+				unlink(out_filename);
+			}
+		}
+	}
+
+	free(out_filename_tmp);
+	free(tmp_filename);
+
+	EVP_cleanup();
+	ERR_free_strings();
+	CONF_modules_free();
+
+	exit(status);
+}