1

Hi (sorry for the title - I couldnt make it more meaningfull),

I am using encryption of string literals using constexpr from here: Compile time string encryption using constexpr

I use it to hide string literals (formats) used in logs in binary files. It works preety well, the macro looks as follows:

#define LOG(format, ...) do { \
  constexpr auto secformat = LIT((format)); \
  WriteLog(static_cast<std::string>(secformat).c_str(), ##__VA_ARGS__); \
} while(0)  

void WriteLog(char const * format, ...);

The problem is that there is also an overloaded version of WriteLog :

void WriteLog(int param1, int param2, char const * format, ...);

and before the LOG macro looked as follows:

#define LOG WriteLog

so compiler was easily choosing a correct overload, this made it possible to make calls like:

LOG("Some log text %d %d", 1, 2); // *1
LOG(SEVERITY_HIGH, DEST_CLIENT, "Some log text %d %d", 1, 2); // *2

now I can handle only the first log case, but it wont work when logs like in *2 are found.

So the question is whether there is some method I could use to still allow to use logs like above *1 and *2?

I was trying to use constexpr functions, tried with variadic templates - but in both cases I was ending with some non constexpr parameters. But I am not very strong with both of those language features.

below is source code ( LIVE on coliru ):

#include <array>
#include <iostream>
#include <string>
#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdarg.h>

// Based on:
// https://eden.dei.uc.pt/~sneves/pubs/2012-snfa2.pdf
typedef uint32_t u32;
typedef uint64_t u64;
typedef unsigned char uchar;

template<u32 S, u32 A = 16807UL, u32 C = 0UL, u32 M = (1UL << 31) - 1>
struct LinearGenerator {
  static const u32 state = ((u64)S * A + C) % M;
  static const u32 value = state;
  typedef LinearGenerator<state> next;
  struct Split { // Leapfrog
    typedef LinearGenerator< state, A*A, 0, M> Gen1;
    typedef LinearGenerator<next::state, A*A, 0, M> Gen2;
  };
};

// Metafunction to get a particular index from generator
template<u32 S, std::size_t index>
struct Generate {
  static const uchar value = Generate<LinearGenerator<S>::state, index - 1>::value;
};

template<u32 S>
struct Generate<S, 0> {
  static const uchar value = static_cast<uchar> (LinearGenerator<S>::value);
};

// List of indices
template<std::size_t...>
struct StList {};

// Concatenate
template<typename TL, typename TR>
struct Concat;

template<std::size_t... SL, std::size_t... SR>
struct Concat<StList<SL...>, StList<SR...>> {
  typedef StList<SL..., SR...> type;
};

template<typename TL, typename TR>
using Concat_t = typename Concat<TL, TR>::type;

// Count from zero to n-1
template<size_t s>
struct Count {
  typedef Concat_t<typename Count<s - 1>::type, StList<s - 1>> type;
};

template<>
struct Count<0> {
  typedef StList<> type;
};

template<size_t s>
using Count_t = typename Count<s>::type;

// Get a scrambled character of a string
template<u32 seed, std::size_t index, std::size_t N>
constexpr uchar get_scrambled_char(const char(&a)[N]) {
  return static_cast<uchar>(a[index]) + Generate<seed, index>::value;
}

// Get a ciphertext from a plaintext string
template<u32 seed, typename T>
struct cipher_helper;

template<u32 seed, std::size_t... SL>
struct cipher_helper<seed, StList<SL...>> {
  static constexpr std::array<uchar, sizeof...(SL)> get_array(const char(&a)[sizeof...(SL)]) {
    return{ { get_scrambled_char<seed, SL>(a)... } };
  }
};

template<u32 seed, std::size_t N>
constexpr std::array<uchar, N> get_cipher_text(const char(&a)[N]) {
  return cipher_helper<seed, Count_t<N>>::get_array(a);
}

// Get a noise sequence from a seed and string length
template<u32 seed, typename T>
struct noise_helper;

template<u32 seed, std::size_t... SL>
struct noise_helper<seed, StList<SL...>> {
  static constexpr std::array<uchar, sizeof...(SL)> get_array() {
    return{ { Generate<seed, SL>::value ... } };
  }
};

template<u32 seed, std::size_t N>
constexpr std::array<uchar, N> get_key() {
  return noise_helper<seed, Count_t<N>>::get_array();
}


/*
// Get an unscrambled character of a string
template<u32 seed, std::size_t index, std::size_t N>
char get_unscrambled_char(const std::array<uchar, N> & a) {
return static_cast<char> (a[index] - Generate<seed, index>::value);
}
*/

// Metafunction to get the size of an array
template<typename T>
struct array_info;

template <typename T, size_t N>
struct array_info<T[N]>
{
  typedef T type;
  enum { size = N };
};

template <typename T, size_t N>
struct array_info<const T(&)[N]> : array_info<T[N]> {};

// Scramble a string
// taken from : https://stackoverflow.com/questions/32287125/compile-time-string-encryption-using-constexpr
template<u32 seed, std::size_t N>
class obfuscated_string {
 private:
  std::array<uchar, N> cipher_text_;
  std::array<uchar, N> key_;
 public:
  explicit constexpr obfuscated_string(const char(&a)[N])
    : cipher_text_(get_cipher_text<seed, N>(a))
    , key_(get_key<seed, N>())
  {}

  std::string to_string() const {
    return (std::string)(*this);
  }

  operator std::string() const {
    char plain_text[N];
    for (volatile std::size_t i = 0; i < N; ++i) {
      volatile char temp = static_cast<char>(cipher_text_[i] - key_[i]);
      plain_text[i] = temp;
    }
    return std::string{ plain_text, plain_text + N };
  }
};

#define RNG_SEED ((__TIME__[7] - '0') * 1  + (__TIME__[6] - '0') * 10  + \
              (__TIME__[4] - '0') * 60   + (__TIME__[3] - '0') * 600 + \
              (__TIME__[1] - '0') * 3600 + (__TIME__[0] - '0') * 36000) + \
              (__LINE__ * 100000)

#define LIT(STR) \
    obfuscated_string<RNG_SEED, array_info<decltype((STR))>::size>{(STR)}

void WriteLog(int iSystem, int iSeverity, char const * szFormat, ...) {
  va_list argptr;
  va_start(argptr, szFormat);
  static const int iBuffSize = 512;
  char aLine[iBuffSize + 3];
  char* szLine = &aLine[0];
  vsnprintf(szLine, iBuffSize, szFormat, argptr);
  std::cout << iSystem << ", " << iSeverity << ": " << szLine << "\n";
  va_end(argptr);
}

void WriteLog(const char* szFormat, ...) {
  va_list argptr;
  va_start(argptr, szFormat);
  static const int iBuffSize = 512;
  char aLine[iBuffSize + 3];
  char* szLine = &aLine[0];
  vsnprintf(szLine, iBuffSize, szFormat, argptr);
  std::cout << -1 << ", " << -1 << ": " << szLine << "\n";
  va_end(argptr);
}

#define LOG(format, ...) do { \
  constexpr auto secformat = LIT((format)); \
  WriteLog(((std::string)(secformat)).c_str(), ##__VA_ARGS__); \
} while(0)

template<u32 seed, std::size_t N>
std::ostream & operator<< (std::ostream & s, const obfuscated_string<seed, N> & str) {
  s << static_cast<std::string>(str);
  return s;
}

int main() {
  LOG("Echo 12 d");
  LOG("Echo 12 %d", 12);
  LOG("Echo 12 %d, %d", 12, 32);

#define LOG_SEVERITY_HIGH 1
#define LOG_SEVERITY_LOW 2

#define LOG_DEST_SERVER 1
#define LOG_DEST_CLIENT 2

  //LOG(LOG_SEVERITY_LOW, LOG_DEST_CLIENT, "Echo 12 %d, %d", 12, 32);
}
Community
  • 1
  • 1
mike
  • 1,670
  • 11
  • 21
  • 1
    `but it wont work when logs like in *2 are found.` - What do you mean, what is the desired behaviour? – Fantastic Mr Fox Jan 17 '17 at 23:26
  • in case *2 `void WriteLog(int param1, int param2, char const * format, ...);` overload should be called and not `void WriteLog(char const * format, ...);` Currently for *2 my LOG macro will choose `param1` as format for initialization of `constexpr auto secformat`. – mike Jan 17 '17 at 23:32
  • The problem does not seem to be related to the use of constexpr - AFAIK Macro functions cannot be overloaded based on argument types, full stop. – harmic Jan 17 '17 at 23:54
  • @harmic thats true, but I dont want to overload macro functions - those could be replaced with some code - like variadic templates or some other trick. I am out of ideas. – mike Jan 17 '17 at 23:59

0 Answers0