1 module tagion.crypto.random.random;
2 
3 import std.format;
4 import std.system : os;
5 import std.traits;
6 import tagion.basic.Version;
7 
8 static if (ver.iOS || ver.OSX || ver.BSD || ver.Android) {
9     enum is_getrandom = false;
10     extern (C) void arc4random_buf(void* buf, size_t buflen) nothrow;
11 }
12 else static if (ver.linux) {
13     enum is_getrandom = true;
14     extern (C) ptrdiff_t getrandom(void* buf, size_t buflen, uint flags) nothrow;
15 }
16 else {
17     static assert(0, format("Random function not support for %s", os));
18 }
19 
20 bool isGetRandomAvailable() nothrow {
21     import core.stdc.errno;
22 
23     static if (is_getrandom) {
24         enum GRND_NONBLOCK = 0x0001;
25         const res = getrandom(null, 0, GRND_NONBLOCK);
26         if (res < 0) {
27             switch (errno) {
28             case ENOSYS:
29             case EPERM:
30                 return false;
31             default:
32                 return true;
33             }
34         }
35         else {
36             return true;
37         }
38     }
39     else {
40         return true;
41     }
42 }
43 
44 unittest {
45     assert(isGetRandomAvailable, "hardware random function is not available in this environment");
46 }
47 
48 /++
49 + getRandom - runs platform specific random function.
50 +/
51 @trusted
52 void getRandom(ref scope ubyte[] buf) nothrow
53 in (buf.length <= 256)
54 do {
55     if (buf.length == 0) {
56         return;
57     }
58     static if (is_getrandom) {
59         enum sikkenogetskidt = "Problem with random generation";
60         // GRND_NONBLOCK = 0x0001. Don't block and return EAGAIN instead
61         enum GRND_RANDOM = 0x0002; // No effect
62         // GRND_INSECURE = 0x0004. Return non-cryptographic random bytes
63         const size = getrandom(&buf[0], buf.length, GRND_RANDOM);
64         assert(size == buf.length, sikkenogetskidt);
65     }
66     else {
67         arc4random_buf(&buf[0], buf.length);
68     }
69 }
70 
71 @trusted
72 T getRandom(T)() nothrow if (isBasicType!T) {
73     T result;
74     auto buf = (cast(ubyte*)&result)[0 .. T.sizeof];
75     getRandom(buf);
76     return result;
77 
78 }