libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
obfuscatedstring.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
13#include <cstddef>
14#include <cstdint>
15#include <promeki/namespace.h>
16#include <promeki/string.h>
17#include <promeki/fnv1a.h>
18
19PROMEKI_NAMESPACE_BEGIN
20
63template <size_t N, uint64_t Seed> class ObfuscatedString {
64 public:
74 consteval ObfuscatedString(const char (&str)[N]) : _data{} {
75 uint64_t state = initState();
76 for (size_t i = 0; i < N - 1; ++i) {
77 uint64_t key = keyAt(i);
78 unsigned char k0 = static_cast<unsigned char>(key);
79 unsigned char k1 = static_cast<unsigned char>(key >> 8);
80 unsigned char c = static_cast<unsigned char>(str[i]);
81
82 // Round 1: XOR with position-derived key byte.
83 c ^= k0;
84 // Round 2: modular add of CBC state byte.
85 c = static_cast<unsigned char>(c + static_cast<unsigned char>(state));
86 // Round 3: rotate left by 1-7 bits (derived from second key byte).
87 unsigned shift = (k1 & 0x07) | 1;
88 c = static_cast<unsigned char>((c << shift) | (c >> (8 - shift)));
89 // Round 4: XOR with second key byte.
90 c ^= k1;
91
92 _data[i] = static_cast<char>(c);
93 state = advanceState(state, c, key);
94 }
95 }
96
101 String decode() const {
102 String out(N - 1, '\0');
103 uint64_t state = initState();
104 for (size_t i = 0; i < N - 1; ++i) {
105 uint64_t key = keyAt(i);
106 unsigned char k0 = static_cast<unsigned char>(key);
107 unsigned char k1 = static_cast<unsigned char>(key >> 8);
108 unsigned char c = static_cast<unsigned char>(_data[i]);
109
110 // Undo round 4: XOR with second key byte.
111 c ^= k1;
112 // Undo round 3: rotate right.
113 unsigned shift = (k1 & 0x07) | 1;
114 c = static_cast<unsigned char>((c >> shift) | (c << (8 - shift)));
115 // Undo round 2: subtract CBC state byte.
116 c = static_cast<unsigned char>(c - static_cast<unsigned char>(state));
117 // Undo round 1: XOR.
118 c ^= k0;
119
120 out.setCharAt(i, Char(static_cast<char>(c)));
121 state = advanceState(state, static_cast<unsigned char>(_data[i]), key);
122 }
123 return out;
124 }
125
130 operator String() const { return decode(); }
131
132 private:
133 // Initial 64-bit CBC state derived from the full seed.
134 static constexpr uint64_t initState() {
135 uint64_t h = Seed * 0xbf58476d1ce4e5b9ULL;
136 h ^= h >> 31;
137 h *= 0x94d049bb133111ebULL;
138 h ^= h >> 31;
139 return h;
140 }
141
142 // Per-byte 64-bit key derived from Seed and position (splitmix64 finalizer).
143 static constexpr uint64_t keyAt(size_t i) {
144 uint64_t h =
145 (Seed ^ (static_cast<uint64_t>(i) * 0x9e3779b97f4a7c15ULL)) * 0xbf58476d1ce4e5b9ULL;
146 h ^= h >> 31;
147 h *= 0x94d049bb133111ebULL;
148 h ^= h >> 31;
149 return h;
150 }
151
152 // Advance the 64-bit CBC state using ciphertext and key.
153 static constexpr uint64_t advanceState(uint64_t state, unsigned char cipher, uint64_t key) {
154 state ^= static_cast<uint64_t>(cipher) | (key << 8);
155 state *= 0x517cc1b727220a95ULL;
156 state ^= state >> 29;
157 return state;
158 }
159
160 char _data[N - 1];
161};
162
163PROMEKI_NAMESPACE_END
164
165// NOLINTNEXTLINE(bugprone-macro-parentheses)
166
171#define PROMEKI_OBFUSCATE_SEED \
172 (::promeki::fnv1a(__FILE__) ^ (static_cast<uint64_t>(__LINE__) * 0x9e3779b97f4a7c15ULL) ^ \
173 ::promeki::fnv1a(__DATE__ __TIME__))
174
184// NOLINTNEXTLINE(bugprone-macro-parentheses)
185#define PROMEKI_OBFUSCATE(str) \
186 ([]() { \
187 static constexpr auto _obf = ::promeki::ObfuscatedString<sizeof(str), PROMEKI_OBFUSCATE_SEED>(str); \
188 return _obf.decode(); \
189 }())
190
191#endif // PROMEKI_ENABLE_CORE