File: rng.cpp

package info (click to toggle)
libcrypto%2B%2B 8.4.0-1
  • links: PTS
  • area: main
  • in suites: bullseye
  • size: 23,204 kB
  • sloc: cpp: 104,596; asm: 10,488; sh: 7,343; makefile: 51
file content (166 lines) | stat: -rw-r--r-- 4,663 bytes parent folder | download | duplicates (4)
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// rng.cpp - originally written and placed in the public domain by Wei Dai

#include "pch.h"

#include "rng.h"
#include "fips140.h"

#include <time.h>
#include <math.h>

NAMESPACE_BEGIN(CryptoPP)

// linear congruential generator
// originally by William S. England

// do not use for cryptographic purposes

/*
** Original_numbers are the original published m and q in the
** ACM article above.  John Burton has furnished numbers for
** a reportedly better generator.  The new numbers are now
** used in this program by default.
*/

#ifndef LCRNG_ORIGINAL_NUMBERS
const word32 LC_RNG::m=2147483647L;
const word32 LC_RNG::q=44488L;

const word16 LC_RNG::a=(unsigned int)48271L;
const word16 LC_RNG::r=3399;
#else
const word32 LC_RNG::m=2147483647L;
const word32 LC_RNG::q=127773L;

const word16 LC_RNG::a=16807;
const word16 LC_RNG::r=2836;
#endif

void LC_RNG::GenerateBlock(byte *output, size_t size)
{
	while (size--)
	{
		const word32 hi = seed/q;
		const word32 lo = seed%q;
		const sword64 test = a*lo - r*hi;

		if (test > 0)
			seed = static_cast<word32>(test);
		else
			seed = static_cast<word32>(test + m);

		*output++ = byte((GETBYTE(seed, 0) ^ GETBYTE(seed, 1) ^ GETBYTE(seed, 2) ^ GETBYTE(seed, 3)));
	}
}

// ********************************************************

#ifndef CRYPTOPP_IMPORTS

X917RNG::X917RNG(BlockTransformation *c, const byte *seed, const byte *deterministicTimeVector)
	: m_cipher(c),
	  m_size(m_cipher->BlockSize()),
	  m_datetime(m_size),
	  m_randseed(seed, m_size),
	  m_lastBlock(m_size),
	  m_deterministicTimeVector(deterministicTimeVector, deterministicTimeVector ? m_size : 0)
{
	// Valgrind finding, http://github.com/weidai11/cryptopp/issues/105
	// Garbage in the tail creates a non-conforming X9.17 or X9.31 generator.
	if (m_size > 8)
	{
		memset(m_datetime, 0x00, m_size);
		memset(m_lastBlock, 0x00, m_size);
	}

	if (!deterministicTimeVector)
	{
		time_t tstamp1 = ::time(NULLPTR);
		xorbuf(m_datetime, (byte *)&tstamp1, UnsignedMin(sizeof(tstamp1), m_size));
		m_cipher->ProcessBlock(m_datetime);
		clock_t tstamp2 = clock();
		xorbuf(m_datetime, (byte *)&tstamp2, UnsignedMin(sizeof(tstamp2), m_size));
		m_cipher->ProcessBlock(m_datetime);
	}

	// for FIPS 140-2
	// GenerateBlock(m_lastBlock, m_size);

	// Make explicit call to avoid virtual-dispatch findings in ctor
	ArraySink target(m_lastBlock, m_size);
	X917RNG::GenerateIntoBufferedTransformation(target, DEFAULT_CHANNEL, m_size);
}

void X917RNG::GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size)
{
	while (size > 0)
	{
		// calculate new enciphered timestamp
		if (m_deterministicTimeVector.size())
		{
			m_cipher->ProcessBlock(m_deterministicTimeVector, m_datetime);
			IncrementCounterByOne(m_deterministicTimeVector, m_size);
		}
		else
		{
			clock_t c = clock();
			xorbuf(m_datetime, (byte *)&c, UnsignedMin(sizeof(c), m_size));
			time_t t = ::time(NULLPTR);
			xorbuf(m_datetime+m_size-UnsignedMin(sizeof(t), m_size), (byte *)&t, UnsignedMin(sizeof(t), m_size));
			m_cipher->ProcessBlock(m_datetime);
		}

		// combine enciphered timestamp with seed
		xorbuf(m_randseed, m_datetime, m_size);

		// generate a new block of random bytes
		m_cipher->ProcessBlock(m_randseed);
		if (memcmp(m_lastBlock, m_randseed, m_size) == 0)
			throw SelfTestFailure("X917RNG: Continuous random number generator test failed.");

		// output random bytes
		size_t len = UnsignedMin(m_size, size);
		target.ChannelPut(channel, m_randseed, len);
		size -= len;

		// compute new seed vector
		memcpy(m_lastBlock, m_randseed, m_size);
		xorbuf(m_randseed, m_datetime, m_size);
		m_cipher->ProcessBlock(m_randseed);
	}
}

#endif

MaurerRandomnessTest::MaurerRandomnessTest()
	: sum(0.0), n(0)
{
	for (unsigned i=0; i<V; i++)
		tab[i] = 0;
}

size_t MaurerRandomnessTest::Put2(const byte *inString, size_t length, int /*messageEnd*/, bool /*blocking*/)
{
	while (length--)
	{
		byte inByte = *inString++;
		if (n >= Q)
			sum += ::log(double(n - tab[inByte]));
		tab[inByte] = n;
		n++;
	}
	return 0;
}

double MaurerRandomnessTest::GetTestValue() const
{
	if (BytesNeeded() > 0)
		throw Exception(Exception::OTHER_ERROR, "MaurerRandomnessTest: " + IntToString(BytesNeeded()) + " more bytes of input needed");

	double fTu = (sum/(n-Q))/::log(2.0);	// this is the test value defined by Maurer

	double value = fTu * 0.1392;		// arbitrarily normalize it to
	return value > 1.0 ? 1.0 : value;	// a number between 0 and 1
}

NAMESPACE_END