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