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 }