diff --git a/repos/gems/lib/mk/spec/x86_64/vfs_cbe_trust_anchor.mk b/repos/gems/lib/mk/spec/x86_64/vfs_cbe_trust_anchor.mk index 8edb1c8571..9ac5ac1d93 100644 --- a/repos/gems/lib/mk/spec/x86_64/vfs_cbe_trust_anchor.mk +++ b/repos/gems/lib/mk/spec/x86_64/vfs_cbe_trust_anchor.mk @@ -1,7 +1,10 @@ OPENSSL_DIR = $(call select_from_ports,openssl) -SRC_CC = vfs.cc +SRC_CC += vfs.cc +SRC_CC += aes_256.cc +SRC_CC += integer.cc +INC_DIR += $(REP_DIR)/src/lib/vfs/cbe_trust_anchor INC_DIR += $(OPENSSL_DIR)/include LIBS += libcrypto diff --git a/repos/gems/src/lib/vfs/cbe_trust_anchor/aes_256.cc b/repos/gems/src/lib/vfs/cbe_trust_anchor/aes_256.cc new file mode 100644 index 0000000000..ca24e5e243 --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_trust_anchor/aes_256.cc @@ -0,0 +1,225 @@ +/* + * \brief Local variants of doing AES-256 and AES-256 key wrapping + * \author Martin Stein + * \date 2021-04-16 + */ + +/* + * Copyright (C) 2021 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include + +/* OpenSSL includes */ +namespace Openssl { + + #include +} + +/* local includes */ +#include +#include + +using namespace Genode; + + +namespace Aes_256 +{ + struct Initialization_vector + { + unsigned char values[16]; + }; +} + +namespace Aes_256_key_wrap { + + enum { KEY_PLAINTEXT_NR_OF_64_BIT_VALUES = KEY_PLAINTEXT_SIZE / 8 }; + enum { NR_OF_WRAPPING_STEPS = 6 }; + enum { INTEGRITY_CHECK_VALUE = 0xa6a6a6a6a6a6a6a6 }; +} + + +template +static void inline overwrite_object_with_zeroes(OBJECT_TYPE &object) +{ + memset(&object, 0, sizeof(object)); + + /* trigger compiler to not drop the memset */ + asm volatile(""::"r"(&object):"memory"); +} + + +void Aes_256::encrypt_with_zeroed_iv(unsigned char *ciphertext_base, + size_t ciphertext_size, + unsigned char const *plaintext_base, + unsigned char const *key_base, + size_t key_size) +{ + Openssl::AES_KEY aes_key; + if (AES_set_encrypt_key(key_base, key_size * 8, &aes_key)) { + class Failed_to_set_key { }; + throw Failed_to_set_key { }; + } + Aes_256::Initialization_vector iv { }; + memset(iv.values, 0, sizeof(iv.values)); + AES_cbc_encrypt( + plaintext_base, ciphertext_base, ciphertext_size, &aes_key, iv.values, + AES_ENCRYPT); + + /* ensure that relevant encryption data doesn't remain on the stack */ + overwrite_object_with_zeroes(aes_key); +} + + +void Aes_256::decrypt_with_zeroed_iv(unsigned char *plaintext_base, + size_t plaintext_size, + unsigned char const *ciphertext_base, + unsigned char const *key_base, + size_t key_size) +{ + Openssl::AES_KEY aes_key; + if (AES_set_decrypt_key(key_base, key_size * 8, &aes_key)) { + class Failed_to_set_key { }; + throw Failed_to_set_key { }; + } + Aes_256::Initialization_vector iv { }; + memset(iv.values, 0, sizeof(iv.values)); + AES_cbc_encrypt( + ciphertext_base, plaintext_base, plaintext_size, &aes_key, iv.values, + AES_DECRYPT); + + /* ensure that relevant encryption data doesn't remain on the stack */ + overwrite_object_with_zeroes(aes_key); +} + + +void Aes_256_key_wrap::wrap_key(unsigned char *ciphertext_uint8, + size_t ciphertext_size, + unsigned char const *key_plaintext_uint8, + size_t key_plaintext_size, + unsigned char const *key_encryption_key_uint8, + size_t key_encryption_key_size) +{ + if (ciphertext_size != CIPHERTEXT_SIZE) { + class Bad_ciphertext_size { }; + throw Bad_ciphertext_size { }; + } + if (key_plaintext_size != KEY_PLAINTEXT_SIZE) { + class Bad_key_plaintext_size { }; + throw Bad_key_plaintext_size { }; + } + if (key_encryption_key_size != KEY_ENCRYPTION_KEY_SIZE) { + class Bad_key_encryption_key_size { }; + throw Bad_key_encryption_key_size { }; + } + uint64_t *ciphertext { (uint64_t *)ciphertext_uint8 }; + uint64_t const *key_plaintext { (uint64_t const *)key_plaintext_uint8 }; + uint64_t const *key_encryption_key { + (uint64_t const *)key_encryption_key_uint8 }; + + ciphertext[0] = INTEGRITY_CHECK_VALUE; + memcpy( + &ciphertext[1], &key_plaintext[0], + ciphertext_size - sizeof(ciphertext[0])); + + for (unsigned step_idx = 0; + step_idx < NR_OF_WRAPPING_STEPS; + step_idx++) { + + for (unsigned value_idx = 1; + value_idx <= KEY_PLAINTEXT_NR_OF_64_BIT_VALUES; + value_idx++) { + + uint64_t encryption_input[2]; + encryption_input[0] = ciphertext[0]; + encryption_input[1] = ciphertext[value_idx]; + + uint64_t encryption_output[2]; + + Aes_256::encrypt_with_zeroed_iv( + (unsigned char *)encryption_output, + sizeof(encryption_output), + (unsigned char *)encryption_input, + (unsigned char *)key_encryption_key, + key_encryption_key_size); + + uint64_t const xor_operand { + Integer::u64_swap_byte_order( + ((uint64_t)KEY_PLAINTEXT_NR_OF_64_BIT_VALUES * step_idx) + + value_idx) }; + + ciphertext[0] = encryption_output[0] ^ xor_operand; + ciphertext[value_idx] = encryption_output[1]; + } + } +} + + +void Aes_256_key_wrap::unwrap_key(unsigned char *key_plaintext_uint8, + size_t key_plaintext_size, + bool &key_plaintext_corrupt, + unsigned char const *ciphertext_uint8, + size_t ciphertext_size, + unsigned char const *key_encryption_key_uint8, + size_t key_encryption_key_size) +{ + if (key_plaintext_size != KEY_PLAINTEXT_SIZE) { + class Bad_key_plaintext_size { }; + throw Bad_key_plaintext_size { }; + } + if (ciphertext_size != CIPHERTEXT_SIZE) { + class Bad_ciphertext_size { }; + throw Bad_ciphertext_size { }; + } + if (key_encryption_key_size != KEY_ENCRYPTION_KEY_SIZE) { + class Bad_key_encryption_key_size { }; + throw Bad_key_encryption_key_size { }; + } + uint64_t *key_plaintext { (uint64_t *)key_plaintext_uint8 }; + uint64_t const *ciphertext { (uint64_t const *)ciphertext_uint8 }; + uint64_t const *key_encryption_key { + (uint64_t const *)key_encryption_key_uint8 }; + + uint64_t integrity_check_value { ciphertext[0] }; + memcpy(&key_plaintext[0], &ciphertext[1], key_plaintext_size); + + for (signed step_idx = NR_OF_WRAPPING_STEPS - 1; + step_idx >= 0; + step_idx--) { + + for (unsigned value_idx = KEY_PLAINTEXT_NR_OF_64_BIT_VALUES; + value_idx >= 1; + value_idx--) { + + uint64_t const xor_operand { + Integer::u64_swap_byte_order( + ((uint64_t)KEY_PLAINTEXT_NR_OF_64_BIT_VALUES * step_idx) + + value_idx) }; + + uint64_t encryption_input[2]; + encryption_input[0] = integrity_check_value ^ xor_operand; + encryption_input[1] = key_plaintext[value_idx - 1]; + + uint64_t encryption_output[2]; + + Aes_256::decrypt_with_zeroed_iv( + (unsigned char *)encryption_output, + sizeof(encryption_output), + (unsigned char *)encryption_input, + (unsigned char *)key_encryption_key, + key_encryption_key_size); + + integrity_check_value = encryption_output[0]; + key_plaintext[value_idx - 1] = encryption_output[1]; + } + } + if (integrity_check_value == INTEGRITY_CHECK_VALUE) { + key_plaintext_corrupt = false; + } else { + key_plaintext_corrupt = true; + } +} diff --git a/repos/gems/src/lib/vfs/cbe_trust_anchor/aes_256.h b/repos/gems/src/lib/vfs/cbe_trust_anchor/aes_256.h new file mode 100644 index 0000000000..9bedb62145 --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_trust_anchor/aes_256.h @@ -0,0 +1,71 @@ +/* + * \brief Local variants of doing AES-256 and AES-256 key wrapping + * \author Martin Stein + * \date 2021-04-16 + */ + +/* + * Copyright (C) 2021 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _AES_256_H_ +#define _AES_256_H_ + +/* Genode includes */ +#include + +namespace Aes_256 +{ + void encrypt_with_zeroed_iv(unsigned char *ciphertext_base, + Genode::size_t ciphertext_size, + unsigned char const *plaintext_base, + unsigned char const *key_base, + Genode::size_t key_size); + + void decrypt_with_zeroed_iv(unsigned char *plaintext_base, + Genode::size_t plaintext_size, + unsigned char const *ciphertext_base, + unsigned char const *key_base, + Genode::size_t key_size); +} + +namespace Aes_256_key_wrap +{ + enum { KEY_PLAINTEXT_SIZE = 32 }; + enum { CIPHERTEXT_SIZE = 40 }; + enum { KEY_ENCRYPTION_KEY_SIZE = 32 }; + + /** + * Implementation of the "Key Wrap" algorithm (alternative indexing-based + * variant) defined in RFC 3394 "Advanced Encryption Standard (AES) Key + * Wrap Algorithm" paragraph 2.2.1, artificially tailored to a + * key-encryption-key (KEK) size of 256 bits and a key (key data) size of + * 256 bits. + */ + void wrap_key(unsigned char *ciphertext_uint8, + Genode::size_t ciphertext_size, + unsigned char const *key_plaintext_uint8, + Genode::size_t key_plaintext_size, + unsigned char const *key_encryption_key_uint8, + Genode::size_t key_encryption_key_size); + + /** + * Implementation of the "Key Unwrap" algorithm (alternative indexing-based + * variant) defined in RFC 3394 "Advanced Encryption Standard (AES) Key + * Wrap Algorithm" paragraph 2.2.2, artificially tailored to a + * key-encryption-key (KEK) size of 256 bits and a key (key data) size of + * 256 bits. + */ + void unwrap_key(unsigned char *key_plaintext_uint8, + Genode::size_t key_plaintext_size, + bool &key_plaintext_corrupt, + unsigned char const *ciphertext_uint8, + Genode::size_t ciphertext_size, + unsigned char const *key_encryption_key_uint8, + Genode::size_t key_encryption_key_size); +} + +#endif /* _AES_256_H_ */ diff --git a/repos/gems/src/lib/vfs/cbe_trust_anchor/integer.cc b/repos/gems/src/lib/vfs/cbe_trust_anchor/integer.cc new file mode 100644 index 0000000000..85fc63efd3 --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_trust_anchor/integer.cc @@ -0,0 +1,28 @@ +/* + * \brief Helper functions for modifying integer values + * \author Martin Stein + * \date 2021-04-16 + */ + +/* + * Copyright (C) 2021 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* local includes */ +#include + +using namespace Genode; + + +uint64_t Integer::u64_swap_byte_order(uint64_t input) +{ + uint64_t result { 0 }; + for (unsigned byte_idx = 0; byte_idx < 8; byte_idx++) { + uint8_t const byte { (uint8_t)(input >> (byte_idx * 8)) }; + result |= (uint64_t)byte << ((7 - byte_idx) * 8); + } + return result; +} diff --git a/repos/gems/src/lib/vfs/cbe_trust_anchor/integer.h b/repos/gems/src/lib/vfs/cbe_trust_anchor/integer.h new file mode 100644 index 0000000000..1547c42bdd --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_trust_anchor/integer.h @@ -0,0 +1,25 @@ +/* + * \brief Helper functions for modifying integer values + * \author Martin Stein + * \date 2021-04-16 + */ + +/* + * Copyright (C) 2021 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _INTEGER_H_ +#define _INTEGER_H_ + +/* Genode includes */ +#include + +namespace Integer { + + Genode::uint64_t u64_swap_byte_order(Genode::uint64_t input); +} + +#endif /* _INTEGER_H_ */ diff --git a/repos/gems/src/lib/vfs/cbe_trust_anchor/vfs.cc b/repos/gems/src/lib/vfs/cbe_trust_anchor/vfs.cc index 9deaf20578..179c0d62da 100644 --- a/repos/gems/src/lib/vfs/cbe_trust_anchor/vfs.cc +++ b/repos/gems/src/lib/vfs/cbe_trust_anchor/vfs.cc @@ -20,89 +20,15 @@ /* OpenSSL includes */ #include -#include /* CBE includes */ #include -namespace Aes_cbc -{ - struct Iv - { - unsigned char values[16]; - }; - - void encrypt_without_iv(unsigned char *ciphertext_base, - size_t ciphertext_size, - unsigned char const *plaintext_base, - unsigned char const *key_base, - size_t key_size); - - void decrypt_without_iv(unsigned char *plaintext_base, - size_t plaintext_size, - unsigned char const *ciphertext_base, - unsigned char const *key_base, - size_t key_size); -} - - -/** - * Clean up crypto relevant data which would stay on the stack otherwise - */ -template -static void inline cleanup_crypto_data(T &t, S &s) -{ - Genode::memset(&t, 0, sizeof(t)); - Genode::memset(&s, 0, sizeof(s)); - - /* trigger compiler to not drop the memsets */ - asm volatile(""::"r"(&t),"r"(&s):"memory"); -} - - -void Aes_cbc::encrypt_without_iv(unsigned char *ciphertext_base, - size_t ciphertext_size, - unsigned char const *plaintext_base, - unsigned char const *key_base, - size_t key_size) -{ - AES_KEY aes_key; - if (AES_set_encrypt_key(key_base, key_size * 8, &aes_key)) { - class Failed_to_set_key { }; - throw Failed_to_set_key { }; - } - Aes_cbc::Iv iv { }; - Genode::memset(iv.values, 0, sizeof(iv.values)); - AES_cbc_encrypt( - plaintext_base, ciphertext_base, ciphertext_size, &aes_key, iv.values, - AES_ENCRYPT); - - cleanup_crypto_data(aes_key, iv); -} - - -void Aes_cbc::decrypt_without_iv(unsigned char *plaintext_base, - size_t plaintext_size, - unsigned char const *ciphertext_base, - unsigned char const *key_base, - size_t key_size) -{ - AES_KEY aes_key; - if (AES_set_decrypt_key(key_base, key_size * 8, &aes_key)) { - class Failed_to_set_key { }; - throw Failed_to_set_key { }; - } - Aes_cbc::Iv iv { }; - Genode::memset(iv.values, 0, sizeof(iv.values)); - AES_cbc_encrypt( - ciphertext_base, plaintext_base, plaintext_size, &aes_key, iv.values, - AES_DECRYPT); - - cleanup_crypto_data(aes_key, iv); -} - +/* local includes */ +#include enum { PRIVATE_KEY_SIZE = 32 }; +enum { PASSPHRASE_HASH_SIZE = 32 }; namespace Vfs_cbe_trust_anchor { @@ -215,7 +141,7 @@ class Trust_anchor Genode::memcpy( key_plaintext.value, _encrypt_key.value, Key::KEY_LEN); - Aes_cbc::encrypt_without_iv( + Aes_256::encrypt_with_zeroed_iv( _encrypt_key.value, Key::KEY_LEN, key_plaintext.value, @@ -247,7 +173,7 @@ class Trust_anchor Genode::memcpy( key_ciphertext.value, _decrypt_key.value, Key::KEY_LEN); - Aes_cbc::decrypt_without_iv( + Aes_256::decrypt_with_zeroed_iv( _decrypt_key.value, Key::KEY_LEN, key_ciphertext.value, @@ -336,21 +262,36 @@ class Trust_anchor if (!_read_key_file_finished()) { break; } - if (_key_io_job_buffer.size == PRIVATE_KEY_SIZE) { + if (_key_io_job_buffer.size == Aes_256_key_wrap::CIPHERTEXT_SIZE) { - Aes_cbc::decrypt_without_iv( + bool private_key_corrupt; + Aes_256_key_wrap::unwrap_key( _private_key.value, - PRIVATE_KEY_SIZE, + sizeof(_private_key.value), + private_key_corrupt, (unsigned char *)_key_io_job_buffer.base, + _key_io_job_buffer.size, (unsigned char *)_passphrase_hash_buffer.base, _passphrase_hash_buffer.size); - _job_state = Job_state::COMPLETE; - _job_success = true; + if (private_key_corrupt) { + + Genode::error("failed to unwrap the private key"); + _job_success = false; + + } else { + + _job_success = true; + } + _job_state = Job_state::COMPLETE; progress = true; } else { + Genode::error( + "content read from file 'encrypted_private_key' " + "has unexpected size"); + _job_state = Job_state::COMPLETE; _job_success = false; progress = true; @@ -396,11 +337,12 @@ class Trust_anchor _private_key_io_job_buffer.base, _private_key_io_job_buffer.size); - _key_io_job_buffer.size = PRIVATE_KEY_SIZE; - Aes_cbc::encrypt_without_iv( + _key_io_job_buffer.size = Aes_256_key_wrap::CIPHERTEXT_SIZE; + Aes_256_key_wrap::wrap_key( (unsigned char *)_key_io_job_buffer.base, _key_io_job_buffer.size, (unsigned char *)_private_key_io_job_buffer.base, + _private_key_io_job_buffer.size, (unsigned char *)_passphrase_hash_buffer.base, _passphrase_hash_buffer.size); @@ -643,7 +585,7 @@ class Trust_anchor struct Key_io_job_buffer : Util::Io_job::Buffer { - char buffer[PRIVATE_KEY_SIZE] { }; + char buffer[Aes_256_key_wrap::CIPHERTEXT_SIZE] { }; Key_io_job_buffer() { @@ -652,8 +594,19 @@ class Trust_anchor } }; - Key_io_job_buffer _key_io_job_buffer { }; - Key_io_job_buffer _passphrase_hash_buffer { }; + struct Passphrase_hash_buffer : Util::Io_job::Buffer + { + char buffer[PASSPHRASE_HASH_SIZE] { }; + + Passphrase_hash_buffer() + { + Buffer::base = buffer; + Buffer::size = sizeof (buffer); + } + }; + + Key_io_job_buffer _key_io_job_buffer { }; + Passphrase_hash_buffer _passphrase_hash_buffer { }; bool _check_key_file(Path const &path) {