1 /**
2 * /dev/random EntropySource
3 * 
4 * Copyright:
5 * (C) 1999-2009 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.entropy.dev_random;
12 
13 version(Posix):
14 import botan.constants;
15 static if (BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM):
16 
17 import botan.entropy.entropy_src;
18 import botan.utils.types;
19 import core.stdc.config;
20 import core.sys.posix.sys.types;
21 import core.sys.posix.sys.select;
22 import core.sys.posix.sys.stat;
23 import core.sys.posix.unistd;
24 import core.sys.posix.fcntl;
25 import core.stdc.string;
26 import botan.utils.rounding;
27 import std.string : toStringz;
28 import std.algorithm : max;
29 
30 /**
31 * Entropy source reading from kernel devices like /dev/random
32 */
33 final class DeviceEntropySource : EntropySource
34 {
35 public:
36     @property string name() const { return "RNG Device Reader"; }
37 
38     /**
39     * Gather entropy from a RNG device
40     */
41     void poll(ref EntropyAccumulator accum)
42     {
43         if (m_devices.empty)
44             return;
45         
46         __gshared immutable size_t ENTROPY_BITS_PER_BYTE = 8;
47         __gshared immutable size_t MS_WAIT_TIME = 32;
48         __gshared immutable size_t READ_ATTEMPT = 32;
49         
50         int max_fd = m_devices[0];
51         fd_set read_set;
52         FD_ZERO(&read_set);
53         foreach (device; m_devices[])
54         {
55             FD_SET(device, &read_set);
56             max_fd = max(device, max_fd);
57         }
58         
59         timeval timeout;
60         
61         timeout.tv_sec = (MS_WAIT_TIME / 1000);
62         timeout.tv_usec = (MS_WAIT_TIME % 1000) * 1000;
63         
64         if (select(max_fd + 1, &read_set, (fd_set*).init, (fd_set*).init, &timeout) < 0)
65             return;
66         
67         SecureVector!ubyte* io_buffer = &accum.getIoBuffer(READ_ATTEMPT);
68 
69         foreach (device; m_devices[])
70         {
71             if (FD_ISSET(device, &read_set))
72             {
73                 const ssize_t got = read(device, io_buffer.ptr, io_buffer.length);
74                 if (got > 0)
75                     accum.add(io_buffer.ptr, got, ENTROPY_BITS_PER_BYTE);
76             }
77         }
78     }
79 
80 
81     /**
82     Device_EntropySource constructor
83     Open a file descriptor to each (available) device in fsnames
84     */
85     this()(auto const ref Vector!string fsnames)
86     {
87         enum O_NONBLOCK = 0;
88         enum O_NOCTTY = 0;
89         
90         const int flags = O_RDONLY | O_NONBLOCK | O_NOCTTY;
91         
92         foreach (fsname; fsnames[])
93         {
94             FDType fd = open(fsname.toStringz, flags);
95             
96             if (fd >= 0 && fd < FD_SETSIZE)
97                 m_devices.pushBack(fd);
98             else if (fd >= 0)
99                 close(fd);
100         }
101     }
102 
103     ~this()
104     {
105         foreach (device; m_devices[])
106             close(device);
107     }
108 private:
109     alias FDType = int;
110 
111     Vector!FDType m_devices;
112 }
113 
114 @nogc nothrow pure private:
115 
116 alias __fd_mask = c_long;
117 enum uint __NFDBITS = 8 * __fd_mask.sizeof;
118 
119 auto __FDELT( int d )
120 {
121     return d / __NFDBITS;
122 }
123 
124 auto __FDMASK( int d )
125 {
126     return cast(__fd_mask) 1 << ( d % __NFDBITS );
127 }
128 
129 enum FD_SETSIZE = 1024;
130 
131 void FD_CLR( int fd, fd_set* fdset )
132 {
133     fdset.fds_bits[__FDELT( fd )] &= ~__FDMASK( fd );
134 }
135 
136 bool FD_ISSET( int fd, const(fd_set)* fdset )
137 {
138     return (fdset.fds_bits[__FDELT( fd )] & __FDMASK( fd )) != 0;
139 }
140 
141 void FD_SET( int fd, fd_set* fdset )
142 {
143     fdset.fds_bits[__FDELT( fd )] |= __FDMASK( fd );
144 }
145 
146 void FD_ZERO( fd_set* fdset )
147 {
148     fdset.fds_bits[0 .. $] = 0;
149 }