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