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