diff --git a/src/misc/strobf.h b/src/misc/strobf.h index 68c5420..34c2ac7 100644 --- a/src/misc/strobf.h +++ b/src/misc/strobf.h @@ -2,11 +2,12 @@ #include #include #include -#include namespace str_obf { + constexpr static auto max_key_power = 6; /* 64 bytes max */ + namespace internal { - template + template struct message { /* helper to access the types */ static constexpr auto _size = size; @@ -14,33 +15,33 @@ namespace str_obf { using _key_t = key_t; /* memory */ - std::array buffer{0}; + std::array buffer{}; key_t key{}; /* some memory access helpers */ - std::string_view string_view() const noexcept { return {this->buffer.begin(), this->length}; } - std::string string() const { return {this->buffer.begin(), this->length}; } - const char* c_str() const noexcept { return &this->buffer[0]; } + [[nodiscard]] std::string_view string_view() const noexcept { return {this->buffer.begin(), this->length}; } + [[nodiscard]] std::string string() const { return {this->buffer.begin(), this->length}; } + [[nodiscard]] const char* c_str() const noexcept { return &this->buffer[0]; } }; constexpr auto time_seed() noexcept { std::uint64_t shifted = 0; - for( const auto c : __TIME__ ) + for(const auto c : __TIME__) { - shifted <<= 8; - shifted |= c; + shifted <<= 8U; + shifted |= (unsigned) c; } return shifted; } constexpr uint64_t string_hash(const char* str, int h = 0) noexcept { - return !str[h] ? 5381 : (string_hash(str, h + 1) * 33) ^ str[h]; + return !str[h] ? 5381 : (unsigned) (string_hash(str, h + 1) * 33) ^ (unsigned) str[h]; } #ifdef WIN32 - #pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned +#pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned #endif constexpr std::uint32_t rng32_next(std::uint64_t& state, const std::uint32_t& inc) noexcept { std::uint64_t oldstate = state; @@ -52,47 +53,34 @@ namespace str_obf { return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); } #ifdef WIN32 - #pragma warning(default: 4146) // unary minus operator applied to unsigned type, result still unsigned +#pragma warning(default: 4146) // unary minus operator applied to unsigned type, result still unsigned #endif /* we use a buffer dividable by 8 so the compiler could do crazy shit, when loading (moving) the characters */ - constexpr size_t recommand_message_buffer(size_t message_size) noexcept { + constexpr std::uint64_t recommand_message_buffer(std::uint64_t message_size) noexcept { + if(message_size <= 4) return 4; /* we could use the eax register here */ return (message_size & 0xFFFFFFF8) + ((message_size & 0x7) > 0 ? 8 : 0); } } + inline void _invalid_key_size() {} + template - constexpr inline void crypt(char_t* begin, size_t length, const key_t& key) noexcept { + constexpr inline void crypt(char_t* begin, std::uint64_t length, const key_t& key) noexcept { static_assert(sizeof(char_t) == 1, "Currently only 8 bit supported"); if(length == 0) return; - - auto kbegin = std::begin(key); - auto kend = std::end(key); - if(kbegin == kend) return; - - auto it = kbegin; + if(key.size() == 0) _invalid_key_size(); + if(key.size() & (key.size() - 1)) _invalid_key_size(); /* key must be an power of 2 */ auto left = length; - -#ifdef __clang__ - /* - * Enforce clang here to not evaluate this loop at compile time as long its not called in a constexpr context! - * We lose compiler opts. here a bit, but cases where a xor was made over larger than 8 bit registers were really rare! - */ - #pragma nounroll -#endif + size_t key_index{0}; while(left-- > 0) { - if(it == kend) - it = kbegin; - - *begin ^= *it; - - it++; - begin++; + key_index &= key.size(); + *begin ^= key[key_index + key_index]; } } - template + template constexpr inline auto encode(const char_t(&message)[message_size], const key_t& key) noexcept { constexpr auto message_buffer_size = internal::recommand_message_buffer(message_size); internal::message result{}; @@ -102,11 +90,11 @@ namespace str_obf { auto bit = result.buffer.begin(); auto mit = message; - size_t index = message_size; + std::uint64_t index = message_size; while(index-- > 0) *bit++ = *mit++; - size_t padding = message_buffer_size - message_size; + std::uint64_t padding = message_buffer_size - message_size; if(padding) { /* to make the string end less obvious we add some noise here (it does not harm user performance) */ std::uint64_t rng_seed = internal::time_seed() ^ internal::string_hash(message, 0); std::uint64_t rng_base = rng_seed; @@ -119,10 +107,10 @@ namespace str_obf { return result; } - template + template constexpr inline auto str_length(const char_t(&message)[length]) noexcept { return length; } - template + template inline std::string decode(const internal::message& message) { std::string result{}; result.resize(message_size); @@ -133,26 +121,32 @@ namespace str_obf { return result; } - constexpr inline size_t generate_key_length(std::uint64_t seed, size_t max_size) noexcept { - if(max_size <= 8) return max_size; /* We dont need a longer key then the message itself. As well compiler opt. doesn't matter here */ - if(max_size > 64) max_size = 64; + /* length is a power of 2! */ + constexpr inline std::uint64_t generate_key_length(std::uint64_t seed, std::uint64_t message_size) noexcept { + if(message_size <= 1) return 1; + + size_t power2{0}; + while(message_size >>= 1U) + power2++; + + if(power2 > max_key_power) + power2 = max_key_power; std::uint64_t rng_base = seed; - size_t length = 0; + std::uint64_t length = 0; do { - length = (size_t) ((internal::rng32_next(rng_base, (uint32_t) seed) >> 12UL) & 0xFFUL); - } while(length < 8 || length >= max_size); + length = (std::uint64_t) ((internal::rng32_next(rng_base, (uint32_t) seed) >> 12UL) & 0xFFUL); + } while(length == 0 || length > power2); - /* it does not really matter if we have a 8 byte aligned number here, because we iterate so or so byte for byte */ - return length; + return 1U << length; } - template + template constexpr inline auto generate_key(const char* _str_seed) noexcept { std::uint64_t rng_seed = internal::time_seed() ^ internal::string_hash(_str_seed, 0) ^ line_number; std::uint64_t rng_base = rng_seed; - constexpr size_t key_length = generate_key_length(internal::time_seed() ^ (line_number << 37UL), max_size); + constexpr std::uint64_t key_length = generate_key_length(internal::time_seed() ^ (line_number << 37UL), message_size); std::array result{}; for(auto& it : result) it = (internal::rng32_next(rng_base, (uint32_t) rng_seed) >> 16UL) & 0xFFUL; @@ -202,4 +196,4 @@ constexpr auto variable_name = ::str_obf::encode(string, str_obf::generate_key<_ (([]{ \ static strobf_define(_, message); \ return strobf_val(_); \ -})()) +})()) \ No newline at end of file