A fork of https://github.com/crosspoint-reader/crosspoint-reader
1#include "ObfuscationUtils.h"
2
3#include <Logging.h>
4#include <base64.h>
5#include <esp_mac.h>
6#include <mbedtls/base64.h>
7
8#include <cstring>
9
10namespace obfuscation {
11
12namespace {
13constexpr size_t HW_KEY_LEN = 6;
14
15// Simple lazy init — no thread-safety concern on single-core ESP32-C3.
16const uint8_t* getHwKey() {
17 static uint8_t key[HW_KEY_LEN] = {};
18 static bool initialized = false;
19 if (!initialized) {
20 esp_efuse_mac_get_default(key);
21 initialized = true;
22 }
23 return key;
24}
25} // namespace
26
27void xorTransform(std::string& data) {
28 const uint8_t* key = getHwKey();
29 for (size_t i = 0; i < data.size(); i++) {
30 data[i] ^= key[i % HW_KEY_LEN];
31 }
32}
33
34void xorTransform(std::string& data, const uint8_t* key, size_t keyLen) {
35 if (keyLen == 0 || key == nullptr) return;
36 for (size_t i = 0; i < data.size(); i++) {
37 data[i] ^= key[i % keyLen];
38 }
39}
40
41String obfuscateToBase64(const std::string& plaintext) {
42 if (plaintext.empty()) return "";
43 std::string temp = plaintext;
44 xorTransform(temp);
45 return base64::encode(reinterpret_cast<const uint8_t*>(temp.data()), temp.size());
46}
47
48std::string deobfuscateFromBase64(const char* encoded, bool* ok) {
49 if (encoded == nullptr || encoded[0] == '\0') {
50 if (ok) *ok = false;
51 return "";
52 }
53 if (ok) *ok = true;
54 size_t encodedLen = strlen(encoded);
55 // First call: get required output buffer size
56 size_t decodedLen = 0;
57 int ret = mbedtls_base64_decode(nullptr, 0, &decodedLen, reinterpret_cast<const unsigned char*>(encoded), encodedLen);
58 if (ret != 0 && ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
59 LOG_ERR("OBF", "Base64 decode size query failed (ret=%d)", ret);
60 if (ok) *ok = false;
61 return "";
62 }
63 std::string result(decodedLen, '\0');
64 ret = mbedtls_base64_decode(reinterpret_cast<unsigned char*>(&result[0]), decodedLen, &decodedLen,
65 reinterpret_cast<const unsigned char*>(encoded), encodedLen);
66 if (ret != 0) {
67 LOG_ERR("OBF", "Base64 decode failed (ret=%d)", ret);
68 if (ok) *ok = false;
69 return "";
70 }
71 result.resize(decodedLen);
72 xorTransform(result);
73 return result;
74}
75
76void selfTest() {
77 const char* testInputs[] = {"", "hello", "WiFi P@ssw0rd!", "a"};
78 bool allPassed = true;
79 for (const char* input : testInputs) {
80 String encoded = obfuscateToBase64(std::string(input));
81 std::string decoded = deobfuscateFromBase64(encoded.c_str());
82 if (decoded != input) {
83 LOG_ERR("OBF", "FAIL: \"%s\" -> \"%s\" -> \"%s\"", input, encoded.c_str(), decoded.c_str());
84 allPassed = false;
85 }
86 }
87 // Verify obfuscated form differs from plaintext
88 String enc = obfuscateToBase64("test123");
89 if (enc == "test123") {
90 LOG_ERR("OBF", "FAIL: obfuscated output identical to plaintext");
91 allPassed = false;
92 }
93 if (allPassed) {
94 LOG_DBG("OBF", "Obfuscation self-test PASSED");
95 }
96}
97
98} // namespace obfuscation