Coverage Report

Created: 2020-03-26 13:53

/src/botan/src/lib/entropy/dev_random/dev_random.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* Reader of /dev/random and company
3
* (C) 1999-2009,2013 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7
8
#include <botan/internal/dev_random.h>
9
#include <botan/exceptn.h>
10
11
#include <sys/types.h>
12
#include <sys/select.h>
13
#include <sys/stat.h>
14
#include <unistd.h>
15
#include <errno.h>
16
#include <fcntl.h>
17
18
namespace Botan {
19
20
/**
21
Device_EntropySource constructor
22
Open a file descriptor to each (available) device in fsnames
23
*/
24
Device_EntropySource::Device_EntropySource(const std::vector<std::string>& fsnames)
25
0
   {
26
#ifndef O_NONBLOCK
27
  #define O_NONBLOCK 0
28
#endif
29
30
#ifndef O_NOCTTY
31
  #define O_NOCTTY 0
32
#endif
33
34
0
   const int flags = O_RDONLY | O_NONBLOCK | O_NOCTTY;
35
0
36
0
   m_max_fd = 0;
37
0
38
0
   for(auto fsname : fsnames)
39
0
      {
40
0
      int fd = ::open(fsname.c_str(), flags);
41
0
42
0
      if(fd < 0)
43
0
         {
44
0
         /*
45
0
         ENOENT or EACCES is normal as some of the named devices may not exist
46
0
         on this system. But any other errno value probably indicates
47
0
         either a bug in the application or file descriptor exhaustion.
48
0
         */
49
0
         if(errno != ENOENT && errno != EACCES)
50
0
            throw System_Error("Opening OS RNG device failed", errno);
51
0
         }
52
0
      else
53
0
         {
54
0
         if(fd > FD_SETSIZE)
55
0
            {
56
0
            ::close(fd);
57
0
            throw Invalid_State("Open of OS RNG succeeded but returned fd is too large for fd_set");
58
0
            }
59
0
60
0
         m_dev_fds.push_back(fd);
61
0
         m_max_fd = std::max(m_max_fd, fd);
62
0
         }
63
0
      }
64
0
   }
65
66
/**
67
Device_EntropySource destructor: close all open devices
68
*/
69
Device_EntropySource::~Device_EntropySource()
70
0
   {
71
0
   for(int fd : m_dev_fds)
72
0
      {
73
0
      // ignoring return value here, can't throw in destructor anyway
74
0
      ::close(fd);
75
0
      }
76
0
   }
77
78
/**
79
* Gather entropy from a RNG device
80
*/
81
size_t Device_EntropySource::poll(RandomNumberGenerator& rng)
82
0
   {
83
0
   size_t bits = 0;
84
0
85
0
   if(m_dev_fds.size() > 0)
86
0
      {
87
0
      fd_set read_set;
88
0
      FD_ZERO(&read_set);
89
0
90
0
      for(int dev_fd : m_dev_fds)
91
0
         {
92
0
         FD_SET(dev_fd, &read_set);
93
0
         }
94
0
95
0
      secure_vector<uint8_t> io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST);
96
0
97
0
      struct ::timeval timeout;
98
0
      timeout.tv_sec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS / 1000);
99
0
      timeout.tv_usec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS % 1000) * 1000;
100
0
101
0
      if(::select(m_max_fd + 1, &read_set, nullptr, nullptr, &timeout) > 0)
102
0
         {
103
0
         for(int dev_fd : m_dev_fds)
104
0
            {
105
0
            if(FD_ISSET(dev_fd, &read_set))
106
0
               {
107
0
               const ssize_t got = ::read(dev_fd, io_buf.data(), io_buf.size());
108
0
109
0
               if(got > 0)
110
0
                  {
111
0
                  rng.add_entropy(io_buf.data(), static_cast<size_t>(got));
112
0
                  bits += got * 8;
113
0
                  }
114
0
               }
115
0
            }
116
0
         }
117
0
      }
118
0
119
0
   return bits;
120
0
   }
121
122
}