4

My app contains numerous IDs. I want to eventually make the code viewable by others, but not make it easy for runtime reverse engineers to look for easily known IDs. Also, during development it is helpful to have constant IDs in the log files for easier debugging. But at runtime I would like to make these IDs random by generating them during the Release compile. Suggested code using the <random> lib can be seen in GetRandomId1() below. constexpr makes their use in the code possible like in switch statements. However, I am having trouble using constexpr in my proposed function because <random> is not constexpr compatible. Is there another way to generate random numbers at compile time? Or is generating random numbers at compile time to be used as constants at runtime considered against the concept of constexpr?

#include <iostream>
#include <random>

// this is the code I would like to use to generate a random number at compile time
/*constexpr */int GetRandomId1()
{
  std::random_device rd; // non-deterministic seed
  std::mt19937 gen( rd() ); // with constexpr uncommented: 
    // error C3250: 'rd': declaration is not allowed in 'constexpr' function body
    // error C3249: illegal statement or sub-expression for 'constexpr' function
    // error C3250: 'gen': declaration is not allowed in 'constexpr' function body

  std::uniform_int_distribution<> distri( 1000, 9999 ); // with constexpr uncommented: 
    // error C3250: 'distri': declaration is not allowed in 'constexpr' function bod
    // error C3249: illegal statement or sub-expression for 'constexpr' function
    // error C2134: 'std::uniform_int<_Ty>::operator ()': call does not result in a constant expression

  return distri( gen );
}

// this code is what works so far
constexpr int GetRandomId2()
{
  return 22; // how to make somewhat random?
}

constexpr int AAA = 10;
//constexpr int AAA = GetRandonId1(); // error: is not constexpr function
constexpr int BBB = GetRandomId2(); // ok

void Func1( long ab )
{
  switch( ab )
  {
    case AAA:
      std::cout << AAA << std::endl;
      break;

    case BBB:
      std::cout << BBB << std::endl;
      break;
  }
}

int main()
{
  Func1( 22 ); // ok: prints 22

  return 0;
}

I am looking for a straight forward, maintainable solution like the one I proposed and unlike the heavy use of templates as proposed in How can I generate dense unique type IDs at compile time?. Also in this posting @jmihalicza points to the Random number generator for C++ template metaprograms research paper. This paper describes compile-time random number generation using template metaprogramming which is a complicated attempt that accomplishes a task for which IMO constexpr was (dare I say, or should have been?) designed.

Because of app architectural reasons I don’t have to worry about ID collisions so that is not a concern. The app code would make sure there would be no duplicates returned.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
rtischer8277
  • 496
  • 6
  • 27
  • 7
    you could always add a custom pre-build step, that generates random numbers to file, to be `#include`d – sp2danny Jul 26 '16 at 15:27

1 Answers1

7

I gave a constexpr random number generator here some time ago, for a related purpose of constexpr string encryption to help against the same adversaries you are concerned with.

I think it is of comparable cryptographic security to mt19937, which is (a very elaborate) linear feedback generator and not really cryptographically secure anyways.

The idea in this code is to use __TIME__ and __LINE__ as the seed to the generator.

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);
};


// Seed

#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)

I didn't attempt to rewrite this using constexpr instead of templates, but tbh I think in C++11 standard using constexpr functions for anything at all complicated is not significantly nicer than templates. It only really gets nice in C++14 standard, when you can actually have local variables and so on.

Anyways it still shouldn't be that hard, this generator implementation is not that bad.

You definitely need to give up dreams of talking to std::random_device at compile time though. IIRC, in libc++ implementation, that is basically a thin wrapper over std::fopen("/dev/urandom"). I'm not aware of any C++ standard or proposal that would allow filesystem operations within constexpr computations. :)

Community
  • 1
  • 1
Chris Beck
  • 15,614
  • 4
  • 51
  • 87
  • What does it mean to become “cryptographically secure”? Why is that necessary at compile-time? Is that the rationale behind the `constexpr`? To my way of thinking since the programmer controls the code in the scope of any particular `constexpr` function, then it should be he who decides what should be treated as `const` for the rest of the program. The decision is no greater than deciding at compile-time whether a member variable is public, private or protected. – rtischer8277 Aug 05 '16 at 15:01
  • The question is, what kind of code should the compiler be able to run in order to produce the `constexpr` literal type? Should it be header-only like for metaprogramming like your solution? Misses the point of `constexpr`. Should it be relegated to external file code? Misses the point too. – rtischer8277 Aug 05 '16 at 15:01
  • I think you have some misunderstandings about what `constexpr` is. `constexpr` is not the same as `const`, it really has nothing to do with `public, private` etc. It also has nothing to do with enabling compiler optimizations -- if the compiler is able to compute an expression at compile-time, it is already free to optimize it under the as-if rule. The most significant benefit of `constexpr` is that literal types can be used as template parameters. – Chris Beck Aug 05 '16 at 18:44
  • The whole premise of your question is that you want to conceal information in your executable, from some adversary. The "runtime reverse engineers" you mentioned. The strongest thing you could hope for is that the concealed information is cryptographically secure -- they wouldn't be able to recover the keys from the bits written in the binary without enormous computational power. Usually, that is the reason to use `std::random_device`, since it produces cryptographically secure randomness. But since you didn't use a secure generator, it accomplished nothing. (And this is anyways problematic.) – Chris Beck Aug 05 '16 at 18:47
  • "The question is, what kind of code should the compiler be able to run in order to produce the constexpr literal type? Should it be header-only like for metaprogramming like your solution? Misses the point of constexpr. Should it be relegated to external file code? Misses the point too." No, this is an entirely new question from the question that you asked. – Chris Beck Aug 05 '16 at 20:08
  • My question has not changed. You are off topic when discussing whether or not my desire to use some form of compile-time generated IDs is cryptographically secure or not. That topic was your own creation. You would be on topic if you discussed `constexpr`'s capabilities at compile time vs runtime. – rtischer8277 Aug 06 '16 at 15:12
  • That's fine if you don't care about cryptographic security. Your question was about how to generate random numbers at compile-time for the purpose of generating random id's to make it difficult for "runtime reverse engineers". I gave a solution to this that works. If you instead want to ask some sort of meta question "what is the purpose of `constexpr`, and why is C++11 `constexpr` not a total replacement for templates" I encourage you to ask that question and see what responses you get. But that really has nothing to do with the problem that you posed or the code example that you gave. – Chris Beck Aug 06 '16 at 15:37