1 /**
2 * Unit tests for RNG
3 * 
4 * Copyright:
5 * (C) 2014-2015 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.rng.test;
12 
13 import botan.constants;
14 static if (BOTAN_TEST):
15 import botan.test;
16 import botan.libstate.libstate;
17 import botan.codec.hex;
18 import botan.rng.rng;
19 import botan.utils.parsing;
20 import core.atomic;
21 import std.stdio;
22 public import memutils.hashmap;
23 
24 class FixedOutputRNG : RandomNumberGenerator
25 {
26 public:
27     override bool isSeeded() const { return !m_buf.empty; }
28     
29     ubyte random()
30     {
31         if (!isSeeded())
32             throw new Exception("Out of bytes");
33 
34         ubyte output = m_buf.front();
35         m_buf.removeFront();
36         return output;
37     }
38     
39     override void reseed(size_t) {}
40     
41     override void randomize(ubyte* output, size_t len)
42     {
43         if (len <= m_buf.length) {
44             output[0 .. len] = m_buf.ptr[0 .. len];
45             auto new_buf = Vector!ubyte(m_buf.ptr[len .. m_buf.length]);
46             m_buf = new_buf.move();
47         } else {
48             for(size_t j = 0; j != len; j++)
49                 output[j] = random();
50         }
51     }
52     
53     override void addEntropy(const(ubyte)* b, size_t s)
54     {
55         m_buf.insert(b[0 .. s]);
56     }
57     
58     override @property string name() const { return "Fixed_Output_RNG"; }
59     
60     override void clear() {}
61 
62     override SecureVector!ubyte randomVec(size_t bytes) { return super.randomVec(bytes); }
63     
64     this(const ref Vector!ubyte input)
65     {    
66         m_buf.insert(input.ptr[0 .. input.length]);
67     }
68     
69     this(string in_str)
70     {
71         Vector!ubyte input = hexDecode(in_str);
72         m_buf.insert(input.ptr[0 .. input.length]);
73     }
74     
75     this() {}
76 protected:
77     size_t remaining() const { return m_buf.length; }
78 private:
79     Vector!ubyte m_buf;
80 }
81 
82 RandomNumberGenerator getRng(string algo_str, string ikm_hex)
83 {
84     //logDebug("getRng: ", algo_str);
85     class AllOnceRNG : FixedOutputRNG
86     {
87     public:
88         this(const ref Vector!ubyte input) {
89             super(input);
90         }
91         
92         override SecureVector!ubyte randomVec(size_t)
93         {
94             SecureVector!ubyte vec = SecureVector!ubyte(this.remaining());
95             this.randomize(vec.ptr, vec.length);
96             return vec;
97         }
98     }
99     
100     const auto ikm = hexDecode(ikm_hex);
101     
102     AlgorithmFactory af = globalState().algorithmFactory();
103     
104     const auto algo_name = parseAlgorithmName(algo_str);
105     
106     const string rng_name = algo_name[0];
107     
108     static if (BOTAN_HAS_HMAC_DRBG) {
109         import botan.rng.hmac_drbg;
110         if (rng_name == "HMAC_DRBG") {
111             auto mac = af.makeMac("HMAC(" ~ algo_name[1] ~ ")");
112             if (!mac) logDebug("No Mac found");
113             return new HMAC_DRBG(mac, new AllOnceRNG(ikm));
114         }
115     }
116     
117     static if (BOTAN_HAS_X931_RNG) {
118         import botan.rng.x931_rng;
119         if (rng_name == "X9.31-RNG")
120             return new ANSIX931RNG(af.makeBlockCipher(algo_name[1]), new FixedOutputRNG(ikm));
121     }
122     
123     return null;
124 }
125 
126 
127 shared size_t total_tests;
128 static if (BOTAN_HAS_X931_RNG)
129 size_t x931Test(string algo,
130                  string ikm,
131                  string output,
132                  size_t L)
133 {
134     atomicOp!"+="(total_tests, 1);
135     Unique!RandomNumberGenerator rng = getRng(algo, ikm);
136     
137     if (!rng)
138         throw new Exception("Unknown RNG " ~ algo);
139     
140     const string got = hexEncode(rng.randomVec(L));
141     
142     if (got != output)
143     {
144         logTrace("X9.31 " ~ got ~ " != " ~ output);
145         return 1;
146     }
147     
148     return 0;
149 }
150 
151 static if (BOTAN_HAS_HMAC_DRBG)
152 size_t hmacDrbgTest(ref HashMap!(string, string) m)
153 {
154     atomicOp!"+="(total_tests, 1);
155     const string algo = m["RNG"];
156     const string ikm = m["EntropyInput"];
157     
158     Unique!RandomNumberGenerator rng = getRng(algo, ikm);
159 
160     if (!rng)
161         throw new Exception("Unknown RNG " ~ algo);
162     
163     rng.reseed(0); // force initialization
164     
165     // now reseed
166     const auto reseed_input = hexDecode(m["EntropyInputReseed"]);
167     rng.addEntropy(reseed_input.ptr, reseed_input.length);
168     
169     const string output = m["Out"];
170     
171     const size_t out_len = output.length / 2;
172     
173     rng.randomVec(out_len); // gen 1st block (discarded)
174     
175     const string got = hexEncode(rng.randomVec(out_len));
176     
177     if (got != output)
178     {
179         logError(algo ~ " " ~ got ~ " != " ~ output);
180         return 1;
181     }
182     
183     return 0;
184 }
185 
186 static if (BOTAN_HAS_TESTS && !SKIP_RNG_TEST) unittest
187 {
188     logDebug("Testing rng/test.d ...");
189     File hmac_drbg_vec = File("test_data/hmac_drbg.vec", "r");
190     File x931_vec = File("test_data/x931.vec", "r");
191     
192     size_t fails = 0;
193 
194     import std.functional : toDelegate;
195 
196     static if (BOTAN_HAS_HMAC_DRBG)
197     fails += runTestsBb(hmac_drbg_vec, "RNG", "Out", true, toDelegate(&hmacDrbgTest));
198     
199     static if (BOTAN_HAS_X931_RNG)
200     fails += runTestsBb(x931_vec, "RNG", "Out", true,
201         (ref HashMap!(string, string) m) {
202             return x931Test(m["RNG"], m["IKM"], m["Out"], to!uint(m["L"]));
203         });
204 
205 
206     testReport("rng", total_tests, fails);
207 }