diff --git a/CMakeLists.txt b/CMakeLists.txt index b51961a..367017e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ endif() message(STATUS "CXX Namespace: ${SFRAME_CXX_NAMESPACE}") message(STATUS "CMake Export Namespace: ${SFRAME_EXPORT_NAMESPACE}") -# Use -DCRYPTO=(OPENSSL_1_1 | OPENSSL_3 | BORINGSSL) to configure crypto +# Use -DCRYPTO=(OPENSSL_1_1 | OPENSSL_3 | BORINGSSL | STM32) to configure crypto if(NOT DEFINED CRYPTO) set(CRYPTO "OPENSSL_3") endif() @@ -92,8 +92,13 @@ elseif(${CRYPTO} STREQUAL "BORINGSSL") find_package(OpenSSL REQUIRED) add_compile_definitions(BORINGSSL) set(CRYPTO_LIB OpenSSL::Crypto) +elseif(${CRYPTO} STREQUAL "STM32") + message(STATUS "Configuring with STM32") + find_package(STM32Cryptographic REQUIRED) + add_compile_definitions(STM32 NO_ALLOC) + set(CRYPTO_LIB STM32Cryptographic) else() - message(FATAL_ERROR "Please select a crypto back-end (OPENSSL_1_1 or OPENSSL_3) [${CRYPTO}]") + message(FATAL_ERROR "Please select a crypto back-end (OPENSSL_1_1, OPENSSL_3, BORINGSSL, STM32) [${CRYPTO}]") endif() diff --git a/include/sframe/sframe.h b/include/sframe/sframe.h index dcceb72..5179fae 100644 --- a/include/sframe/sframe.h +++ b/include/sframe/sframe.h @@ -29,6 +29,8 @@ namespace SFRAME_NAMESPACE { struct crypto_error : std::runtime_error { crypto_error(); + crypto_error(std::size_t err_code); + crypto_error(const std::string& err_str); }; struct unsupported_ciphersuite_error : std::runtime_error diff --git a/src/crypto_boringssl.cpp b/src/crypto_boringssl.cpp index 4bb6ba3..b3404f3 100644 --- a/src/crypto_boringssl.cpp +++ b/src/crypto_boringssl.cpp @@ -20,6 +20,16 @@ crypto_error::crypto_error() { } +crypto_error::crypto_error(std::size_t err_code) + : std::runtime_error(ERR_error_string(err_code, nullptr)) +{ +} + +crypto_error::crypto_error(const std::string& err_str) + : std::runtime_error(err_str) +{ +} + static const EVP_MD* openssl_digest_type(CipherSuite suite) { diff --git a/src/crypto_openssl11.cpp b/src/crypto_openssl11.cpp index d68e841..4373086 100644 --- a/src/crypto_openssl11.cpp +++ b/src/crypto_openssl11.cpp @@ -26,6 +26,16 @@ crypto_error::crypto_error() { } +crypto_error::crypto_error(std::size_t err_code) + : std::runtime_error(ERR_error_string(err_code, nullptr)) +{ +} + +crypto_error::crypto_error(const std::string& err_str) + : std::runtime_error(err_str) +{ +} + static const EVP_MD* openssl_digest_type(CipherSuite suite) { diff --git a/src/crypto_openssl3.cpp b/src/crypto_openssl3.cpp index d1bda30..468a558 100644 --- a/src/crypto_openssl3.cpp +++ b/src/crypto_openssl3.cpp @@ -20,6 +20,16 @@ crypto_error::crypto_error() { } +crypto_error::crypto_error(std::size_t err_code) + : std::runtime_error(ERR_error_string(err_code, nullptr)) +{ +} + +crypto_error::crypto_error(const std::string& err_str) + : std::runtime_error(err_str) +{ +} + static const EVP_CIPHER* openssl_cipher(CipherSuite suite) { diff --git a/src/crypto_stm32.cpp b/src/crypto_stm32.cpp new file mode 100644 index 0000000..193c0ce --- /dev/null +++ b/src/crypto_stm32.cpp @@ -0,0 +1,276 @@ +#if defined(STM32) + +#include "crypto.h" +#include "header.h" + +#include +#include +#include +#include + +#include + +namespace sframe { + +#define VERIFY_CMOX_CALL(func, success_code) \ + do { \ + const auto retval = func; \ + if (retval != success_code) { \ + throw crypto_error(retval); \ + } \ + } while (0) + +/// +/// Convert between native identifiers / errors and cmox ones +/// + +crypto_error::crypto_error() + : std::runtime_error("unknown CMOX crypto error") +{ +} + +crypto_error::crypto_error(std::size_t err_code) + : std::runtime_error( + "CMOX crypto error (error_code=" + std::to_string(err_code) + ")") +{ +} + +crypto_error::crypto_error(const std::string& err_str) + : std::runtime_error("CMOX crypto error (error=" + err_str + ")") +{ +} + +static cmox_mac_algo_t +cmox_hmac_algo(CipherSuite suite) +{ + switch (suite) { + case CipherSuite::AES_128_CTR_HMAC_SHA256_80: + case CipherSuite::AES_128_CTR_HMAC_SHA256_64: + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: + case CipherSuite::AES_GCM_128_SHA256: + return CMOX_HMAC_SHA256_ALGO; + case CipherSuite::AES_GCM_256_SHA512: + return CMOX_HMAC_SHA512_ALGO; + default: + throw unsupported_ciphersuite_error(); + } +} + +static cmox_hmac_impl_t +cmox_hmac_impl(CipherSuite suite) +{ + switch (suite) { + case CipherSuite::AES_128_CTR_HMAC_SHA256_80: + case CipherSuite::AES_128_CTR_HMAC_SHA256_64: + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: + case CipherSuite::AES_GCM_128_SHA256: + return CMOX_HMAC_SHA256; + case CipherSuite::AES_GCM_256_SHA512: + return CMOX_HMAC_SHA512; + default: + throw unsupported_ciphersuite_error(); + } +} + +static std::size_t +cmox_hmac_size(CipherSuite suite) +{ + switch (suite) { + case CipherSuite::AES_128_CTR_HMAC_SHA256_80: + case CipherSuite::AES_128_CTR_HMAC_SHA256_64: + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: + case CipherSuite::AES_GCM_128_SHA256: + return CMOX_SHA256_SIZE; + case CipherSuite::AES_GCM_256_SHA512: + return CMOX_SHA512_SIZE; + default: + throw unsupported_ciphersuite_error(); + } +} + +/// +/// HKDF +/// + +owned_bytes +hkdf_extract(CipherSuite suite, input_bytes salt, input_bytes ikm) +{ + std::size_t computed_size = 0; + const auto hmac_size = cmox_hmac_size(suite); + auto out = owned_bytes(hmac_size); + VERIFY_CMOX_CALL(cmox_mac_compute(cmox_hmac_algo(suite), + ikm.data(), + ikm.size(), + salt.data(), + salt.size(), + nullptr, + 0, + out.data(), + out.size(), + &computed_size), + CMOX_MAC_SUCCESS); + + return out; +} + +owned_bytes +hkdf_expand(CipherSuite suite, input_bytes prk, input_bytes info, size_t size) +{ + cmox_hmac_handle_t Hmac_Ctx; + cmox_mac_handle_t* mac_ctx; + + const auto digest_size = cmox_hmac_size(suite); + auto N = static_cast((size + digest_size - 1) / digest_size); + + mac_ctx = cmox_hmac_construct(&Hmac_Ctx, cmox_hmac_impl(suite)); + if (mac_ctx == nullptr) { + throw crypto_error(); + } + + owned_bytes computed_hash(digest_size); + owned_bytes out(size); + + uint32_t index = 0; + for (uint8_t i = 1; i <= N; i++) { + computed_hash.resize(0); + + VERIFY_CMOX_CALL(cmox_mac_init(mac_ctx), CMOX_MAC_SUCCESS); + + VERIFY_CMOX_CALL(cmox_mac_setKey(mac_ctx, prk.data(), prk.size()), + CMOX_MAC_SUCCESS); + + VERIFY_CMOX_CALL(cmox_mac_append(mac_ctx, info.data(), info.size()), + CMOX_MAC_SUCCESS); + + VERIFY_CMOX_CALL(cmox_mac_append(mac_ctx, &i, 1), CMOX_MAC_SUCCESS); + + computed_hash.resize(digest_size); + VERIFY_CMOX_CALL( + cmox_mac_generateTag(mac_ctx, computed_hash.data(), nullptr), + CMOX_MAC_SUCCESS); + + const auto to_copy = (i == N) ? size - index : digest_size; + std::memcpy(&out[index], computed_hash.data(), to_copy); + index += to_copy; + } + + VERIFY_CMOX_CALL(cmox_mac_cleanup(mac_ctx), CMOX_MAC_SUCCESS); + + return out; +} + +static output_bytes +seal_aead(CipherSuite suite, + input_bytes key, + input_bytes nonce, + output_bytes ct, + input_bytes aad, + input_bytes pt) +{ + auto tag_size = cipher_overhead(suite); + if (ct.size() < pt.size() + tag_size) { + throw buffer_too_small_error("Ciphertext buffer too small"); + } + + std::size_t computed_size = 0; + VERIFY_CMOX_CALL(cmox_aead_encrypt(CMOX_AESFAST_GCMFAST_ENC_ALGO, + pt.data(), + pt.size(), + tag_size, + key.data(), + key.size(), + nonce.data(), + nonce.size(), + aad.data(), + aad.size(), + ct.data(), + &computed_size), + CMOX_CIPHER_SUCCESS); + + return ct.subspan(0, computed_size); +} + +output_bytes +seal(CipherSuite suite, + input_bytes key, + input_bytes nonce, + output_bytes ct, + input_bytes aad, + input_bytes pt) +{ + switch (suite) { + case CipherSuite::AES_128_CTR_HMAC_SHA256_80: + case CipherSuite::AES_128_CTR_HMAC_SHA256_64: + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: { + // TODO(GhostofCookie): return seal_ctr(suite, key, nonce, ct, aad, pt); + throw unsupported_ciphersuite_error(); + } + + case CipherSuite::AES_GCM_128_SHA256: + case CipherSuite::AES_GCM_256_SHA512: { + return seal_aead(suite, key, nonce, ct, aad, pt); + } + } + + throw unsupported_ciphersuite_error(); +} + +static output_bytes +open_aead(CipherSuite suite, + input_bytes key, + input_bytes nonce, + output_bytes pt, + input_bytes aad, + input_bytes ct) +{ + auto tag_size = cipher_overhead(suite); + if (ct.size() < tag_size) { + throw buffer_too_small_error("Ciphertext buffer too small"); + } + + std::size_t computed_size = 0; + VERIFY_CMOX_CALL(cmox_aead_decrypt(CMOX_AESFAST_GCMFAST_DEC_ALGO, + ct.data(), + ct.size(), + tag_size, + key.data(), + key.size(), + nonce.data(), + nonce.size(), + aad.data(), + aad.size(), + pt.data(), + &computed_size), + CMOX_CIPHER_AUTH_SUCCESS); + + return pt.subspan(0, computed_size); +} + +output_bytes +open(CipherSuite suite, + input_bytes key, + input_bytes nonce, + output_bytes pt, + input_bytes aad, + input_bytes ct) +{ + switch (suite) { + case CipherSuite::AES_128_CTR_HMAC_SHA256_80: + case CipherSuite::AES_128_CTR_HMAC_SHA256_64: + case CipherSuite::AES_128_CTR_HMAC_SHA256_32: { + // TODO(GhostofCookie): return open_ctr(suite, key, nonce, pt, aad, ct); + throw unsupported_ciphersuite_error(); + } + + case CipherSuite::AES_GCM_128_SHA256: + case CipherSuite::AES_GCM_256_SHA512: { + return open_aead(suite, key, nonce, pt, aad, ct); + } + } + + throw unsupported_ciphersuite_error(); +} + +} // namespace sframe + +#endif diff --git a/src/sframe.cpp b/src/sframe.cpp index 7616bc1..2db60e9 100644 --- a/src/sframe.cpp +++ b/src/sframe.cpp @@ -71,7 +71,7 @@ KeyRecord::from_base_key(CipherSuite suite, auto key_size = cipher_key_size(suite); auto nonce_size = cipher_nonce_size(suite); - const auto empty_byte_string = owned_bytes<0>(); + const auto empty_byte_string = owned_bytes<1>(0); const auto key_label = sframe_key_label(suite, key_id); const auto salt_label = sframe_salt_label(suite, key_id);