added readme; added special case camouflaging

This commit is contained in:
eerieaerial 2025-05-12 14:04:06 +04:00
parent 5fb3f5c228
commit 235addc585
9 changed files with 290 additions and 59 deletions

View file

@ -14,8 +14,6 @@
#ifndef ZT_N_CAMOPATTERN_HPP
#define ZT_N_CAMOPATTERN_HPP
#define CAMO_TRACE
#include <stdint.h>
#include <array>
#include <unordered_map>
@ -26,33 +24,12 @@
#include "Buffer.hpp"
#include "RuntimeEnvironment.hpp"
#define BYTES_IN_WORD 4
#define BYTES_IN_WORD (sizeof(uint32_t) / sizeof(uint8_t))
#define PATTERN_COUNT 16
#define PATTERN_INDEX_MASK 0xf
#define NO_CAMO PATTERN_COUNT
#define ZT_CAMO_DEFAULT_WORDSTR "DLGE"
namespace std {
template<typename T, size_t N>
struct hash<std::array<T, N>> {
size_t operator()(const std::array<T, N>& array) const {
size_t hash = 0;
for (const auto& element : array) {
hash = hash * 31 + std::hash<T>{}(element);
}
return hash;
}
};
template<>
struct hash<ZeroTier::Address> {
size_t operator()(const ZeroTier::Address& address) const {
return std::hash<uint64_t>{}(address.toInt());
}
};
}
/**
* Camo functions debug trace macro
*/
@ -66,6 +43,43 @@ namespace std {
#define CT(...) ((void)0)
#endif
/**
* Type shorthands
*/
namespace ZeroTier {
enum class CamoLevel;
typedef std::array<uint8_t, BYTES_IN_WORD> CamoPatternBytes;
typedef std::array<CamoPatternBytes, PATTERN_COUNT> CamoPatternArray;
typedef std::unordered_map<CamoPatternBytes, size_t> CamoIndexMap;
typedef std::unordered_map<Address, CamoLevel> KnownHostsMap;
}
/**
* Hash functions for the respective unordered_maps
*/
namespace std {
template<>
struct hash<ZeroTier::CamoPatternBytes> {
size_t operator()(const ZeroTier::CamoPatternBytes& bytes) const {
uint32_t word = 0;
for (const auto& byte : bytes) {
word <<= 8;
word |= byte;
}
return std::hash<uint32_t>{}(word);
}
};
template<>
struct hash<ZeroTier::Address> {
size_t operator()(const ZeroTier::Address& address) const {
return std::hash<uint64_t>{}(address.toInt());
}
};
}
namespace ZeroTier {
/**
@ -102,7 +116,7 @@ class CamoPattern
static size_t getCamoIndex(const Buffer<C> &buffer)
{
size_t result = NO_CAMO;
std::array<uint8_t, BYTES_IN_WORD> camo;
CamoPatternBytes camo;
if (buffer.size() > BYTES_IN_WORD * 2)
{
for (size_t i = 0; i < BYTES_IN_WORD; i++)
@ -144,6 +158,8 @@ public:
/**
* Apply camouflage to buffer
*
* This increases buffer length by adding ID word to the end
*
* @param buffer Buffer to apply camouflage to
*/
template<unsigned int C>
@ -153,8 +169,8 @@ public:
if (isInitialized && (camoIndex == NO_CAMO)) { // Only apply if not already applied
CT("PACKET CONTENTS BEFORE APPLYING CAMO:");
buffer.dump();
camoIndex = std::minstd_rand(std::time(nullptr))() & PATTERN_INDEX_MASK;
std::array<uint8_t, BYTES_IN_WORD> camo = camoValues[camoIndex];
camoIndex = std::minstd_rand(std::time(nullptr))() % PATTERN_COUNT;
CamoPatternBytes camo = camoValues[camoIndex];
// Increase buffer size first to avoid overflow
size_t originalSize = buffer.size();
@ -166,15 +182,16 @@ public:
data[i + originalSize] = data[i + BYTES_IN_WORD];
}
// Copy the first word on the second word's place
for (size_t i = 0; i < BYTES_IN_WORD; i++) {
data[i + BYTES_IN_WORD] = data[i];
}
// Apply XOR to the rest of the buffer
for (size_t i = BYTES_IN_WORD * 2; i < buffer.size(); i++) {
for (size_t i = BYTES_IN_WORD; i < buffer.size(); i++) {
data[i] ^= camo[i % BYTES_IN_WORD];
}
// Apply XOR to create the camouflage pattern in the second word
for (size_t i = 0; i < BYTES_IN_WORD; i++) {
data[i + BYTES_IN_WORD] = data[i] ^ camo[i];
}
CT("PACKET CONTENTS AFTER APPLYING CAMO:");
buffer.dump();
}
@ -183,7 +200,7 @@ public:
/**
* Remove camouflage from buffer
*
* This decreases buffer length by removing 'CAMO' from the end
* This decreases buffer length by removing stored ID word from the end
*
* @param buffer Buffer to remove camouflage from
*/
@ -200,10 +217,10 @@ public:
CT("PACKET CONTENTS BEFORE STRIPPING CAMO:");
buffer.dump();
uint8_t * const data = reinterpret_cast<uint8_t *>(buffer.unsafeData());
std::array<uint8_t, BYTES_IN_WORD> camo = camoValues[camoIndex];
CamoPatternBytes camo = camoValues[camoIndex];
for (size_t i = BYTES_IN_WORD * 2; i < buffer.size(); i++)
{
data[i] ^= camo[i % BYTES_IN_WORD];
data[i] ^= camo[i % BYTES_IN_WORD];
}
size_t storedWordIndex = buffer.size() - BYTES_IN_WORD;
for (size_t i = 0; i < BYTES_IN_WORD; i++)
@ -218,7 +235,7 @@ public:
return result;
}
static void init(CamoLevel level, uint32_t word, std::unordered_map<Address, CamoLevel> hosts, CamoRelayRule rule);
static void init(CamoLevel level, uint32_t word, KnownHostsMap hosts, CamoRelayRule rule);
private:
@ -226,10 +243,10 @@ private:
static CamoLevel camoLevel;
static uint32_t camoWord;
static CamoRelayRule relayRule;
static std::array<std::array<uint8_t, BYTES_IN_WORD>, PATTERN_COUNT> camoValues;
static std::unordered_map<std::array<uint8_t, BYTES_IN_WORD>, size_t> camoIndices;
static CamoPatternArray camoValues;
static CamoIndexMap camoIndices;
static std::mutex camoMutex;
static std::unordered_map<Address, CamoLevel> knownHosts;
static KnownHostsMap knownHosts;
};
} // namespace ZeroTier