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 }