1 /*
2 * Copyright (C) 2018 Rafael Ostertag
3 *
4 * This file is part of YAPET.
5 *
6 * YAPET is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
10 *
11 * YAPET is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * YAPET. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Additional permission under GNU GPL version 3 section 7
20 *
21 * If you modify this program, or any covered work, by linking or combining it
22 * with the OpenSSL project's OpenSSL library (or a modified version of that
23 * library), containing parts covered by the terms of the OpenSSL or SSLeay
24 * licenses, Rafael Ostertag grants you additional permission to convey the
25 * resulting work. Corresponding Source for a non-source form of such a
26 * combination shall include the source code for the parts of OpenSSL used as
27 * well as that of the covered work.
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37 #ifdef HAVE_FCNTL_H
38 #include <fcntl.h>
39 #endif
40
41 #include <assert.h>
42
43 #include "consts.h"
44 #include "intl.h"
45 #include "rngengine.hh"
46
47 using namespace yapet::pwgen;
48
49 namespace {
50 constexpr char DEV_URANDOM[]{"/dev/urandom"};
51 constexpr int BAD_FD{-1};
52
openDevUrandom()53 int openDevUrandom() {
54 auto fd{::open(DEV_URANDOM, O_RDONLY)};
55 if (fd == BAD_FD) {
56 char msg[YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE];
57 std::snprintf(msg, YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE,
58 _("Cannot open '%s'"), DEV_URANDOM);
59 throw std::runtime_error(msg);
60 }
61 return fd;
62 }
63
closeFd(int fd)64 void closeFd(int fd) {
65 if (fd != BAD_FD) {
66 ::close(fd);
67 }
68 }
69 } // namespace
70
fillCache()71 void RngEngine::fillCache() {
72 int retval = ::read(fd, &byteCache, BYTE_CACHE_SIZE);
73 if (retval != BYTE_CACHE_SIZE) {
74 throw std::runtime_error(_("Error reading random bytes from file"));
75 }
76 positionInCache = 0;
77 }
78
RngEngine()79 RngEngine::RngEngine() : fd{openDevUrandom()}, positionInCache{EMPTY_CACHE} {}
80
~RngEngine()81 RngEngine::~RngEngine() { closeFd(fd); }
82
RngEngine(const RngEngine & other)83 RngEngine::RngEngine(const RngEngine& other)
84 : byteCache{other.byteCache},
85 positionInCache{other.positionInCache} {
86 fd = ::dup(other.fd);
87 }
88
RngEngine(RngEngine && other)89 RngEngine::RngEngine(RngEngine&& other)
90 : fd{other.fd},
91 byteCache{other.byteCache},
92 positionInCache{other.positionInCache} {
93 other.fd = BAD_FD;
94 other.positionInCache = EMPTY_CACHE;
95 }
96
operator =(const RngEngine & other)97 RngEngine& RngEngine::operator=(const RngEngine& other) {
98 if (this == &other) return *this;
99
100 closeFd(fd);
101
102 fd = ::dup(fd);
103 byteCache = other.byteCache;
104 positionInCache = other.positionInCache;
105
106 return *this;
107 }
108
operator =(RngEngine && other)109 RngEngine& RngEngine::operator=(RngEngine&& other) {
110 if (this == &other) return *this;
111
112 closeFd(fd);
113
114 fd = other.fd;
115 other.fd = BAD_FD;
116
117 byteCache = std::move(other.byteCache);
118 positionInCache = other.positionInCache;
119 other.positionInCache = EMPTY_CACHE;
120
121 return *this;
122 }
123
operator ()()124 std::uint8_t RngEngine::operator()() {
125 if (positionInCache == EMPTY_CACHE || positionInCache >= BYTE_CACHE_SIZE) {
126 fillCache();
127 }
128 assert(positionInCache >= 0 && positionInCache < BYTE_CACHE_SIZE);
129
130 return byteCache[positionInCache++];
131 }
132