diff options
Diffstat (limited to 'ubifs-utils/mkfs.ubifs')
-rw-r--r-- | ubifs-utils/mkfs.ubifs/fscrypt.c | 256 | ||||
-rw-r--r-- | ubifs-utils/mkfs.ubifs/fscrypt.h | 112 | ||||
-rw-r--r-- | ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 292 |
3 files changed, 374 insertions, 286 deletions
diff --git a/ubifs-utils/mkfs.ubifs/fscrypt.c b/ubifs-utils/mkfs.ubifs/fscrypt.c new file mode 100644 index 0000000..68001e1 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/fscrypt.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2017 sigma star gmbh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Richard Weinberger <richard@sigma-star.at> + * David Oberhollenzer <david.oberhollenzer@sigma-star.at> + */ + +#define PROGRAM_NAME "mkfs.ubifs" +#include "fscrypt.h" + + +static __u8 fscrypt_masterkey[FS_MAX_KEY_SIZE]; +static struct cipher *fscrypt_cipher; + + +unsigned char *calc_fscrypt_subkey(struct fscrypt_context *fctx) +{ + int ret; + unsigned char *new_key = xmalloc(FS_MAX_KEY_SIZE); + + ret = derive_key_aes(fctx->nonce, fscrypt_masterkey, FS_MAX_KEY_SIZE, new_key); + if (ret < 0) { + err_msg("derive_key_aes failed: %i\n", ret); + + free(new_key); + new_key = NULL; + } + + return new_key; +} + +struct fscrypt_context *inherit_fscrypt_context(struct fscrypt_context *fctx) +{ + struct fscrypt_context *new_fctx = NULL; + + if (fctx) { + new_fctx = xmalloc(sizeof(*new_fctx)); + new_fctx->format = fctx->format; + new_fctx->contents_encryption_mode = fctx->contents_encryption_mode; + new_fctx->filenames_encryption_mode = fctx->filenames_encryption_mode; + new_fctx->flags = fctx->flags; + memcpy(new_fctx->master_key_descriptor, fctx->master_key_descriptor, + FS_KEY_DESCRIPTOR_SIZE); + RAND_bytes((void *)&new_fctx->nonce, FS_KEY_DERIVATION_NONCE_SIZE); + } + + return new_fctx; +} + +void free_fscrypt_context(struct fscrypt_context *fctx) +{ + free(fctx); +} + +void print_fscrypt_master_key_descriptor(struct fscrypt_context *fctx) +{ + int i; + + normsg_cont("fscrypt master key descriptor: "); + for (i = 0; i < FS_KEY_DESCRIPTOR_SIZE; i++) { + normsg_cont("%02x", fctx->master_key_descriptor[i]); + } + normsg(""); +} + +unsigned int fscrypt_fname_encrypted_size(struct fscrypt_context *fctx, + unsigned int ilen) +{ + int padding; + + padding = 4 << (fctx->flags & FS_POLICY_FLAGS_PAD_MASK); + ilen = max_t(unsigned int, ilen, FS_CRYPTO_BLOCK_SIZE); + return round_up(ilen, padding); +} + +int encrypt_path(void **outbuf, void *data, unsigned int data_len, + unsigned int max_namelen, struct fscrypt_context *fctx) +{ + void *inbuf, *crypt_key; + unsigned int padding = 4 << (fctx->flags & FS_POLICY_FLAGS_PAD_MASK); + unsigned int cryptlen; + int ret; + + cryptlen = max_t(unsigned int, data_len, FS_CRYPTO_BLOCK_SIZE); + cryptlen = round_up(cryptlen, padding); + cryptlen = min(cryptlen, max_namelen); + + inbuf = xmalloc(cryptlen); + /* CTS mode needs a block size aligned buffer */ + *outbuf = xmalloc(round_up(cryptlen, FS_CRYPTO_BLOCK_SIZE)); + + memset(inbuf, 0, cryptlen); + memcpy(inbuf, data, data_len); + + crypt_key = calc_fscrypt_subkey(fctx); + if (!crypt_key) + return err_msg("could not compute subkey"); + + ret = fscrypt_cipher->encrypt_fname(inbuf, cryptlen, + crypt_key, *outbuf); + if (ret < 0) + return err_msg("could not encrypt filename"); + + free(crypt_key); + free(inbuf); + return cryptlen; +} + +int encrypt_data_node(struct fscrypt_context *fctx, unsigned int block_no, + struct ubifs_data_node *dn, size_t length) +{ + void *inbuf, *outbuf, *crypt_key; + size_t ret, pad_len = round_up(length, FS_CRYPTO_BLOCK_SIZE); + + dn->compr_size = length; + + inbuf = xzalloc(pad_len); + outbuf = xzalloc(pad_len); + + memcpy(inbuf, &dn->data, length); + + crypt_key = calc_fscrypt_subkey(fctx); + if (!crypt_key) + return err_msg("could not compute subkey"); + + ret = fscrypt_cipher->encrypt_block(inbuf, pad_len, + crypt_key, block_no, + outbuf); + if (ret != pad_len) { + return err_msg("encrypt_block returned %zi " + "instead of %zi", ret, pad_len); + } + + memcpy(&dn->data, outbuf, pad_len); + + free(inbuf); + free(outbuf); + free(crypt_key); + return pad_len; +} + +static int xdigit(int x) +{ + if (isupper(x)) + return x - 'A' + 0x0A; + if (islower(x)) + return x - 'a' + 0x0A; + return x - '0'; +} + +static int parse_key_descriptor(const char *desc, __u8 *dst) +{ + int i, hi, lo; + + for (i = 0; i < FS_KEY_DESCRIPTOR_SIZE; ++i) { + if (!desc[i * 2] || !desc[i * 2 + 1]) { + err_msg("key descriptor '%s' is too short", desc); + return -1; + } + if (!isxdigit(desc[i * 2]) || !isxdigit(desc[i * 2 + 1])) { + err_msg("invalid key descriptor '%s'", desc); + return -1; + } + + hi = xdigit(desc[i * 2]); + lo = xdigit(desc[i * 2 + 1]); + + dst[i] = (hi << 4) | lo; + } + + if (desc[i * 2]) { + err_msg("key descriptor '%s' is too long", desc); + return -1; + } + return 0; +} + +static int load_master_key(const char *key_file) +{ + int kf; + ssize_t keysize; + + kf = open(key_file, O_RDONLY); + if (kf < 0) { + sys_errmsg("open '%s'", key_file); + return -1; + } + + keysize = read(kf, fscrypt_masterkey, sizeof(fscrypt_masterkey)); + if (keysize < 0) { + sys_errmsg("read '%s'", key_file); + goto fail; + } + if (keysize == 0) { + err_msg("loading key from '%s': file is empty", key_file); + goto fail; + } + + close(kf); + return 0; +fail: + close(kf); + return -1; +} + +struct fscrypt_context *init_fscrypt_context(const char *cipher_name, + unsigned int flags, + const char *key_file, + const char *key_descriptor) +{ + __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; + __u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; + struct fscrypt_context *new_fctx; + + fscrypt_cipher = get_cipher(cipher_name); + + if (!fscrypt_cipher) { + fprintf(stderr, "Cannot find cipher '%s'\n" + "Try `%s --help' for more information\n", + cipher_name, PROGRAM_NAME); + return NULL; + } + + if (parse_key_descriptor(key_descriptor, master_key_descriptor)) + return NULL; + + if (load_master_key(key_file)) + return NULL; + + RAND_bytes((void *)nonce, FS_KEY_DERIVATION_NONCE_SIZE); + + new_fctx = xmalloc(sizeof(*new_fctx)); + + new_fctx->format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; + new_fctx->contents_encryption_mode = FS_ENCRYPTION_MODE_AES_128_CBC; + new_fctx->filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_128_CTS; + new_fctx->flags = flags; + + memcpy(&new_fctx->nonce, nonce, FS_KEY_DERIVATION_NONCE_SIZE); + memcpy(&new_fctx->master_key_descriptor, master_key_descriptor, + FS_KEY_DESCRIPTOR_SIZE); + return new_fctx; +} diff --git a/ubifs-utils/mkfs.ubifs/fscrypt.h b/ubifs-utils/mkfs.ubifs/fscrypt.h new file mode 100644 index 0000000..b6fb6d1 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/fscrypt.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 sigma star gmbh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Richard Weinberger <richard@sigma-star.at> + * David Oberhollenzer <david.oberhollenzer@sigma-star.at> + */ + +#ifndef FSCRYPT_H +#define FSCRYPT_H + + +#include "mkfs.ubifs.h" +#include <sys/types.h> +#include "crypto.h" + + +#ifndef FS_KEY_DESCRIPTOR_SIZE +#define FS_KEY_DESCRIPTOR_SIZE 8 +#endif +#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 +#define FS_KEY_DERIVATION_NONCE_SIZE 16 + +#ifndef FS_ENCRYPTION_MODE_AES_128_CBC +#define FS_ENCRYPTION_MODE_AES_128_CBC 5 +#endif + +#ifndef FS_ENCRYPTION_MODE_AES_128_CTS +#define FS_ENCRYPTION_MODE_AES_128_CTS 6 +#endif + +#ifndef FS_POLICY_FLAGS_VALID +#define FS_POLICY_FLAGS_PAD_4 0x00 +#define FS_POLICY_FLAGS_PAD_8 0x01 +#define FS_POLICY_FLAGS_PAD_16 0x02 +#define FS_POLICY_FLAGS_PAD_32 0x03 +#define FS_POLICY_FLAGS_PAD_MASK 0x03 +#define FS_POLICY_FLAGS_VALID 0x03 +#endif + +#define FS_CRYPTO_BLOCK_SIZE 16 + +/** + * Encryption context for inode + * + * Protector format: + * 1 byte: Protector format (1 = this version) + * 1 byte: File contents encryption mode + * 1 byte: File names encryption mode + * 1 byte: Flags + * 8 bytes: Master Key descriptor + * 16 bytes: Encryption Key derivation nonce + */ +struct fscrypt_context { + __u8 format; + __u8 contents_encryption_mode; + __u8 filenames_encryption_mode; + __u8 flags; + __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; + __u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; +} __attribute__((packed)); + +/** + * For encrypted symlinks, the ciphertext length is stored at the beginning + * of the string in little-endian format. + */ +struct fscrypt_symlink_data { + __le16 len; + char encrypted_path[1]; +} __attribute__((packed)); + + +#ifndef FS_MAX_KEY_SIZE +#define FS_MAX_KEY_SIZE 64 +#endif + +unsigned char *calc_fscrypt_subkey(struct fscrypt_context *fctx); + +struct fscrypt_context *inherit_fscrypt_context(struct fscrypt_context *fctx); + +void free_fscrypt_context(struct fscrypt_context *fctx); + +void print_fscrypt_master_key_descriptor(struct fscrypt_context *fctx); + +unsigned int fscrypt_fname_encrypted_size(struct fscrypt_context *fctx, + unsigned int ilen); + +int encrypt_path(void **outbuf, void *data, unsigned int data_len, + unsigned int max_namelen, struct fscrypt_context *fctx); + +int encrypt_data_node(struct fscrypt_context *fctx, unsigned int block_no, + struct ubifs_data_node *dn, size_t length); + +struct fscrypt_context *init_fscrypt_context(const char *cipher_name, + unsigned int flags, + const char *key_file, + const char *key_descriptor); + +#endif /* FSCRYPT_H */ + diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c index 707758a..1710e25 100644 --- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c +++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c @@ -36,6 +36,7 @@ #endif #include "crypto.h" +#include "fscrypt.h" /* Size (prime number) of hash table for link counting */ #define HASH_TABLE_SIZE 10099 @@ -110,66 +111,6 @@ struct inum_mapping { struct stat st; }; -#ifndef FS_KEY_DESCRIPTOR_SIZE -#define FS_KEY_DESCRIPTOR_SIZE 8 -#endif -#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 -#define FS_KEY_DERIVATION_NONCE_SIZE 16 - -#ifndef FS_ENCRYPTION_MODE_AES_128_CBC -#define FS_ENCRYPTION_MODE_AES_128_CBC 5 -#endif - -#ifndef FS_ENCRYPTION_MODE_AES_128_CTS -#define FS_ENCRYPTION_MODE_AES_128_CTS 6 -#endif - -#ifndef FS_POLICY_FLAGS_VALID -#define FS_POLICY_FLAGS_PAD_4 0x00 -#define FS_POLICY_FLAGS_PAD_8 0x01 -#define FS_POLICY_FLAGS_PAD_16 0x02 -#define FS_POLICY_FLAGS_PAD_32 0x03 -#define FS_POLICY_FLAGS_PAD_MASK 0x03 -#define FS_POLICY_FLAGS_VALID 0x03 -#endif - -#define FS_CRYPTO_BLOCK_SIZE 16 - -/** - * Encryption context for inode - * - * Protector format: - * 1 byte: Protector format (1 = this version) - * 1 byte: File contents encryption mode - * 1 byte: File names encryption mode - * 1 byte: Flags - * 8 bytes: Master Key descriptor - * 16 bytes: Encryption Key derivation nonce - */ -struct fscrypt_context { - __u8 format; - __u8 contents_encryption_mode; - __u8 filenames_encryption_mode; - __u8 flags; - __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; - __u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; -} __attribute__((packed)); - -/** - * For encrypted symlinks, the ciphertext length is stored at the beginning - * of the string in little-endian format. - */ -struct fscrypt_symlink_data { - __le16 len; - char encrypted_path[1]; -} __attribute__((packed)); - - -#ifndef FS_MAX_KEY_SIZE -#define FS_MAX_KEY_SIZE 64 -#endif -static __u8 fscrypt_masterkey[FS_MAX_KEY_SIZE]; - /* * Because we copy functions from the kernel, we use a subset of the UBIFS * file-system description object struct ubifs_info. @@ -186,7 +127,6 @@ int yes; static char *root; static int root_len; static struct fscrypt_context *root_fctx; -static struct cipher *fscrypt_cipher; static struct stat root_st; static char *output; static int out_fd; @@ -541,160 +481,6 @@ static long long get_bytes(const char *str) return bytes; } - -static unsigned char *calc_fscrypt_subkey(struct fscrypt_context *fctx) -{ - int ret; - unsigned char *new_key = xmalloc(FS_MAX_KEY_SIZE); - - ret = derive_key_aes(fctx->nonce, fscrypt_masterkey, FS_MAX_KEY_SIZE, new_key); - if (ret < 0) { - err_msg("derive_key_aes failed: %i\n", ret); - - free(new_key); - new_key = NULL; - } - - return new_key; -} - -static struct fscrypt_context *inherit_fscrypt_context(struct fscrypt_context *fctx) -{ - struct fscrypt_context *new_fctx = NULL; - - if (fctx) { - new_fctx = xmalloc(sizeof(*new_fctx)); - new_fctx->format = fctx->format; - new_fctx->contents_encryption_mode = fctx->contents_encryption_mode; - new_fctx->filenames_encryption_mode = fctx->filenames_encryption_mode; - new_fctx->flags = fctx->flags; - memcpy(new_fctx->master_key_descriptor, fctx->master_key_descriptor, - FS_KEY_DESCRIPTOR_SIZE); - RAND_bytes((void *)&new_fctx->nonce, FS_KEY_DERIVATION_NONCE_SIZE); - } - - return new_fctx; -} - -static void free_fscrypt_context(struct fscrypt_context *fctx) -{ - free(fctx); -} - -static void print_fscrypt_master_key_descriptor(struct fscrypt_context *fctx) -{ - int i; - - normsg_cont("fscrypt master key descriptor: "); - for (i = 0; i < FS_KEY_DESCRIPTOR_SIZE; i++) { - normsg_cont("%02x", fctx->master_key_descriptor[i]); - } - normsg(""); -} - -static int xdigit(int x) -{ - if (isupper(x)) - return x - 'A' + 0x0A; - if (islower(x)) - return x - 'a' + 0x0A; - return x - '0'; -} - -static int parse_key_descriptor(const char *desc, __u8 *dst) -{ - int i, hi, lo; - - for (i = 0; i < FS_KEY_DESCRIPTOR_SIZE; ++i) { - if (!desc[i * 2] || !desc[i * 2 + 1]) { - err_msg("key descriptor '%s' is too short", desc); - return -1; - } - if (!isxdigit(desc[i * 2]) || !isxdigit(desc[i * 2 + 1])) { - err_msg("invalid key descriptor '%s'", desc); - return -1; - } - - hi = xdigit(desc[i * 2]); - lo = xdigit(desc[i * 2 + 1]); - - dst[i] = (hi << 4) | lo; - } - - if (desc[i * 2]) { - err_msg("key descriptor '%s' is too long", desc); - return -1; - } - return 0; -} - -static int load_master_key(const char *key_file) -{ - int kf; - ssize_t keysize; - - kf = open(key_file, O_RDONLY); - if (kf < 0) { - sys_errmsg("open '%s'", key_file); - return -1; - } - - keysize = read(kf, fscrypt_masterkey, sizeof(fscrypt_masterkey)); - if (keysize < 0) { - sys_errmsg("read '%s'", key_file); - goto fail; - } - if (keysize == 0) { - err_msg("loading key from '%s': file is empty", key_file); - goto fail; - } - - close(kf); - return 0; -fail: - close(kf); - return -1; -} - -static struct fscrypt_context *init_fscrypt_context(unsigned int flags, - const char *key_file, - const char *key_descriptor) -{ - __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; - __u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; - struct fscrypt_context *new_fctx; - - if (parse_key_descriptor(key_descriptor, master_key_descriptor)) - return NULL; - - if (load_master_key(key_file)) - return NULL; - - RAND_bytes((void *)nonce, FS_KEY_DERIVATION_NONCE_SIZE); - - new_fctx = xmalloc(sizeof(*new_fctx)); - - new_fctx->format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; - new_fctx->contents_encryption_mode = FS_ENCRYPTION_MODE_AES_128_CBC; - new_fctx->filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_128_CTS; - new_fctx->flags = flags; - - memcpy(&new_fctx->nonce, nonce, FS_KEY_DERIVATION_NONCE_SIZE); - memcpy(&new_fctx->master_key_descriptor, master_key_descriptor, - FS_KEY_DESCRIPTOR_SIZE); - return new_fctx; -} - -unsigned int fscrypt_fname_encrypted_size(struct fscrypt_context *fctx, unsigned int ilen) -{ - int padding; - - padding = 4 << (fctx->flags & FS_POLICY_FLAGS_PAD_MASK); - ilen = max_t(unsigned int, ilen, FS_CRYPTO_BLOCK_SIZE); - return round_up(ilen, padding); -} - - /** * open_ubi - open the UBI volume. * @node: name of the UBI volume character device to fetch information about @@ -972,19 +758,11 @@ static int get_options(int argc, char**argv) c->double_hash = 1; c->encrypted = 1; - root_fctx = init_fscrypt_context(fscrypt_flags, + root_fctx = init_fscrypt_context(cipher_name, fscrypt_flags, key_file, key_desc); if (!root_fctx) return -1; - fscrypt_cipher = get_cipher(cipher_name); - if (!fscrypt_cipher) { - fprintf(stderr, "Cannot find cipher '%s'\n" - "Try `%s --help' for more information\n", - cipher_name, PROGRAM_NAME); - return -1; - } - print_fscrypt_master_key_descriptor(root_fctx); } @@ -1616,39 +1394,6 @@ static int set_fscrypt_context(struct ubifs_ino_node *host_ino, ino_t inum, fctx, sizeof(*fctx)); } -static int encrypt_path(void **outbuf, void *data, unsigned int data_len, - unsigned int max_namelen, struct fscrypt_context *fctx) -{ - void *inbuf, *crypt_key; - unsigned int padding = 4 << (fctx->flags & FS_POLICY_FLAGS_PAD_MASK); - unsigned int cryptlen; - int ret; - - cryptlen = max_t(unsigned int, data_len, FS_CRYPTO_BLOCK_SIZE); - cryptlen = round_up(cryptlen, padding); - cryptlen = min(cryptlen, max_namelen); - - inbuf = xmalloc(cryptlen); - /* CTS mode needs a block size aligned buffer */ - *outbuf = xmalloc(round_up(cryptlen, FS_CRYPTO_BLOCK_SIZE)); - - memset(inbuf, 0, cryptlen); - memcpy(inbuf, data, data_len); - - crypt_key = calc_fscrypt_subkey(fctx); - if (!crypt_key) - return err_msg("could not compute subkey"); - - ret = fscrypt_cipher->encrypt_fname(inbuf, cryptlen, - crypt_key, *outbuf); - if (ret < 0) - return err_msg("could not encrypt filename"); - - free(crypt_key); - free(inbuf); - return cryptlen; -} - static int encrypt_symlink(void *dst, void *data, unsigned int data_len, struct fscrypt_context *fctx) { @@ -2011,35 +1756,10 @@ static int add_file(const char *path_name, struct stat *st, ino_t inum, if (!fctx) { dn->compr_size = 0; } else { - void *inbuf, *outbuf, *crypt_key; - size_t ret, pad_len = round_up(out_len, FS_CRYPTO_BLOCK_SIZE); - - dn->compr_size = out_len; - - inbuf = xzalloc(pad_len); - outbuf = xzalloc(pad_len); - - memcpy(inbuf, &dn->data, out_len); - - crypt_key = calc_fscrypt_subkey(fctx); - if (!crypt_key) - return err_msg("could not compute subkey"); - - ret = fscrypt_cipher->encrypt_block(inbuf, pad_len, - crypt_key, block_no, - outbuf); - if (ret != pad_len) { - return err_msg("encrypt_block returned %zi " - "instead of %zi", ret, pad_len); - } - - memcpy(&dn->data, outbuf, pad_len); - - out_len = pad_len; - - free(inbuf); - free(outbuf); - free(crypt_key); + ret = encrypt_data_node(fctx, block_no, dn, out_len); + if (ret < 0) + return ret; + out_len = ret; } dn_len = UBIFS_DATA_NODE_SZ + out_len; |