1/*
2 * Copyright (c) 1996, David Mazieres <dm@uun.org>
3 * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
4 * Copyright (C) 2017 Apple Inc. All rights reserved.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/*
20 * Arc4 random number generator for OpenBSD.
21 *
22 * This code is derived from section 17.1 of Applied Cryptography,
23 * second edition, which describes a stream cipher allegedly
24 * compatible with RSA Labs "RC4" cipher (the actual description of
25 * which is a trade secret). The same algorithm is used as a stream
26 * cipher called "arcfour" in Tatu Ylonen's ssh package.
27 *
28 * RC4 is a registered trademark of RSA Laboratories.
29 */
30
31#include "CryptoRandom.h"
32
33#include "BAssert.h"
34#include "BPlatform.h"
35#include "Mutex.h"
36#include "StaticPerProcess.h"
37#include "VMAllocate.h"
38#include <mutex>
39
40#if !BOS(DARWIN)
41#include <errno.h>
42#include <fcntl.h>
43#include <unistd.h>
44#endif
45
46#if BOS(DARWIN)
47#include <CommonCrypto/CommonCryptoError.h>
48#include <CommonCrypto/CommonRandom.h>
49#endif
50
51namespace bmalloc {
52
53class ARC4Stream {
54public:
55 ARC4Stream();
56
57 uint8_t i;
58 uint8_t j;
59 uint8_t s[256];
60};
61
62class ARC4RandomNumberGenerator : public StaticPerProcess<ARC4RandomNumberGenerator> {
63public:
64 ARC4RandomNumberGenerator(const std::lock_guard<Mutex>&);
65
66 uint32_t randomNumber();
67 void randomValues(void* buffer, size_t length);
68
69private:
70 inline void addRandomData(unsigned char *data, int length);
71 void stir();
72 void stirIfNeeded();
73 inline uint8_t getByte();
74
75 ARC4Stream m_stream;
76 int m_count;
77};
78DECLARE_STATIC_PER_PROCESS_STORAGE(ARC4RandomNumberGenerator);
79DEFINE_STATIC_PER_PROCESS_STORAGE(ARC4RandomNumberGenerator);
80
81ARC4Stream::ARC4Stream()
82{
83 for (int n = 0; n < 256; n++)
84 s[n] = n;
85 i = 0;
86 j = 0;
87}
88
89ARC4RandomNumberGenerator::ARC4RandomNumberGenerator(const std::lock_guard<Mutex>&)
90 : m_count(0)
91{
92}
93
94void ARC4RandomNumberGenerator::addRandomData(unsigned char* data, int length)
95{
96 m_stream.i--;
97 for (int n = 0; n < 256; n++) {
98 m_stream.i++;
99 uint8_t si = m_stream.s[m_stream.i];
100 m_stream.j += si + data[n % length];
101 m_stream.s[m_stream.i] = m_stream.s[m_stream.j];
102 m_stream.s[m_stream.j] = si;
103 }
104 m_stream.j = m_stream.i;
105}
106
107void ARC4RandomNumberGenerator::stir()
108{
109 unsigned char randomness[128];
110 size_t length = sizeof(randomness);
111
112#if BOS(DARWIN)
113 RELEASE_BASSERT(!CCRandomGenerateBytes(randomness, length));
114#else
115 static std::once_flag onceFlag;
116 static int fd;
117 std::call_once(
118 onceFlag,
119 [] {
120 int ret = 0;
121 do {
122 ret = open("/dev/urandom", O_RDONLY, 0);
123 } while (ret == -1 && errno == EINTR);
124 RELEASE_BASSERT(ret >= 0);
125 fd = ret;
126 });
127 ssize_t amountRead = 0;
128 while (static_cast<size_t>(amountRead) < length) {
129 ssize_t currentRead = read(fd, randomness + amountRead, length - amountRead);
130 // We need to check for both EAGAIN and EINTR since on some systems /dev/urandom
131 // is blocking and on others it is non-blocking.
132 if (currentRead == -1)
133 RELEASE_BASSERT(errno == EAGAIN || errno == EINTR);
134 else
135 amountRead += currentRead;
136 }
137#endif
138
139 addRandomData(randomness, length);
140
141 // Discard early keystream, as per recommendations in:
142 // http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
143 for (int i = 0; i < 256; i++)
144 getByte();
145 m_count = 1600000;
146}
147
148void ARC4RandomNumberGenerator::stirIfNeeded()
149{
150 if (m_count <= 0)
151 stir();
152}
153
154uint8_t ARC4RandomNumberGenerator::getByte()
155{
156 m_stream.i++;
157 uint8_t si = m_stream.s[m_stream.i];
158 m_stream.j += si;
159 uint8_t sj = m_stream.s[m_stream.j];
160 m_stream.s[m_stream.i] = sj;
161 m_stream.s[m_stream.j] = si;
162 return (m_stream.s[(si + sj) & 0xff]);
163}
164
165void ARC4RandomNumberGenerator::randomValues(void* buffer, size_t length)
166{
167 std::lock_guard<Mutex> lock(mutex());
168
169 unsigned char* result = reinterpret_cast<unsigned char*>(buffer);
170 stirIfNeeded();
171 while (length--) {
172 m_count--;
173 stirIfNeeded();
174 result[length] = getByte();
175 }
176}
177
178void cryptoRandom(void* buffer, size_t length)
179{
180 ARC4RandomNumberGenerator::get()->randomValues(buffer, length);
181}
182
183} // namespace bmalloc
184
185