1 // Written in the D programming language. 2 3 /* WARNING: randombytes_set_implementation isn't straightaway accessible from 'wrapper' and shouldn't be used through 'deimos' either, except You know the inner working of sodium_init ! */ 4 5 /** 6 * Functions related to randomness. 7 */ 8 9 module wrapper.sodium.randombytes; 10 11 import wrapper.sodium.core; // assure sodium got initialized 12 13 public 14 import deimos.sodium.randombytes : 15 // randombytes_implementation, 16 randombytes_BYTES_MAX, 17 randombytes_SEEDBYTES, 18 randombytes_seedbytes, 19 // randombytes_buf, 20 // randombytes_buf_deterministic, 21 randombytes_random, 22 randombytes_uniform, 23 randombytes_stir, 24 randombytes_close; 25 /* randombytes_set_implementation, 26 randombytes_implementation_name, 27 randombytes; */ 28 29 import std..string : fromStringz; // @system 30 31 32 // overloading some functions between module deimos.sodium.randombytes and this module 33 34 35 /** The randombytes_buf() function fills `size` bytes starting at buf with an unpredictable sequence of bytes. */ 36 alias randombytes_buf = deimos.sodium.randombytes.randombytes_buf; 37 38 /** The randombytes_buf() function fills the array `buf` with an unpredictable sequence of bytes. */ 39 pragma(inline, true) 40 void randombytes_buf(scope ubyte[] buf) @nogc nothrow @trusted 41 { 42 // enforce(buf !is null, "buf is null"); // not necessary 43 randombytes_buf(buf.ptr, buf.length); // __attribute__ ((nonnull)); 44 } 45 46 alias randombytes_buf_deterministic = deimos.sodium.randombytes.randombytes_buf_deterministic; 47 48 pragma(inline, true) 49 void randombytes_buf_deterministic(ubyte[] buf, const ubyte[randombytes_SEEDBYTES] seed) @nogc nothrow pure @trusted 50 { 51 // enforce(buf !is null, "buf is null"); // not necessary 52 randombytes_buf_deterministic(buf.ptr, buf.length, seed); // __attribute__ ((nonnull)); 53 } 54 55 /* 56 We can't trust in general to receive a valid address from randombytes_implementation_name() to be evaluated as a null-terminated C string, except 57 randombytes_set_implementation wasn't used or used to reset to a sodium-supplied implementaion or used and a valid implementation_name function supplied. 58 Now randombytes_set_implementation isn't straightaway accessible ! 59 */ 60 string randombytes_implementation_name() @nogc nothrow @trusted 61 { 62 import std.exception : assumeUnique; 63 static import deimos.sodium.randombytes; 64 const(char)[] c_arr; 65 try 66 c_arr = fromStringz(deimos.sodium.randombytes.randombytes_implementation_name()); 67 catch (Exception t) { /* known not to throw */} 68 return assumeUnique(c_arr); 69 } 70 71 alias randombytes = deimos.sodium.randombytes.randombytes; 72 73 /** The randombytes() function fills the array `buf` with an unpredictable sequence of bytes. */ 74 pragma(inline, true) 75 void randombytes(scope ubyte[] buf) @nogc nothrow @trusted 76 { 77 // enforce(buf !is null, "buf is null"); // not necessary 78 randombytes(buf.ptr, buf.length); // __attribute__ ((nonnull)); 79 } 80 81 // InputRange interface 82 83 /// InputRange interface to random ubytes (based on calls to randombytes_random()) 84 auto randombytes_range() @nogc nothrow @trusted 85 { 86 /** 87 * An InputRange representing an infinite, randomly generated sequence of ubytes 88 */ 89 static struct RandomBytes_Range { 90 enum chunk_size = 4; 91 uint randombytes_random_result = void; 92 ubyte randombytes_random_index = 0; 93 bool refresh_required = true; // be lazy and don't refresh in popFront() 94 95 enum empty = false; 96 @property ubyte front() { 97 if (refresh_required) { 98 randombytes_random_result = randombytes_random(); 99 refresh_required = false; 100 } 101 return *(cast(ubyte*)&randombytes_random_result + randombytes_random_index); 102 } 103 104 void popFront() { 105 randombytes_random_index = ++randombytes_random_index % chunk_size; 106 if (!randombytes_random_index) 107 refresh_required = true; 108 } 109 110 int opApply(int delegate(ubyte) operations) { 111 int result = 0; 112 for ( ; !empty ; popFront) { 113 result = operations(front); 114 if (result) 115 break; 116 } 117 return result; 118 } 119 /+ instead use enumerate 120 int opApply(int delegate(size_t, ubyte) operations) { 121 int result = 0; 122 size_t counter; 123 for ( ; !empty ; popFront) { 124 result = operations(counter, front); 125 ++counter; 126 if (result) 127 break; 128 } 129 return result; 130 } 131 +/ 132 } // struct RandomBytes_Range 133 134 return RandomBytes_Range(); 135 } 136 137 138 version(QRNG_ONLINE) { 139 /* 140 Probably no public interest, thus wrapper/sodium/QRNG.d is not provided 141 142 This is an interface to QRNG Service, https://qrng.physik.hu-berlin.de/, 143 specifically an interface to "online/live API" opposed to "file based random data" QRNG Service. 144 There is the project https://code.dlang.org/packages/quantum-random as well, which deals with QRNG. 145 QRNG: quantum random number generator, based on the quantum randomness of photon arrival times 146 Server Status: offline since 6/4/2019 06:40 147 Server Stats: 1,427,783,703,486,414 random Bytes (1.27 PB) delivered since November 2010. 148 149 See also Random number servers: https://en.wikipedia.org/wiki/List_of_random_number_generators 150 */ 151 import wrapper.sodium.QRNG; 152 alias QRNG_randombytes_range = wrapper.sodium.QRNG.QRNG_randombytes_range; 153 } 154 155 @system 156 unittest 157 { 158 import std.stdio : writeln, writefln; 159 import std.algorithm.searching : any, all; 160 import std.algorithm.comparison : equal; 161 import std.range : array, take, enumerate, iota; 162 debug writeln("unittest block 1 from sodium.randombytes.d"); 163 164 ubyte[] buf = new ubyte[8]; 165 assert(!any(buf)); // none of buf evaluate to true 166 167 //randombytes_buf 168 randombytes_buf(buf.ptr, buf.length); 169 assert( any(buf)); 170 writefln("Unpredictable sequence of %s bytes: %s", buf.length, buf); 171 172 //randombytes_implementation_name 173 { 174 static import deimos.sodium.randombytes; 175 writeln("deimos.sodium.randombytes.randombytes_implementation_name(): ", fromStringz(deimos.sodium.randombytes.randombytes_implementation_name())); 176 } 177 //randombytes 178 buf[] = ubyte.init; 179 assert(!any(buf)); // none of buf evaluate to true 180 randombytes(buf.ptr, buf.length); 181 assert( any(buf)); 182 writefln("Unpredictable sequence of %s bytes: %s", buf.length, buf); 183 184 foreach (i,element; randombytes_range().take(8).enumerate(1)) 185 { /* writefln("%s: %02X", i, element); */ } 186 // writeln; 187 188 int cnt; 189 foreach (element; randombytes_range()) { 190 if (++cnt > 8) 191 break; 192 // writefln("%s: %02X", cnt, element); 193 } 194 195 ubyte[] populated_from_infinite_range = array(randombytes_range().take(8)); 196 // writefln("0x %(%02X %)", populated_from_infinite_range); 197 198 //randombytes_buf_deterministic 199 immutable ubyte[randombytes_SEEDBYTES] seed = array(iota(ubyte(0), randombytes_SEEDBYTES))[]; 200 randombytes_buf_deterministic(buf.ptr, buf.length, seed); 201 202 debug(TRAVIS_TEST) 203 { 204 /* 205 This block runs successfully but is disabled in regular builds as it does dubious actions; only for a test run by travis/code coverage): 206 The documentation says about function randombytes_set_implementation: 207 "This function should only be called once, before sodium_init()." 208 As we are calling after sodium_init() and twice: 209 Thus for testing during development, debug(TRAVIS_TEST) may be 'activated' temporarily. 210 */ 211 extern(C) const(char)* dummy_implementation_name() 212 { 213 static const(char)[] str = "dummy" ~ '\0'; 214 return str.ptr; 215 } 216 217 extern(C) uint dummy_random() 218 { 219 return 1234567890; 220 } 221 222 extern(C) void dummy_buf(void* buf, const size_t size) 223 { 224 import core.stdc..string : memcpy; 225 size_t len_remaining = size; 226 ubyte[] buffer_ubyte = new ubyte[size]; 227 while (len_remaining) { 228 buffer_ubyte[size-len_remaining] = len_remaining % 0xff; 229 --len_remaining; 230 } 231 memcpy(buf, buffer_ubyte.ptr, buffer_ubyte.length); 232 } 233 234 static import deimos.sodium.randombytes; 235 extern(C) __gshared deimos.sodium.randombytes.randombytes_implementation randombytes_dummy_implementation; 236 randombytes_dummy_implementation.implementation_name = &dummy_implementation_name; 237 randombytes_dummy_implementation.random = &dummy_random; 238 randombytes_dummy_implementation.buf = &dummy_buf; 239 { 240 deimos.sodium.randombytes.randombytes_set_implementation(&randombytes_dummy_implementation);// __attribute__ ((nonnull)); 241 scope(exit) { // reestablish the default 242 import wrapper.sodium.randombytes_sysrandom; 243 deimos.sodium.randombytes.randombytes_set_implementation(&randombytes_sysrandom_implementation); 244 } 245 // test our non-random number generator being set and working as expected 246 ubyte[] buffer = new ubyte[8]; 247 assert(!any(buffer)); // none of buf evaluate to true 248 //randombytes_buf 249 randombytes_buf(buffer.ptr, buffer.length); 250 assert(equal(buffer, [8, 7, 6, 5, 4, 3, 2, 1])); 251 writefln(" predictable sequence of %s bytes: %s", buffer.length, buffer); 252 253 //randombytes_random 254 uint predictable = randombytes_random(); 255 assert(predictable == 1234567890); 256 writeln(" predictable uint: ", predictable); 257 258 //randombytes_implementation_name 259 string impl_name = randombytes_implementation_name(); 260 assert(impl_name == "dummy"); 261 writeln("wrapper.sodium.randombytes.randombytes_implementation_name(): ", impl_name); 262 } 263 } // debug(TRAVIS_TEST) 264 } 265 266 @safe 267 unittest 268 { 269 import std.stdio : writeln, writefln; 270 import std.algorithm.searching : any, all; 271 import std.range : iota, array, take, takeExactly; 272 273 writeln("unittest block 2 from sodium.randombytes.d"); 274 275 assert(randombytes_SEEDBYTES == randombytes_seedbytes()); 276 277 //randombytes_random 278 debug writeln("Unpredictable uint: ", randombytes_random()); 279 280 //randombytes_uniform 281 debug writeln("Unpredictable uint between 0 and 48: ", randombytes_uniform(49)); 282 /* 283 uint[] six_outof_49; 284 do { 285 uint r = randombytes_uniform(49) +1; 286 if (!canFind(six_outof_49, r)) 287 six_outof_49 ~= r; 288 } while (six_outof_49.length<6); 289 sort(six_outof_49); 290 writeln(six_outof_49); 291 */ 292 293 //randombytes_close 294 randombytes_close(); 295 //randombytes_stir 296 randombytes_stir(); 297 298 //randombytes_implementation_name 299 string impl_name = randombytes_implementation_name(); 300 writeln("wrapper.sodium.randombytes.randombytes_implementation_name(): ", impl_name); 301 assert(impl_name == "sysrandom"); 302 303 //randombytes 304 ubyte[] buf = new ubyte[8]; 305 assert(!any(buf)); // none of buf evaluate to true 306 307 randombytes(buf); 308 assert( any(buf)); 309 randombytes(null); // whether randombytes accepts null though it's attributed __attribute__ ((nonnull)); 310 randombytes_buf(buf); 311 312 //randombytes_buf_deterministic 313 immutable ubyte[randombytes_SEEDBYTES] seed = array(iota(ubyte(0), randombytes_SEEDBYTES))[]; 314 randombytes_buf_deterministic(buf, seed); 315 } 316 317 @nogc @safe 318 unittest { 319 ubyte[4] a; 320 ubyte[randombytes_SEEDBYTES] seed; 321 randombytes(a); 322 randombytes_buf_deterministic(a, seed); 323 }