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 }