aboutsummaryrefslogtreecommitdiff
path: root/rng.cpp
blob: 5bed44b8da17728d252b3c88423eb3b715ca8f41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <stdexcept>
#include <cstring>
#include <cassert>
#include "rng.h"

#ifdef __APPLE__
#include <cstdlib>
#else
#include <bsd/stdlib.h>
#endif

using namespace std;


//adapted from http://blog.regehr.org/archives/1063 (rotl32c version)
inline uint64_t rotl64(uint64_t x,uint32_t n){
	assert(n<64);
	return (x<<n)|(x>>(-n&63));
}
inline uint64_t rotr64(uint64_t x,uint32_t n){
	assert(n<64);
	return (x>>n)|(x<<(-n&63));
}

KeyRng::KeyRng(const char *key_,int keylen_)
		:keylen(keylen_),idx(0),state(0){
	if(keylen<=0)throw invalid_argument("KeyRng: Key should not be empty");
	assert(key_);
	key=new uint8_t[keylen];
	memcpy(key,key_,keylen);
	stir();
}

KeyRng::KeyRng(const string &key_)
		:keylen(key_.size()),idx(0),state(0){
	if(keylen==0)throw invalid_argument("KeyRng: Key should not be empty");
	key=new uint8_t[keylen];
	memcpy(key,key_.data(),keylen);
	stir();
}

KeyRng::~KeyRng(){
	delete[] key;
}

void KeyRng::stir(){
	for(int i=0;i<10;i++)get();
}

uint32_t KeyRng::get(){
	//Progressively and cyclicly mixes in the key with the state.
	//Mostly own creation; the "tempering" part is derived from a (part of a) Mersenne Twister implementation.
	//In terms of distribution of values, this is similar to arc4random().
	//This passes DieHarder 3.31.1 (tested 2016/10/06) with 2 "weak" results. The initial key "wachtwoord" was used.
	//At least n subsequent values should be independent, where n is the length of the initial key.
	state^=key[idx];
	state+=17;
	key[idx]+=13;
	idx=(idx+1)%keylen;
	state^=rotr64(state,11); //tempering
	state^=rotl64(state,7)&0x9d2c5680;
	state^=rotr64(state,18);
	return state>>32;
}

uint32_t KeyRng::get_uniform(uint32_t upbound){
	if(upbound<=1)return 0;
	uint32_t min=((uint64_t)1<<32)%upbound; //this is the amount of unusable RNG outputs when avoiding modulo bias
	while(true){
		uint32_t v=get();
		if(v>=min)return v%upbound;
	}
}


uint32_t CryptoRng::get(){
	return arc4random();
}

uint32_t CryptoRng::get_uniform(uint32_t upbound){
	return arc4random_uniform(upbound);
}