Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
run: build/Tests/Tactility/TactilityTests
- name: "Run TactilityKernel Tests"
run: build/Tests/TactilityKernel/TactilityKernelTests
- name: "Run CryptModuleTests Tests"
run: build/Tests/crypt-module/CryptModuleTests
DevicetreeTests:
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

build/
buildsim/
build-*/
cmake-*/
CMakeCache.txt
*.cbp
Expand Down
1 change: 1 addition & 0 deletions Buildscripts/release-sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ def main():

# Modules
add_module(target_path, "lvgl-module")
add_module(target_path, "crypt-module")

# Drivers
add_driver(target_path, "bm8563-module")
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
add_subdirectory(Libraries/minmea)
add_subdirectory(Modules/hal-device-module)
add_subdirectory(Modules/lvgl-module)
add_subdirectory(Modules/crypt-module)

# FreeRTOS
set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/Devices/simulator/Source CACHE STRING "")
Expand Down
1 change: 1 addition & 0 deletions Firmware/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ else ()
TactilityFreeRtos
hal-device-module
lvgl-module
crypt-module
SDL2::SDL2-static
SDL2-static
)
Expand Down
24 changes: 24 additions & 0 deletions Modules/crypt-module/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.20)

include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake")

file(GLOB_RECURSE SOURCE_FILES "source/*.c*")

list(APPEND REQUIRES_LIST
TactilityKernel
mbedtls
)

if (DEFINED ENV{ESP_IDF_VERSION})
list(APPEND REQUIRES_LIST
nvs_flash
esp_hw_support
esp_rom
)
endif ()
Comment thread
KenVanHoeylandt marked this conversation as resolved.

tactility_add_module(crypt-module
SRCS ${SOURCE_FILES}
INCLUDE_DIRS include/
REQUIRES ${REQUIRES_LIST}
)
85 changes: 85 additions & 0 deletions Modules/crypt-module/include/tactility/crypt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: Apache-2.0
/** @file crypt.h
*
* @brief Encryption helper functions.
*
* Offers AES 256 CBC encryption with built-in key.
* The key is built from data including:
* - the internal factory MAC address
* - random data stored in NVS
*
* It's important to use flash encryption to avoid an attacker to get
* access to your encrypted data. If flash encryption is disabled,
* someone can fetch the key from the partitions.
*
* See:
* https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/secure-boot-v2.html
* https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/flash-encryption.html
*/
#pragma once

#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Deterministically derives an IV from the given data (the first 16 bytes of its SHA-256 hash).
*
* Calling this again with the same data always produces the same IV - use this when there's nowhere
* to store a per-encryption IV alongside the ciphertext, and the caller can supply the same associated
* data (e.g. an identifier that's known at both encrypt and decrypt time, not the secret itself) on both
* ends. Because the IV doesn't change between encryptions with the same associated data, encrypting the
* same plaintext twice produces the same ciphertext - prefer crypt_generate_iv() when ciphertext can be
* stored alongside a random IV instead.
*
* @param[in] data input data
* @param[in] dataLength input data length
* @param[out] iv output IV
*/
void crypt_get_iv(const void* data, size_t dataLength, uint8_t iv[16]);

/**
* @brief Fills the IV with cryptographically secure random bytes.
*
* Use this when the IV can be stored alongside the ciphertext (e.g. prefixed to it) and read back for
* decryption. This gives every encryption operation a unique, unpredictable IV, which is the standard
* and strongest way to use crypt_encrypt()/crypt_decrypt().
*
* @param[out] iv output IV
*/
void crypt_generate_iv(uint8_t iv[16]);

/**
* @brief Encrypt data.
*
* Important: Use flash encryption to increase security.
* Important: input and output data must be aligned to 16 bytes.
*
* @param[in] iv the AES IV
* @param[in] inData input data
* @param[out] outData output data
* @param[in] dataLength data length, a multiple of 16 (for both inData and outData)
* @return the result of esp_aes_crypt_cbc() (MBEDTLS_ERR_*), or -1 if dataLength is not a positive multiple of 16
*/
int crypt_encrypt(const uint8_t iv[16], const uint8_t* inData, uint8_t* outData, size_t dataLength);

/**
* @brief Decrypt data.
*
* Important: Use flash encryption to increase security.
* Important: input and output data must be aligned to 16 bytes.
*
* @param[in] iv AES IV
* @param[in] inData input data
* @param[out] outData output data
* @param[in] dataLength data length, a multiple of 16 (for both inData and outData)
* @return the result of esp_aes_crypt_cbc() (MBEDTLS_ERR_*), or -1 if dataLength is not a positive multiple of 16
*/
int crypt_decrypt(const uint8_t iv[16], const uint8_t* inData, uint8_t* outData, size_t dataLength);

#ifdef __cplusplus
}
#endif
12 changes: 12 additions & 0 deletions Modules/crypt-module/include/tactility/crypt_module.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once

#ifdef __cplusplus
extern "C" {
#endif

extern struct Module crypt_module;

#ifdef __cplusplus
}
#endif
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include <cstddef>
#include <cstdint>
#include <stddef.h>
#include <stdint.h>

namespace tt::crypt {
#ifdef __cplusplus
extern "C" {
#endif

/**
* Implementation of DJB2 hashing algorithm.
* @param[in] str the string to calculate the hash for
* @return the hash
*/
uint32_t djb2(const char* str);
uint32_t djb2_str(const char* str);

/**
* Implementation of DJB2 hashing algorithm.
* @param[in] data the bytes to calculate the hash for
* @param[in] length the size of data
* @return the hash
*/
uint32_t djb2(const void* data, size_t length);
uint32_t djb2_data(const void* data, size_t length);

} // namespace
#ifdef __cplusplus
}
#endif
Original file line number Diff line number Diff line change
@@ -1,24 +1,45 @@
#include <Tactility/crypt/Crypt.h>
// SPDX-License-Identifier: Apache-2.0
#include <tactility/crypt.h>

#include <tactility/log.h>
#include <tactility/check.h>
#include <tactility/log.h>

#include <mbedtls/aes.h>
#include <mbedtls/platform_util.h>
#include <mbedtls/sha256.h>
#include <cstring>
#include <cstdint>

#ifdef ESP_PLATFORM
#include "esp_cpu.h"
#include "esp_mac.h"
#include "esp_random.h"
#include "nvs_flash.h"
#else
#include <random>
#endif

namespace tt::crypt {

constexpr auto* TAG = "Crypt";
constexpr auto* TAG = "crypt";

#define TT_NVS_NAMESPACE "tt_secure"

/**
* Fills a buffer with cryptographically secure random bytes.
* @param[out] out output buffer
* @param[in] length number of bytes to fill
*/
static void fill_random(uint8_t* out, size_t length) {
#ifdef ESP_PLATFORM
esp_fill_random(out, length);
#else
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_int_distribution<int> dist(0, 255);
for (size_t i = 0; i < length; ++i) {
out[i] = static_cast<uint8_t>(dist(gen));
}
#endif
}

#ifdef ESP_PLATFORM
/**
* Get a key based on hardware parameters.
Expand All @@ -28,7 +49,7 @@ static void get_hardware_key(uint8_t key[32]) {
uint8_t mac[8];
// MAC can be 6 or 8 bytes
size_t mac_length = esp_mac_addr_len_get(ESP_MAC_EFUSE_FACTORY);
LOG_I(TAG, "Using MAC with length %u", (unsigned)mac_length);
LOG_I(TAG, "Using MAC with length %zu", mac_length);
check(mac_length <= 8);
ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_EFUSE_FACTORY));

Expand Down Expand Up @@ -73,17 +94,12 @@ static void get_nvs_key(uint8_t key[32]) {

size_t length = 32;
if (nvs_get_blob(handle, "key", key, &length) == ESP_OK) {
LOG_I(TAG, "Fetched key from NVS (%u bytes)", (unsigned)length);
LOG_I(TAG, "Fetched key from NVS (%zu bytes)", length);
check(length == 32);
} else {
// TODO: Improved randomness
esp_cpu_cycle_count_t cycle_count = esp_cpu_get_cycle_count();
auto seed = cycle_count;
srand(seed);
for (int i = 0; i < 32; ++i) {
key[i] = (uint8_t)(rand());
}
fill_random(key, 32);
ESP_ERROR_CHECK(nvs_set_blob(handle, "key", key, 32));
ESP_ERROR_CHECK(nvs_commit(handle));
LOG_I(TAG, "Stored new key in NVS");
}
Comment thread
KenVanHoeylandt marked this conversation as resolved.

Expand All @@ -99,7 +115,7 @@ static void get_nvs_key(uint8_t key[32]) {
* @param[in] length data length (all buffers must be at least this size)
*/
static void xorKey(const uint8_t* inLeft, const uint8_t* inRight, uint8_t* out, size_t length) {
for (int i = 0; i < length; ++i) {
for (size_t i = 0; i < length; ++i) {
out[i] = inLeft[i] ^ inRight[i];
}
}
Expand All @@ -121,19 +137,23 @@ static void getKey(uint8_t key[32]) {
get_hardware_key(hardware_key);
get_nvs_key(nvs_key);
xorKey(hardware_key, nvs_key, key, 32);
mbedtls_platform_zeroize(hardware_key, sizeof(hardware_key));
mbedtls_platform_zeroize(nvs_key, sizeof(nvs_key));
#else
LOG_W(TAG, "Using unsafe key for debugging purposes.");
memset(key, 0, 32);
#endif
}

void getIv(const void* data, size_t dataLength, uint8_t iv[16]) {
memset(iv, 0, 16);
auto* data_bytes = (uint8_t*)data;
for (int i = 0; i < dataLength; ++i) {
size_t safe_index = i % 16;
iv[safe_index] %= data_bytes[i];
}
void crypt_get_iv(const void* data, size_t dataLength, uint8_t iv[16]) {
uint8_t hash[32];
mbedtls_sha256(static_cast<const unsigned char*>(data), dataLength, hash, 0);
memcpy(iv, hash, 16);
mbedtls_platform_zeroize(hash, sizeof(hash));
}

void crypt_generate_iv(uint8_t iv[16]) {
fill_random(iv, 16);
}

static int aes256CryptCbc(
Expand Down Expand Up @@ -162,28 +182,30 @@ static int aes256CryptCbc(
return result;
}

int encrypt(const uint8_t iv[16], const uint8_t* inData, uint8_t* outData, size_t dataLength) {
check(dataLength % 16 == 0, "Length is not a multiple of 16 bytes (for AES 256)");
int crypt_encrypt(const uint8_t iv[16], const uint8_t* inData, uint8_t* outData, size_t dataLength) {
uint8_t key[32];
getKey(key);

// TODO: Is this still needed after switching to regular AES functions?
uint8_t iv_copy[16];
memcpy(iv_copy, iv, sizeof(iv_copy));

return aes256CryptCbc(key, MBEDTLS_AES_ENCRYPT, dataLength, iv_copy, inData, outData);
int result = aes256CryptCbc(key, MBEDTLS_AES_ENCRYPT, dataLength, iv_copy, inData, outData);
mbedtls_platform_zeroize(key, sizeof(key));
mbedtls_platform_zeroize(iv_copy, sizeof(iv_copy));
return result;
}

int decrypt(const uint8_t iv[16], const uint8_t* inData, uint8_t* outData, size_t dataLength) {
check(dataLength % 16 == 0, "Length is not a multiple of 16 bytes (for AES 256)");
int crypt_decrypt(const uint8_t iv[16], const uint8_t* inData, uint8_t* outData, size_t dataLength) {
uint8_t key[32];
getKey(key);

// TODO: Is this still needed after switching to regular AES functions?
uint8_t iv_copy[16];
memcpy(iv_copy, iv, sizeof(iv_copy));

return aes256CryptCbc(key, MBEDTLS_AES_DECRYPT, dataLength, iv_copy, inData, outData);
int result = aes256CryptCbc(key, MBEDTLS_AES_DECRYPT, dataLength, iv_copy, inData, outData);
mbedtls_platform_zeroize(key, sizeof(key));
mbedtls_platform_zeroize(iv_copy, sizeof(iv_copy));
return result;
}

} // namespace
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#include "Tactility/crypt/Hash.h"
// SPDX-License-Identifier: Apache-2.0
#include <tactility/hash.h>

namespace tt::crypt {

uint32_t djb2(const char* str) {
uint32_t djb2_str(const char* str) {
uint32_t hash = 5381;
char c = (char)*str++;
while (c != 0) {
Expand All @@ -12,7 +11,7 @@ uint32_t djb2(const char* str) {
return hash;
}

uint32_t djb2(const void* data, size_t length) {
uint32_t djb2_data(const void* data, size_t length) {
uint32_t hash = 5381;
auto* data_bytes = static_cast<const uint8_t*>(data);
uint8_t c = *data_bytes++;
Expand All @@ -24,5 +23,3 @@ uint32_t djb2(const void* data, size_t length) {
}
return hash;
}

} // namespace
Loading
Loading