From aa9fa3e99537b99b4805f2528808450977dbb9cf Mon Sep 17 00:00:00 2001 From: Johannes Bauer Date: Sat, 19 Oct 2019 11:07:55 +0200 Subject: [PATCH] Started working on a coldboot-resistant "vault" implementation When not needed, encrypt the keys in-memory with a large pre-key so that forensic acquisition of data using coldboot becomes infeasible. Not used yet internally. --- vault.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vault.h | 28 +++++++ 2 files changed, 257 insertions(+) create mode 100644 vault.c create mode 100644 vault.h diff --git a/vault.c b/vault.c new file mode 100644 index 0000000..91a32c7 --- /dev/null +++ b/vault.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include +#include +#include "vault.h" + +struct vault_t * vault_init(void *inner_data, unsigned int data_length) { + struct vault_t *vault; + + vault = calloc(1, sizeof(struct vault_t)); + if (!vault) { + return NULL; + } + + vault->key = malloc(DEFAULT_KEY_LENGTH_BYTES); + vault->key_length = DEFAULT_KEY_LENGTH_BYTES; + if (!vault->key) { + vault_free(vault); + return NULL; + } + + if (inner_data) { + vault->data = inner_data; + vault->free_data = false; + } else { + vault->free_data = true; + vault->data = malloc(data_length); + if (!vault->data) { + vault_free(vault); + return NULL; + } + } + vault->is_open = true; + vault->data_length = data_length; + + return vault; +} + +static void vault_destroy_content(struct vault_t *vault) { + if (vault->data) { + OPENSSL_cleanse(vault->data, vault->data_length); + } + if (vault->key) { + OPENSSL_cleanse(vault->key, vault->key_length); + } +} + +static bool vault_derive_key(const struct vault_t *vault, uint8_t key[static 32]) { + /* Derive the AES key from it */ + if (PKCS5_PBKDF2_HMAC((char*)vault->key, vault->key_length, NULL, 0, VAULT_PBKDF2_ITERATIONS, EVP_sha256(), 32, key) != 1) { + return false; + } + return true; +} + +bool vault_open(struct vault_t *vault) { + if (vault->is_open) { + return true; + } + + uint8_t key[32]; + if (!vault_derive_key(vault, key)) { + return false; + } + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) { + return false; + } + + bool success = true; + do { + if (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) != 1) { + success = false; + break; + } + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, sizeof(uint64_t), NULL) != 1) { + success = false; + break; + } + + if (EVP_DecryptInit_ex(ctx, NULL, NULL, key, (unsigned char*)&vault->iv) != 1) { + success = false; + break; + } + + int len = 0; + if (EVP_DecryptUpdate(ctx, vault->data, &len, vault->data, vault->data_length) != 1) { + success = false; + break; + } + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, vault->auth_tag) != 1) { + success = false; + break; + } + + if (EVP_DecryptFinal_ex(ctx, vault->data + len, &len) != 1) { + success = false; + break; + } + + } while (false); + + if (success) { + vault->is_open = true; + OPENSSL_cleanse(vault->key, vault->key_length); + OPENSSL_cleanse(vault->auth_tag, 16); + } else { + /* Vault may be in an inconsistent state. Destroy contents. */ + vault_destroy_content(vault); + } + + EVP_CIPHER_CTX_free(ctx); + OPENSSL_cleanse(key, sizeof(key)); + return success; +} + +bool vault_close(struct vault_t *vault) { + if (!vault->is_open) { + return true; + } + + /* Generate a new key source */ + if (RAND_bytes(vault->key, vault->key_length) != 1) { + return false; + } + + uint8_t key[32]; + if (!vault_derive_key(vault, key)) { + return false; + } + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) { + return false; + } + + /* IV doesn't really make sense here because we never reuse the key, but we + * still do it for good measure (in case someone copies & pastes our code + * into a different application). */ + bool success = true; + do { + vault->iv++; + if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) != 1) { + success = false; + break; + } + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, sizeof(uint64_t), NULL) != 1) { + success = false; + break; + } + + if (EVP_EncryptInit_ex(ctx, NULL, NULL, key, (unsigned char*)&vault->iv) != 1) { + success = false; + break; + } + + int len = 0; + if (EVP_EncryptUpdate(ctx, vault->data, &len, vault->data, vault->data_length) != 1) { + success = false; + break; + } + + if (EVP_EncryptFinal_ex(ctx, vault->data + len, &len) != 1) { + success = false; + break; + } + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, vault->auth_tag) != 1) { + success = false; + break; + } + } while (false); + + if (success) { + vault->is_open = false; + } else { + /* Vault may be in an inconsistent state. Destroy contents. */ + vault_destroy_content(vault); + } + + EVP_CIPHER_CTX_free(ctx); + OPENSSL_cleanse(key, sizeof(key)); + return success; +} + +void vault_free(struct vault_t *vault) { + vault_destroy_content(vault); + if (vault->free_data) { + free(vault->data); + } + free(vault->key); + free(vault); +} + +#ifndef __TEST_VAULT__ + +static void dump(const uint8_t *data, unsigned int length) { + for (unsigned int i = 0; i < length; i++) { + fprintf(stderr, "%02x ", data[i]); + } + fprintf(stderr, "\n"); +} + +int main(void) { + /* gcc -Wall -std=c11 -Wmissing-prototypes -Wstrict-prototypes -Werror=implicit-function-declaration -Wimplicit-fallthrough -Wshadow -pie -fPIE -fsanitize=address -fsanitize=undefined -fsanitize=leak -o vault vault.c -lasan -lubsan -lcrypto + */ + uint8_t data[64]; + dump(data, sizeof(data)); + struct vault_t *vault = vault_init(data, sizeof(data)); + if (!vault_close(vault)) { + fprintf(stderr, "vault close failed.\n"); + abort(); + } + dump(data, sizeof(data)); + if (!vault_open(vault)) { + fprintf(stderr, "vault open failed.\n"); + abort(); + } + dump(data, sizeof(data)); + vault_free(vault); + return 0; +} +#endif diff --git a/vault.h b/vault.h new file mode 100644 index 0000000..f93bf07 --- /dev/null +++ b/vault.h @@ -0,0 +1,28 @@ +#ifndef __VAULT_H__ +#define __VAULT_H__ + +#include +#include + +struct vault_t { + bool is_open; + void *data; + unsigned int data_length; + bool free_data; + uint8_t *key; + unsigned int key_length; + uint8_t auth_tag[16]; + uint64_t iv; +}; + +#define DEFAULT_KEY_LENGTH_BYTES (1024 * 1024) +#define VAULT_PBKDF2_ITERATIONS 50000 + +/*************** AUTO GENERATED SECTION FOLLOWS ***************/ +struct vault_t * vault_init(void *inner_data, unsigned int data_length); +bool vault_open(struct vault_t *vault); +bool vault_close(struct vault_t *vault); +void vault_free(struct vault_t *vault); +/*************** AUTO GENERATED SECTION ENDS ***************/ + +#endif