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 }