From 1ddca5c2349fbd18a6042d62a3b1ccdcb0f03bc7 Mon Sep 17 00:00:00 2001 From: Maxim Radugin Date: Thu, 5 Sep 2013 22:58:07 +0300 Subject: [PATCH] Initial commit --- Makefile | 25 ++++ crypto_backend.h | 94 +++++++++++++ crypto_cipher_kernel.c | 228 ++++++++++++++++++++++++++++++ crypto_kernel.c | 300 ++++++++++++++++++++++++++++++++++++++++ dmcryptfile.c | 306 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 953 insertions(+) create mode 100644 Makefile create mode 100755 crypto_backend.h create mode 100644 crypto_cipher_kernel.c create mode 100755 crypto_kernel.c create mode 100755 dmcryptfile.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a09359e --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +TARGET = dmcryptfile +LIBS = -lm +CC = gcc +CFLAGS = -g -Wall + +.PHONY: default all clean + +default: $(TARGET) +all: default + +OBJECTS = $(patsubst %.c, %.o, $(wildcard *.c)) +HEADERS = $(wildcard *.h) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c $< -o $@ + +.PRECIOUS: $(TARGET) $(OBJECTS) + +$(TARGET): $(OBJECTS) + $(CC) $(OBJECTS) -Wall $(LIBS) -o $@ + +clean: + -rm -f *.o + -rm -f $(TARGET) + diff --git a/crypto_backend.h b/crypto_backend.h new file mode 100755 index 0000000..c449f2e --- /dev/null +++ b/crypto_backend.h @@ -0,0 +1,94 @@ +/* + * crypto backend implementation + * + * Copyright (C) 2010-2012, Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2012, Milan Broz + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This file 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef _CRYPTO_BACKEND_H +#define _CRYPTO_BACKEND_H + +#define ENABLE_AF_ALG + +#include +#include + +struct crypt_device; +struct crypt_hash; +struct crypt_hmac; +struct crypt_cipher; + +int crypt_backend_init(struct crypt_device *ctx); + +#define CRYPT_BACKEND_KERNEL (1 << 0) /* Crypto uses kernel part, for benchmark */ + +uint32_t crypt_backend_flags(void); +const char *crypt_backend_version(void); + +/* HASH */ +int crypt_hash_size(const char *name); +int crypt_hash_init(struct crypt_hash **ctx, const char *name); +int crypt_hash_write(struct crypt_hash *ctx, const char *buffer, size_t length); +int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length); +int crypt_hash_destroy(struct crypt_hash *ctx); + +/* HMAC */ +int crypt_hmac_size(const char *name); +int crypt_hmac_init(struct crypt_hmac **ctx, const char *name, + const void *buffer, size_t length); +int crypt_hmac_write(struct crypt_hmac *ctx, const char *buffer, size_t length); +int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length); +int crypt_hmac_destroy(struct crypt_hmac *ctx); + +/* RNG (if fips paramater set, must provide FIPS compliance) */ +enum { CRYPT_RND_NORMAL = 0, CRYPT_RND_KEY = 1, CRYPT_RND_SALT = 2 }; +int crypt_backend_rng(char *buffer, size_t length, int quality, int fips); + +/* PBKDF*/ +int crypt_pbkdf_check(const char *kdf, const char *hash, + const char *password, size_t password_size, + const char *salt, size_t salt_size, + uint64_t *iter_secs); +int crypt_pbkdf(const char *kdf, const char *hash, + const char *password, size_t password_length, + const char *salt, size_t salt_length, + char *key, size_t key_length, + unsigned int iterations); + +#if 0 +/* internal PBKDF2 implementation */ +int pkcs5_pbkdf2(const char *hash, + const char *P, size_t Plen, + const char *S, size_t Slen, + unsigned int c, + unsigned int dkLen,char *DK); +#endif + +/* CRC32 */ +uint32_t crypt_crc32(uint32_t seed, const unsigned char *buf, size_t len); + +/* ciphers */ +extern int crypt_cipher_init(struct crypt_cipher **ctx, const char *name, + const char *mode, const void *buffer, size_t length); +extern int crypt_cipher_destroy(struct crypt_cipher *ctx); +extern int crypt_cipher_encrypt(struct crypt_cipher *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length); +extern int crypt_cipher_decrypt(struct crypt_cipher *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length); + +#endif /* _CRYPTO_BACKEND_H */ diff --git a/crypto_cipher_kernel.c b/crypto_cipher_kernel.c new file mode 100644 index 0000000..2dd6595 --- /dev/null +++ b/crypto_cipher_kernel.c @@ -0,0 +1,228 @@ +/* + * Linux kernel userspace API crypto backend implementation (skcipher) + * + * Copyright (C) 2012, Red Hat, Inc. All rights reserved. + * Copyright (C) 2012, Milan Broz + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This file 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "crypto_backend.h" + +#ifdef ENABLE_AF_ALG + +#include + +#ifndef AF_ALG +#define AF_ALG 38 +#endif +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif + +struct crypt_cipher { + int tfmfd; + int opfd; +}; + +/* Shared with hash kernel backend */ +int crypt_kernel_socket_init(struct sockaddr_alg *sa, int *tfmfd, int *opfd); + +int crypt_kernel_socket_init(struct sockaddr_alg *sa, int *tfmfd, int *opfd) +{ + *tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (*tfmfd == -1) + return -ENOTSUP; + + if (bind(*tfmfd, (struct sockaddr *)sa, sizeof(*sa)) == -1) { + close(*tfmfd); + *tfmfd = -1; + return -ENOENT; + } + + *opfd = accept(*tfmfd, NULL, 0); + if (*opfd == -1) { + close(*tfmfd); + *tfmfd = -1; + return -EINVAL; + } + + return 0; +} + +/* + *ciphers + * + * ENOENT - algorithm not available + * ENOTSUP - AF_ALG family not available + * (but cannot check specificaly for skcipher API) + */ +int crypt_cipher_init(struct crypt_cipher **ctx, const char *name, + const char *mode, const void *buffer, size_t length) +{ + struct crypt_cipher *h; + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "skcipher", + }; + int r; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + snprintf((char *)sa.salg_name, sizeof(sa.salg_name), + "%s(%s)", mode, name); + + r = crypt_kernel_socket_init(&sa, &h->tfmfd, &h->opfd); + if (r < 0) { + free(h); + return r; + } + + if (setsockopt(h->tfmfd, SOL_ALG, ALG_SET_KEY, buffer, length) == -1) { + crypt_cipher_destroy(h); + return -EINVAL; + } + + *ctx = h; + return 0; +} + +/* The in/out should be aligned to page boundary */ +static int crypt_cipher_crypt(struct crypt_cipher *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length, + uint32_t direction) +{ + int r = 0; + ssize_t len; + struct af_alg_iv *alg_iv; + struct cmsghdr *header; + uint32_t *type; + struct iovec iov = { + .iov_base = (void*)(uintptr_t)in, + .iov_len = length, + }; + int iv_msg_size = iv ? CMSG_SPACE(sizeof(*alg_iv) + iv_length) : 0; + char buffer[CMSG_SPACE(sizeof(*type)) + iv_msg_size]; + struct msghdr msg = { + .msg_control = buffer, + .msg_controllen = sizeof(buffer), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + if (!in || !out || !length) + return -EINVAL; + + if ((!iv && iv_length) || (iv && !iv_length)) + return -EINVAL; + + memset(buffer, 0, sizeof(buffer)); + + /* Set encrypt/decrypt operation */ + header = CMSG_FIRSTHDR(&msg); + header->cmsg_level = SOL_ALG; + header->cmsg_type = ALG_SET_OP; + header->cmsg_len = CMSG_LEN(sizeof(*type)); + type = (void*)CMSG_DATA(header); + *type = direction; + + /* Set IV */ + if (iv) { + header = CMSG_NXTHDR(&msg, header); + header->cmsg_level = SOL_ALG; + header->cmsg_type = ALG_SET_IV; + header->cmsg_len = iv_msg_size; + alg_iv = (void*)CMSG_DATA(header); + alg_iv->ivlen = iv_length; + memcpy(alg_iv->iv, iv, iv_length); + } + + len = sendmsg(ctx->opfd, &msg, 0); + if (len != (ssize_t)length) { + r = -EIO; + goto bad; + } + + len = read(ctx->opfd, out, length); + if (len != (ssize_t)length) + r = -EIO; +bad: + memset(buffer, 0, sizeof(buffer)); + return r; +} + +int crypt_cipher_encrypt(struct crypt_cipher *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length) +{ + return crypt_cipher_crypt(ctx, in, out, length, + iv, iv_length, ALG_OP_ENCRYPT); +} + +int crypt_cipher_decrypt(struct crypt_cipher *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length) +{ + return crypt_cipher_crypt(ctx, in, out, length, + iv, iv_length, ALG_OP_DECRYPT); +} + +int crypt_cipher_destroy(struct crypt_cipher *ctx) +{ + if (ctx->tfmfd != -1) + close(ctx->tfmfd); + if (ctx->opfd != -1) + close(ctx->opfd); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} + +#else /* ENABLE_AF_ALG */ + +int crypt_cipher_init(struct crypt_cipher **ctx, const char *name, + const char *mode, const void *buffer, size_t length) +{ + return -ENOTSUP; +} + +int crypt_cipher_destroy(struct crypt_cipher *ctx) +{ + return 0; +} + +int crypt_cipher_encrypt(struct crypt_cipher *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length) +{ + return -EINVAL; +} +int crypt_cipher_decrypt(struct crypt_cipher *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length) +{ + return -EINVAL; +} +#endif diff --git a/crypto_kernel.c b/crypto_kernel.c new file mode 100755 index 0000000..1775565 --- /dev/null +++ b/crypto_kernel.c @@ -0,0 +1,300 @@ +/* + * Linux kernel userspace API crypto backend implementation + * + * Copyright (C) 2010-2012, Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2012, Milan Broz + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This file 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "crypto_backend.h" + +/* FIXME: remove later */ +#ifndef AF_ALG +#define AF_ALG 38 +#endif +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif + +static int crypto_backend_initialised = 0; +static char version[64]; + +struct hash_alg { + const char *name; + const char *kernel_name; + int length; +}; + +static struct hash_alg hash_algs[] = { + { "sha1", "sha1", 20 }, + { "sha256", "sha256", 32 }, + { "sha512", "sha512", 64 }, + { "ripemd160", "rmd160", 20 }, + { "whirlpool", "wp512", 64 }, + { NULL, NULL, 0 } +}; + +struct crypt_hash { + int tfmfd; + int opfd; + int hash_len; +}; + +struct crypt_hmac { + int tfmfd; + int opfd; + int hash_len; +}; + +/* Defined in crypt_kernel_ciphers.c */ +extern int crypt_kernel_socket_init(struct sockaddr_alg *sa, int *tfmfd, int *opfd); + +int crypt_backend_init(struct crypt_device *ctx) +{ + struct utsname uts; + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "hash", + .salg_name = "sha1", + }; + int tfmfd = -1, opfd = -1; + + if (crypto_backend_initialised) + return 0; + + if (uname(&uts) == -1 || strcmp(uts.sysname, "Linux")) + return -EINVAL; + + if (crypt_kernel_socket_init(&sa, &tfmfd, &opfd) < 0) + return -EINVAL; + + close(tfmfd); + close(opfd); + + snprintf(version, sizeof(version), "%s %s kernel cryptoAPI", + uts.sysname, uts.release); + + crypto_backend_initialised = 1; + return 0; +} + +uint32_t crypt_backend_flags(void) +{ + return CRYPT_BACKEND_KERNEL; +} + +const char *crypt_backend_version(void) +{ + return crypto_backend_initialised ? version : ""; +} + +static struct hash_alg *_get_alg(const char *name) +{ + int i = 0; + + while (name && hash_algs[i].name) { + if (!strcmp(name, hash_algs[i].name)) + return &hash_algs[i]; + i++; + } + return NULL; +} + +/* HASH */ +int crypt_hash_size(const char *name) +{ + struct hash_alg *ha = _get_alg(name); + + return ha ? ha->length : -EINVAL; +} + +int crypt_hash_init(struct crypt_hash **ctx, const char *name) +{ + struct crypt_hash *h; + struct hash_alg *ha; + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "hash", + }; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + ha = _get_alg(name); + if (!ha) { + free(h); + return -EINVAL; + } + h->hash_len = ha->length; + + strncpy((char *)sa.salg_name, ha->kernel_name, sizeof(sa.salg_name)); + + if (crypt_kernel_socket_init(&sa, &h->tfmfd, &h->opfd) < 0) { + free(h); + return -EINVAL; + } + + *ctx = h; + return 0; +} + +int crypt_hash_write(struct crypt_hash *ctx, const char *buffer, size_t length) +{ + ssize_t r; + + r = send(ctx->opfd, buffer, length, MSG_MORE); + if (r < 0 || (size_t)r < length) + return -EIO; + + return 0; +} + +int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length) +{ + ssize_t r; + + if (length > (size_t)ctx->hash_len) + return -EINVAL; + + r = read(ctx->opfd, buffer, length); + if (r < 0) + return -EIO; + + return 0; +} + +int crypt_hash_destroy(struct crypt_hash *ctx) +{ + if (ctx->tfmfd != -1) + close(ctx->tfmfd); + if (ctx->opfd != -1) + close(ctx->opfd); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} + +/* HMAC */ +int crypt_hmac_size(const char *name) +{ + return crypt_hash_size(name); +} + +int crypt_hmac_init(struct crypt_hmac **ctx, const char *name, + const void *buffer, size_t length) +{ + struct crypt_hmac *h; + struct hash_alg *ha; + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "hash", + }; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + ha = _get_alg(name); + if (!ha) { + free(h); + return -EINVAL; + } + h->hash_len = ha->length; + + snprintf((char *)sa.salg_name, sizeof(sa.salg_name), + "hmac(%s)", ha->kernel_name); + + if (crypt_kernel_socket_init(&sa, &h->tfmfd, &h->opfd) < 0) { + free(h); + return -EINVAL; + } + + if (setsockopt(h->tfmfd, SOL_ALG, ALG_SET_KEY, buffer, length) == -1) { + crypt_hmac_destroy(h); + return -EINVAL; + } + + *ctx = h; + return 0; +} + +int crypt_hmac_write(struct crypt_hmac *ctx, const char *buffer, size_t length) +{ + ssize_t r; + + r = send(ctx->opfd, buffer, length, MSG_MORE); + if (r < 0 || (size_t)r < length) + return -EIO; + + return 0; +} + +int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length) +{ + ssize_t r; + + if (length > (size_t)ctx->hash_len) + return -EINVAL; + + r = read(ctx->opfd, buffer, length); + if (r < 0) + return -EIO; + + return 0; +} + +int crypt_hmac_destroy(struct crypt_hmac *ctx) +{ + if (ctx->tfmfd != -1) + close(ctx->tfmfd); + if (ctx->opfd != -1) + close(ctx->opfd); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} + +/* RNG - N/A */ +int crypt_backend_rng(char *buffer, size_t length, int quality, int fips) +{ + return -EINVAL; +} + +#if 0 +/* PBKDF */ +int crypt_pbkdf(const char *kdf, const char *hash, + const char *password, size_t password_length, + const char *salt, size_t salt_length, + char *key, size_t key_length, + unsigned int iterations) +{ + if (!kdf || strncmp(kdf, "pbkdf2", 6)) + return -EINVAL; + + return pkcs5_pbkdf2(hash, password, password_length, salt, salt_length, + iterations, key_length, key); +} + +#endif diff --git a/dmcryptfile.c b/dmcryptfile.c new file mode 100755 index 0000000..5c9bffa --- /dev/null +++ b/dmcryptfile.c @@ -0,0 +1,306 @@ +/***************************************************************************** + +Encrypts/decrypts file in dm-crypt compatible way, using kernel crypto API. +Copyright (c) 2013 Maxim Radugin. + +Usage: dmcryptfile + +This utility is designed to encrypt disk/partition image files in userspace +without root priviledges and without using device mapper and cryptsetup. +Currently supports only plain mode, no LUKS support! +Backend code is taken from cryptsetup. +Input key size should be of desired cipher key size. +Key is passed directly to the cipher algorithm, no hashing or any other +processing is performed. It is up to the user to provide strong key. +For available ciphers, chain modes and key sizes check /proc/crypto. +Only plain IV mode is supported. +Input file size must be multiple of sector size i.e. 512 bytes. +If input file is not multiple of sector size, output will be truncated. + +****************************************************************************** + +The MIT License (MIT) + +Copyright (c) 2013 Maxim Radugin. + +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 /* for printf */ +#include /* for exit */ +#include +#include /* for getopt_long; standard getopt is in unistd.h */ +#include +#include +#include +#include + +#include "crypto_backend.h" + +#define SECTOR_SIZE 512 + +#define FILENAME_LEN 255 + +enum { + ACTION_NONE, + ACTION_ENCRYPT, + ACTION_DECRYPT +}; + +static struct option long_options[] = +{ + {"cipher", required_argument, 0, 'c'}, + {"key-size", required_argument, 0, 's'}, + {"key-file", required_argument, 0, 'k'}, + {"in-file", required_argument, 0, 'i'}, + {"out-file", required_argument, 0, 'o'}, + {"enc", no_argument, 0, 'e'}, + {"dec", no_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} +}; + +void display_usage(char* name) { + printf("Encrypts/decrypts file in dm-crypt compatible way, using kernel crypto API.\n"); + printf("Copyright (c) 2013 Maxim Radugin.\n\n"); + + printf("Usage: %s \n\n", name); + + printf("This utility is designed to encrypt disk/partition image files in userspace\n"); + printf("without root priviledges and without using device mapper and cryptsetup.\n"); + printf("Currently supports only plain mode, no LUKS support!\n"); + printf("Backend code is taken from cryptsetup.\n"); + printf("Input key size should be of desired cipher key size.\n"); + printf("Key is passed directly to the cipher algorithm, no hashing or any other\n"); + printf("processing is performed. It is up to the user to provide strong key.\n"); + printf("For available ciphers, chain modes and key sizes check /proc/crypto.\n"); + printf("Only plain IV mode is supported.\n"); + printf("Input file size must be multiple of sector size i.e. %d bytes.\n", SECTOR_SIZE); + printf("If input file is not multiple of sector size, output will be truncated.\n\n"); + printf("Mandatory arguments:\n"); + printf(" --cipher,-c Cipher-chainmode-ivmode, for example, aes-xts-plain\n"); + printf(" --key-size,-s Size of key in bytes\n"); + printf(" --key-file,-k Input key file, should be at least key-size bytes.\n"); + printf(" Key data after key-size is ignored.\n"); + printf(" --in-file,-i Input file\n"); + printf(" --out-file,-o Output file\n"); + printf(" --enc,-e Perform encryption operation from input to output file.\n"); + printf(" --dec,-d Perform decryption operation from input to output file.\n"); + printf(" --help,-h,-? Display this text.\n"); + +} + +int main(int argc, char * const argv[]) { + int option_index = 0; + int c; + + int action = ACTION_NONE; + char infile[FILENAME_LEN] = {0}; + char outfile[FILENAME_LEN] = {0}; + + int in_fd = -1; + int out_fd = -1; + + char keyfile[FILENAME_LEN] = {0}; + char *key = 0; + int keysize = 0; + + int error = 0; + + char ciphername[21] = {0}; + char chainmode[21] = {0}; + char ivmode[21] = {0}; + + struct crypt_cipher* cipher = 0; + + while ((c = getopt_long (argc, argv, "eds:f:c:i:o:h?", + long_options, &option_index)) != -1) { + switch (c) { + case 0: + break; + case 'e': + action = ACTION_ENCRYPT; + break; + case 'd': + action = ACTION_DECRYPT; + break; + case 'i': + if (optarg) { + snprintf((char*)&infile, FILENAME_LEN, "%s", optarg); + } + break; + case 'o': + if (optarg) { + snprintf((char*)&outfile, FILENAME_LEN, "%s", optarg); + } + break; + case 'k': + if (optarg) { + snprintf((char*)&keyfile, FILENAME_LEN, "%s", optarg); + } + break; + case 's': + if (optarg) + sscanf(optarg, "%i", &keysize); + break; + case 'c': + if (optarg) { + if (sscanf(optarg, "%20[^-]-%20[^-]-%20s", (char*)&ciphername, (char*)&chainmode, (char*)&ivmode) != 3) { + fprintf(stderr, "Invalid cipher format specified %s, expected format cipher-chainmode-ivmode\n", optarg); + error = 1; + } + else { + printf("Cipher: %s, chain mode: %s, iv mode: %s\n", ciphername, chainmode, ivmode); + if (strcmp(ivmode, "plain") != 0) { + fprintf(stderr, "Only plain iv mode is supported, sorry\n"); + error = 1; + } + } + } + break; + case '?': + case 'h': + display_usage(argv[0]); + return 0; + break; + default: + break; + } + } + + if (!strlen((const char *)&keyfile) || !strlen((const char *)&infile) || !strlen((const char *)&outfile)) { + error = 1; + } + + if (!error) { + int key_fd = open((const char*)&keyfile, O_RDONLY); + if (key_fd < 0) { + perror("Unable to open key file"); + } + else { + key = (char*)malloc(keysize); + if (read(key_fd, key, keysize) != keysize) { + fprintf(stderr, "Key file size should be at least %d bytes\n", keysize); + error = 1; + free(key); + key = 0; + } + else { + printf("Key loaded from %s, %d bits\n", (char*)&keyfile, keysize*8); + } + } + } + + if (!error) { + in_fd = open((const char*)&infile, O_RDONLY); + if (in_fd < 0) { + perror("Unable to open input file"); + error = 1; + } + out_fd = open((const char*)&outfile, O_CREAT | O_TRUNC| O_RDWR); + if (out_fd < 0) { + perror("Unable to create output file"); + error = 1; + } + } + + if (!error) { + int r = crypt_cipher_init(&cipher, (char*)&ciphername, (char*)&chainmode, key, keysize); + if (r != 0) { + fprintf(stderr, "Failed to init cipher, check /proc/crypto for available ciphers and chain modes\n"); + error = 1; + } + } + + if (!error) { + char sector[SECTOR_SIZE]; + char out_sector[SECTOR_SIZE]; + char iv[16]; + int r = 0; + unsigned int scnt = 0; + memset((void*)&iv, 0, sizeof(iv)); + printf("Processing...\n"); + while ((r = read(in_fd, (char*)§or, SECTOR_SIZE)) == SECTOR_SIZE) { + *(unsigned int *)iv = scnt & 0xffffffff; + if (action == ACTION_ENCRYPT) { + if (crypt_cipher_encrypt(cipher, (char*)§or, (char*)&out_sector, SECTOR_SIZE, (char*)&iv, sizeof(iv)) == 0) { + if (write(out_fd, (char*)&out_sector, SECTOR_SIZE) != SECTOR_SIZE) { + perror("Write error"); + error = 2; + break; + } + } + else { + fprintf(stderr, "Encryption error\n"); + error = 3; + break; + } + } + if (action == ACTION_DECRYPT) { + if (crypt_cipher_decrypt(cipher, (char*)§or, (char*)&out_sector, SECTOR_SIZE, (char*)&iv, sizeof(iv)) == 0) { + if (write(out_fd, (char*)&out_sector, SECTOR_SIZE) != SECTOR_SIZE) { + perror("Write error"); + error = 2; + break; + } + } + else { + fprintf(stderr, "Decryption error\n"); + error = 3; + break; + } + } + scnt++; + } + if ((r > 0) && (r != SECTOR_SIZE)) { + fprintf(stderr, "Invalid input file size, should be multiple of %d bytes, output file truncated!\n", SECTOR_SIZE); + } + + if (!error) { + printf("Done.\n"); + if (action == ACTION_ENCRYPT) { + printf("To use produced image with cryptosetup, under super user issue:\n"); + printf("cryptsetup open %s --type plain --cipher %s-%s-%s --key-size=%d --key-file=%s\n", outfile, ciphername, chainmode, ivmode, keysize*8, keyfile); + } + } + } + else { + fprintf(stderr, "Invalid arguments specified, see --help\n"); + } + + if (in_fd >= 0) + close(in_fd); + + if (out_fd >= 0) + close(out_fd); + + if (cipher) { + crypt_cipher_destroy(cipher); + } + + if (key) { + free(key); + } + + return error; +} + + +