1 module dcrypt.random.drng; 2 3 /// The DRNG module contains a collection of deterministic random number generators. 4 5 public import dcrypt.random.prng; 6 import dcrypt.digest; 7 import dcrypt.digests.sha1; 8 import dcrypt.digests.sha2; 9 import dcrypt.digests.sha3; 10 import dcrypt.bitmanip; 11 import dcrypt.util: wipe; 12 import std.algorithm: min; 13 import std.traits: isIntegral; 14 15 /// 16 /// Test if T is a deterministic random number generator. 17 /// Returns: Returns $(D true) if T can be used as a DRNG. 18 /// 19 @safe @nogc nothrow 20 template isDRNG(T) 21 { 22 enum bool isDRNG = isRNG!T && 23 T.isDeterministic && 24 is(typeof( 25 { 26 ubyte[] buf; 27 T rng = T.init; 28 rng.setSeed(cast(const ubyte[]) buf); // Can set the generator to well known state. 29 rng.setSeed(cast(ubyte) 0); // variadic template 30 rng.setSeed(cast(ubyte) 0, cast(ubyte) 0); 31 })); 32 } 33 34 @safe @nogc nothrow 35 template isDRNGWithInput(T) 36 { 37 enum bool isDRNGWithInput = isDRNG!T && isRNGWithInput!T; 38 } 39 40 /// Generate a pseudo random but deterministic sequence of bytes. 41 unittest { 42 import dcrypt.digests.sha2; 43 //import std.stdio; 44 45 HashDRNG_SHA256 drng; 46 47 ubyte[70] buf; 48 49 drng.nextBytes(buf); 50 51 //writefln("%(%.2x%)", buf); 52 } 53 54 alias HashDRNG!(SHA1, 440) HashDRNG_SHA1; 55 alias HashDRNG!(SHA256, 440) HashDRNG_SHA256; 56 alias HashDRNG!(SHA384, 888) HashDRNG_SHA384; 57 alias HashDRNG!(SHA512, 888) HashDRNG_SHA512; 58 59 alias HashDRNG!(SHA3_256, 440) HashDRNG_SHA3_256; 60 alias HashDRNG!(SHA3_384, 888) HashDRNG_SHA3_384; 61 alias HashDRNG!(SHA3_512, 888) HashDRNG_SHA3_512; 62 63 static assert(isDRNG!HashDRNG_SHA256 && isRNGWithInput!HashDRNG_SHA256 , HashDRNG.name~" is no DRNG."); 64 65 /// Standard: NIST SP800-90A, HashDRBG 66 /// 67 /// Params: 68 /// D = The underlying digest. 69 /// seedlen = Length of internal state in bits. See NIST SP800-90A, Section 10.1. 70 @safe 71 struct HashDRNG(D, uint seedlen) if(isStdDigest!D && seedlen % 8 == 0) { 72 73 public { 74 enum isDeterministic = true; 75 enum name = "HashDRNG-" ~ D.name; 76 } 77 78 private { 79 ubyte[seedlen/8] V, C; 80 ulong reseedCounter; 81 } 82 nothrow @nogc: 83 84 ~this() { 85 wipe(V); 86 wipe(C); 87 } 88 89 /// Initialize the generator with given seed. 90 void setSeed(in ubyte[] seed...) { 91 92 hashDF!D(V, seed); 93 94 hashDF!D(V, cast(ubyte) 0x00, V); 95 96 reseedCounter = 1; 97 } 98 99 /// Add entropy to the generators internal state. 100 void addSeed(in ubyte[] seed...) { 101 102 hashDF!D(V, cast(ubyte) 0x01, V, seed); 103 104 hashDF!D(C, cast(ubyte) 0x00, V); 105 106 reseedCounter = 1; 107 } 108 109 /// Generate pseudo random bytes. 110 /// Params: 111 /// buf = Fill this buffer with random data. 112 /// additionalInput = Can provide more entropy. Similar but not equal to callin addSeed() before. 113 void nextBytes(ubyte[] buf, in ubyte[] additionalInput...) { 114 D hash; 115 116 if(additionalInput.length > 0) { 117 hash.putAll(cast(ubyte) 0x02, V, additionalInput); 118 ubyte[D.digestLength] w = hash.finish(); 119 add(V, w); 120 } 121 122 hashGen(buf); 123 124 hash.putAll(cast(ubyte) 0x03, V); 125 immutable ubyte[D.digestLength] H = hash.finish(); 126 127 add(V, H); 128 add(V, reseedCounter); 129 130 ++reseedCounter; 131 } 132 133 private void hashGen(ubyte[] buf) { 134 ubyte[V.length] data = V; 135 D hash; 136 while(buf.length > 0) { 137 size_t len = min(D.digestLength, buf.length); 138 hash.put(data); 139 buf[0..len] = hash.finish()[0..len]; 140 buf = buf[len..$]; 141 increment(data); 142 } 143 wipe(data); 144 } 145 146 } 147 148 /// Hash derivation function. 149 /// Standard: NIST SP800-90A, Section 10.4.1 150 /// Note: Number of output bits is implicitly defined by buf.length*8. 151 private void hashDF(D, T...)(ubyte[] buf, in T seed) if(isDigest!D) { 152 ubyte counter = 1; 153 ubyte[4] outputLen; /// output length in bits 154 155 outputLen = toEndian!uint(cast(uint) buf.length*8); // BC compatible 156 //outputLen = toLittleEndian!uint(cast(uint) buf.length*8, outputLen); 157 158 D digest; 159 160 while(buf.length > 0) { 161 162 digest.putAll(counter, outputLen, seed); 163 164 size_t len = min(buf.length, D.digestLength); 165 buf[0..len] = digest.finish()[0..len]; 166 buf = buf[len..$]; 167 168 ++ counter; 169 } 170 } 171 172 // test hashDF 173 private unittest { 174 ubyte[70] buf; 175 hashDF!SHA256(buf, cast(const ubyte[]) "seed"); 176 177 assert(buf == x"ae678a8fcbcfaf3bcf57395b6fa3c614516d21182992780fb155bc75ded4369ac44ebfc392d9990553d59f6beffa1fb56d3962be000d1a7d009674240f02855b7a8fd125dd19"); 178 } 179 180 181 /// Little endian increment. 182 private void increment(ubyte[] v) nothrow @safe @nogc { 183 ubyte[1] one = 1; 184 add(v, one); 185 } 186 187 188 private alias toBigEndian toEndian; /// easily switch between LE and BE 189 190 /// Add number to little endian byte string. 191 /// a += b; 192 private void add(T)(ubyte[] a, T b) nothrow @safe @nogc 193 if(isIntegral!T) { 194 add(a, toEndian!T(b)); 195 } 196 197 198 /// a += b; 199 private void add(ubyte[] a, in ubyte[] b) nothrow @safe @nogc 200 in { 201 assert(a.length >= b.length); 202 } body { 203 ubyte carry = 0; 204 205 for(uint i = 0; i < a.length; ++i) { 206 uint t = cast(uint) a[$-1-i] + carry; 207 if(i < b.length) { 208 t += b[$-1-i]; 209 } 210 a[$-1-i] = cast(ubyte) t; 211 carry = cast(ubyte) (t >> 8); 212 } 213 214 } 215 216 // testing add() 217 private unittest { 218 ubyte[4] a, b; 219 220 add(a, 0xFF); 221 assert(a[0..4] == toEndian(0xFF)); 222 add(b, 0xFF00); 223 assert(b[0..4] == toEndian(0xFF00)); 224 add(a, b); 225 assert(a[0..4] == toEndian(0xFFFF)); 226 add(a, 1); 227 assert(a[0..4] == toEndian(0x10000)); 228 }