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 }