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 auto ref Vector!ubyte input)
65     {    
66         m_buf.insert(input.ptr[0 .. input.length]);
67     }
68 
69     
70     this(string in_str)
71     {
72         Vector!ubyte input = hexDecode(in_str);
73         m_buf.insert(input.ptr[0 .. input.length]);
74     }
75     
76     this() {}
77 protected:
78     size_t remaining() const { return m_buf.length; }
79 private:
80     Vector!ubyte m_buf;
81 }
82 
83 RandomNumberGenerator getRng(string algo_str, string ikm_hex)
84 {
85     //logDebug("getRng: ", algo_str);
86     class AllOnceRNG : FixedOutputRNG
87     {
88     public:
89         this(const ref Vector!ubyte input) {
90             super(input);
91         }
92         
93         override SecureVector!ubyte randomVec(size_t)
94         {
95             SecureVector!ubyte vec = SecureVector!ubyte(this.remaining());
96             this.randomize(vec.ptr, vec.length);
97             return vec;
98         }
99     }
100     
101     const auto ikm = hexDecode(ikm_hex);
102     
103     AlgorithmFactory af = globalState().algorithmFactory();
104     
105     const auto algo_name = parseAlgorithmName(algo_str);
106     
107     const string rng_name = algo_name[0];
108     
109     static if (BOTAN_HAS_HMAC_DRBG) {
110         import botan.rng.hmac_drbg;
111         if (rng_name == "HMAC_DRBG") {
112             auto mac = af.makeMac("HMAC(" ~ algo_name[1] ~ ")");
113             if (!mac) logDebug("No Mac found");
114             return new HMAC_DRBG(mac, new AllOnceRNG(ikm));
115         }
116     }
117     
118     static if (BOTAN_HAS_X931_RNG) {
119         import botan.rng.x931_rng;
120         if (rng_name == "X9.31-RNG")
121             return new ANSIX931RNG(af.makeBlockCipher(algo_name[1]), new FixedOutputRNG(ikm));
122     }
123     
124     return null;
125 }
126 
127 
128 shared size_t total_tests;
129 static if (BOTAN_HAS_X931_RNG)
130 size_t x931Test(string algo,
131                  string ikm,
132                  string output,
133                  size_t L)
134 {
135     atomicOp!"+="(total_tests, 1);
136     Unique!RandomNumberGenerator rng = getRng(algo, ikm);
137     
138     if (!rng)
139         throw new Exception("Unknown RNG " ~ algo);
140     
141     const string got = hexEncode(rng.randomVec(L));
142     
143     if (got != output)
144     {
145         logTrace("X9.31 " ~ got ~ " != " ~ output);
146         return 1;
147     }
148     
149     return 0;
150 }
151 
152 static if (BOTAN_HAS_HMAC_DRBG)
153 size_t hmacDrbgTest(ref HashMap!(string, string) m)
154 {
155     atomicOp!"+="(total_tests, 1);
156     const string algo = m["RNG"];
157     const string ikm = m["EntropyInput"];
158     
159     Unique!RandomNumberGenerator rng = getRng(algo, ikm);
160 
161     if (!rng)
162         throw new Exception("Unknown RNG " ~ algo);
163     
164     rng.reseed(0); // force initialization
165     
166     // now reseed
167     const auto reseed_input = hexDecode(m["EntropyInputReseed"]);
168     rng.addEntropy(reseed_input.ptr, reseed_input.length);
169     
170     const string output = m["Out"];
171     
172     const size_t out_len = output.length / 2;
173     
174     rng.randomVec(out_len); // gen 1st block (discarded)
175     
176     const string got = hexEncode(rng.randomVec(out_len));
177     
178     if (got != output)
179     {
180         logError(algo ~ " " ~ got ~ " != " ~ output);
181         return 1;
182     }
183     
184     return 0;
185 }
186 
187 static if (BOTAN_HAS_TESTS && !SKIP_RNG_TEST) unittest
188 {
189     logDebug("Testing rng/test.d ...");
190     File hmac_drbg_vec = File("test_data/hmac_drbg.vec", "r");
191     File x931_vec = File("test_data/x931.vec", "r");
192     
193     size_t fails = 0;
194 
195     import std.functional : toDelegate;
196 
197     static if (BOTAN_HAS_HMAC_DRBG)
198     fails += runTestsBb(hmac_drbg_vec, "RNG", "Out", true, toDelegate(&hmacDrbgTest));
199     
200     static if (BOTAN_HAS_X931_RNG)
201     fails += runTestsBb(x931_vec, "RNG", "Out", true,
202         (ref HashMap!(string, string) m) {
203             return x931Test(m["RNG"], m["IKM"], m["Out"], to!uint(m["L"]));
204         });
205 
206 
207     testReport("rng", total_tests, fails);
208 }