Crypt module (#549)
- Moved cryptography code out of Tactility project and into a kernel module. - Converted C++ code to C style interface - Hardened security
This commit is contained in:
committed by
GitHub
parent
9d5993930d
commit
dfaeb9a2d9
@@ -10,6 +10,7 @@ list(APPEND REQUIRES_LIST
|
||||
TactilityFreeRtos
|
||||
hal-device-module
|
||||
lvgl-module
|
||||
crypt-module
|
||||
lv_screenshot
|
||||
minitar
|
||||
minmea
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <Tactility/settings/TimePrivate.h>
|
||||
|
||||
#include <tactility/concurrent/thread.h>
|
||||
#include <tactility/crypt_module.h>
|
||||
#include <tactility/drivers/grove.h>
|
||||
#include <tactility/drivers/uart_controller.h>
|
||||
#include <tactility/filesystem/file_system.h>
|
||||
@@ -331,6 +332,9 @@ void run(const Configuration& config, Module* dtsModules[], DtsDevice dtsDevices
|
||||
// hal-device-module
|
||||
check(module_construct_add_start(&hal_device_module) == ERROR_NONE);
|
||||
|
||||
// crypt-module
|
||||
check(module_construct_add_start(&crypt_module) == ERROR_NONE);
|
||||
|
||||
// Assign early so starting services can use it
|
||||
config_instance = &config;
|
||||
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
#include <Tactility/app/chat/ChatSettings.h>
|
||||
#include <Tactility/app/chat/ChatProtocol.h>
|
||||
|
||||
#include <Tactility/crypt/Crypt.h>
|
||||
#include <Tactility/file/File.h>
|
||||
#include <Tactility/file/PropertiesFile.h>
|
||||
#include <Tactility/Paths.h>
|
||||
|
||||
#include <tactility/crypt.h>
|
||||
|
||||
#include <esp_random.h>
|
||||
|
||||
#include <cstdlib>
|
||||
@@ -38,7 +39,7 @@ constexpr auto* KEY_CHAT_CHANNEL = "chatChannel";
|
||||
uint32_t defaultSenderId = 0;
|
||||
|
||||
// IV_SEED provides basic obfuscation for stored encryption keys, not strong encryption.
|
||||
// The device master key (from crypt::getIv) provides the actual security.
|
||||
// The device master key (from crypt_get_iv) provides the actual security.
|
||||
static constexpr auto* IV_SEED = "chat_key";
|
||||
|
||||
static std::string toHexString(const uint8_t* data, size_t length) {
|
||||
@@ -73,10 +74,10 @@ static bool readHex(const std::string& input, uint8_t* buffer, size_t length) {
|
||||
|
||||
static bool encryptKey(const uint8_t key[ESP_NOW_KEY_LEN], std::string& hexOutput) {
|
||||
uint8_t iv[16];
|
||||
crypt::getIv(IV_SEED, std::strlen(IV_SEED), iv);
|
||||
crypt_get_iv(IV_SEED, std::strlen(IV_SEED), iv);
|
||||
|
||||
uint8_t encrypted[ESP_NOW_KEY_LEN];
|
||||
if (crypt::encrypt(iv, key, encrypted, ESP_NOW_KEY_LEN) != 0) {
|
||||
if (crypt_encrypt(iv, key, encrypted, ESP_NOW_KEY_LEN) != 0) {
|
||||
LOG_E(TAG, "Failed to encrypt key");
|
||||
return false;
|
||||
}
|
||||
@@ -96,9 +97,9 @@ static bool decryptKey(const std::string& hexInput, uint8_t key[ESP_NOW_KEY_LEN]
|
||||
}
|
||||
|
||||
uint8_t iv[16];
|
||||
crypt::getIv(IV_SEED, std::strlen(IV_SEED), iv);
|
||||
crypt_get_iv(IV_SEED, std::strlen(IV_SEED), iv);
|
||||
|
||||
if (crypt::decrypt(iv, encrypted, key, ESP_NOW_KEY_LEN) != 0) {
|
||||
if (crypt_decrypt(iv, encrypted, key, ESP_NOW_KEY_LEN) != 0) {
|
||||
LOG_E(TAG, "Failed to decrypt key");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
#include <Tactility/service/wifi/WifiApSettings.h>
|
||||
#include <Tactility/file/PropertiesFile.h>
|
||||
|
||||
#include <Tactility/crypt/Crypt.h>
|
||||
#include <Tactility/file/File.h>
|
||||
|
||||
#include <tactility/crypt.h>
|
||||
|
||||
#include <Tactility/service/ServicePaths.h>
|
||||
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <iomanip>
|
||||
#include <ranges>
|
||||
@@ -56,60 +58,70 @@ static std::string getApPropertiesFilePath(std::shared_ptr<ServicePaths> paths,
|
||||
return std::format(AP_SETTINGS_FORMAT, paths->getUserDataDirectory(), ssid);
|
||||
}
|
||||
|
||||
static bool encrypt(const std::string& ssidInput, std::string& ssidOutput) {
|
||||
// The IV is derived from the SSID rather than the password/ciphertext, because the SSID is the one
|
||||
// value that's known and identical at both encrypt time (save) and decrypt time (load).
|
||||
static bool encrypt(const std::string& ssid, const std::string& plaintext, std::string& ciphertextOutput) {
|
||||
uint8_t iv[16];
|
||||
const auto length = ssidInput.size();
|
||||
const auto length = plaintext.size();
|
||||
constexpr size_t chunk_size = 16;
|
||||
const auto encrypted_length = ((length / chunk_size) + (length % chunk_size ? 1 : 0)) * chunk_size;
|
||||
|
||||
// crypt_encrypt reads encrypted_length bytes, but plaintext.c_str() only guarantees length + 1 bytes,
|
||||
// so pad the input into a zero-filled buffer of encrypted_length first to avoid reading past it.
|
||||
auto* padded_plaintext = static_cast<uint8_t*>(calloc(encrypted_length, 1));
|
||||
memcpy(padded_plaintext, plaintext.c_str(), length);
|
||||
|
||||
auto* buffer = static_cast<uint8_t*>(malloc(encrypted_length));
|
||||
|
||||
crypt::getIv(ssidInput.c_str(), ssidInput.size(), iv);
|
||||
if (crypt::encrypt(iv, reinterpret_cast<const uint8_t*>(ssidInput.c_str()), buffer, encrypted_length) != 0) {
|
||||
crypt_get_iv(ssid.c_str(), ssid.size(), iv);
|
||||
if (crypt_encrypt(iv, padded_plaintext, buffer, encrypted_length) != 0) {
|
||||
LOG_E(TAG, "Failed to encrypt");
|
||||
free(padded_plaintext);
|
||||
free(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
ssidOutput = toHexString(buffer, encrypted_length);
|
||||
free(padded_plaintext);
|
||||
ciphertextOutput = toHexString(buffer, encrypted_length);
|
||||
free(buffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decrypt(const std::string& ssidInput, std::string& ssidOutput) {
|
||||
assert(!ssidInput.empty());
|
||||
assert(ssidInput.size() % 2 == 0);
|
||||
auto* data = static_cast<uint8_t*>(malloc(ssidInput.size() / 2));
|
||||
if (!readHex(ssidInput, data, ssidInput.size() / 2)) {
|
||||
static bool decrypt(const std::string& ssid, const std::string& ciphertextInput, std::string& plaintextOutput) {
|
||||
assert(!ciphertextInput.empty());
|
||||
assert(ciphertextInput.size() % 2 == 0);
|
||||
auto* data = static_cast<uint8_t*>(malloc(ciphertextInput.size() / 2));
|
||||
if (!readHex(ciphertextInput, data, ciphertextInput.size() / 2)) {
|
||||
LOG_E(TAG, "Failed to read hex");
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t iv[16];
|
||||
crypt::getIv(ssidInput.c_str(), ssidInput.size(), iv);
|
||||
crypt_get_iv(ssid.c_str(), ssid.size(), iv);
|
||||
|
||||
auto result_length = ssidInput.size() / 2;
|
||||
auto result_length = ciphertextInput.size() / 2;
|
||||
// Allocate correct length plus space for string null terminator
|
||||
auto* result = static_cast<uint8_t*>(malloc(result_length + 1));
|
||||
result[result_length] = 0;
|
||||
|
||||
int decrypt_result = crypt::decrypt(
|
||||
int decrypt_result = crypt_decrypt(
|
||||
iv,
|
||||
data,
|
||||
result,
|
||||
ssidInput.size() / 2
|
||||
ciphertextInput.size() / 2
|
||||
);
|
||||
|
||||
free(data);
|
||||
|
||||
if (decrypt_result != 0) {
|
||||
LOG_E(TAG, "Failed to decrypt credentials for \"%ss\": %d", ssidInput.c_str(), decrypt_result);
|
||||
LOG_E(TAG, "Failed to decrypt credentials for \"%s\": %d", ssid.c_str(), decrypt_result);
|
||||
free(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
ssidOutput = reinterpret_cast<char*>(result);
|
||||
plaintextOutput = reinterpret_cast<char*>(result);
|
||||
free(result);
|
||||
return true;
|
||||
}
|
||||
@@ -151,7 +163,7 @@ bool load(const std::string& ssid, WifiApSettings& apSettings) {
|
||||
const auto& encrypted_password = map[AP_PROPERTIES_KEY_PASSWORD];
|
||||
if (encrypted_password.empty()) {
|
||||
apSettings.password = "";
|
||||
} else if (decrypt(encrypted_password, password_decrypted)) {
|
||||
} else if (decrypt(ssid, encrypted_password, password_decrypted)) {
|
||||
apSettings.password = password_decrypted;
|
||||
} else {
|
||||
return false;
|
||||
@@ -196,7 +208,7 @@ bool save(const WifiApSettings& apSettings) {
|
||||
|
||||
std::string password_encrypted;
|
||||
if (!apSettings.password.empty()) {
|
||||
if (!encrypt(apSettings.password, password_encrypted)) {
|
||||
if (!encrypt(apSettings.ssid, apSettings.password, password_encrypted)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user