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 }