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 | |
51 | namespace bmalloc { |
52 | |
53 | class ARC4Stream { |
54 | public: |
55 | ARC4Stream(); |
56 | |
57 | uint8_t i; |
58 | uint8_t j; |
59 | uint8_t s[256]; |
60 | }; |
61 | |
62 | class ARC4RandomNumberGenerator : public StaticPerProcess<ARC4RandomNumberGenerator> { |
63 | public: |
64 | ARC4RandomNumberGenerator(const std::lock_guard<Mutex>&); |
65 | |
66 | uint32_t randomNumber(); |
67 | void randomValues(void* buffer, size_t length); |
68 | |
69 | private: |
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 | }; |
78 | DECLARE_STATIC_PER_PROCESS_STORAGE(ARC4RandomNumberGenerator); |
79 | DEFINE_STATIC_PER_PROCESS_STORAGE(ARC4RandomNumberGenerator); |
80 | |
81 | ARC4Stream::ARC4Stream() |
82 | { |
83 | for (int n = 0; n < 256; n++) |
84 | s[n] = n; |
85 | i = 0; |
86 | j = 0; |
87 | } |
88 | |
89 | ARC4RandomNumberGenerator::ARC4RandomNumberGenerator(const std::lock_guard<Mutex>&) |
90 | : m_count(0) |
91 | { |
92 | } |
93 | |
94 | void 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 | |
107 | void 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 | |
148 | void ARC4RandomNumberGenerator::stirIfNeeded() |
149 | { |
150 | if (m_count <= 0) |
151 | stir(); |
152 | } |
153 | |
154 | uint8_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 | |
165 | void 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 | |
178 | void cryptoRandom(void* buffer, size_t length) |
179 | { |
180 | ARC4RandomNumberGenerator::get()->randomValues(buffer, length); |
181 | } |
182 | |
183 | } // namespace bmalloc |
184 | |
185 | |