1 /* Copyright 2020 The BoringSSL Authors
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15 #include <openssl/ctrdrbg.h>
16
17 #include "../bcm_support.h"
18 #include "../fipsmodule/bcm_interface.h"
19 #include "../internal.h"
20
21 #if defined(BORINGSSL_FIPS)
22
23 #include <atomic>
24
25 // passive_get_seed_entropy writes |out_entropy_len| bytes of entropy, suitable
26 // for seeding a DRBG, to |out_entropy|. It sets |*out_used_cpu| to one if the
27 // entropy came directly from the CPU and zero if it came from the OS. It
28 // actively obtains entropy from the CPU/OS
passive_get_seed_entropy(uint8_t * out_entropy,size_t out_entropy_len,int * out_want_additional_input)29 static void passive_get_seed_entropy(uint8_t *out_entropy,
30 size_t out_entropy_len,
31 int *out_want_additional_input) {
32 *out_want_additional_input = 0;
33 if (bcm_success(BCM_rand_bytes_hwrng(out_entropy, out_entropy_len))) {
34 *out_want_additional_input = 1;
35 } else {
36 CRYPTO_sysrand_for_seed(out_entropy, out_entropy_len);
37 }
38 }
39
40 #define ENTROPY_READ_LEN \
41 (/* last_block size */ 16 + CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD)
42
43 #if defined(OPENSSL_ANDROID)
44
45 #include <errno.h>
46 #include <sys/socket.h>
47 #include <sys/types.h>
48 #include <sys/un.h>
49 #include <unistd.h>
50
51 // socket_history_t enumerates whether the entropy daemon should be contacted
52 // for a given entropy request. Values other than socket_not_yet_attempted are
53 // sticky so if the first attempt to read from the daemon fails it's assumed
54 // that the daemon is not present and no more attempts will be made. If the
55 // first attempt is successful then attempts will be made forever more.
56 enum class socket_history_t {
57 // initial value, no connections to the entropy daemon have been made yet.
58 socket_not_yet_attempted = 0,
59 // reading from the entropy daemon was successful
60 socket_success,
61 // reading from the entropy daemon failed.
62 socket_failed,
63 };
64
65 static std::atomic<socket_history_t> g_socket_history{
66 socket_history_t::socket_not_yet_attempted};
67
68 // DAEMON_RESPONSE_LEN is the number of bytes that the entropy daemon replies
69 // with.
70 #define DAEMON_RESPONSE_LEN 496
71
72 static_assert(ENTROPY_READ_LEN == DAEMON_RESPONSE_LEN,
73 "entropy daemon response length mismatch");
74
get_seed_from_daemon(uint8_t * out_entropy,size_t out_entropy_len)75 static int get_seed_from_daemon(uint8_t *out_entropy, size_t out_entropy_len) {
76 // |RAND_need_entropy| should never call this function for more than
77 // |DAEMON_RESPONSE_LEN| bytes.
78 if (out_entropy_len > DAEMON_RESPONSE_LEN) {
79 abort();
80 }
81
82 const socket_history_t socket_history =
83 g_socket_history.load(std::memory_order_acquire);
84 if (socket_history == socket_history_t::socket_failed) {
85 return 0;
86 }
87
88 int ret = 0;
89 static const char kSocketPath[] = "/dev/socket/prng_seeder";
90 struct sockaddr_un sun;
91 uint8_t buffer[DAEMON_RESPONSE_LEN];
92 size_t done = 0;
93 const int sock = socket(AF_UNIX, SOCK_STREAM, 0);
94 if (sock < 0) {
95 goto out;
96 }
97
98 memset(&sun, 0, sizeof(sun));
99 sun.sun_family = AF_UNIX;
100 static_assert(sizeof(kSocketPath) <= UNIX_PATH_MAX, "kSocketPath too long");
101 OPENSSL_memcpy(sun.sun_path, kSocketPath, sizeof(kSocketPath));
102
103 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun))) {
104 goto out;
105 }
106
107 while (done < sizeof(buffer)) {
108 ssize_t n;
109 do {
110 n = read(sock, buffer + done, sizeof(buffer) - done);
111 } while (n == -1 && errno == EINTR);
112
113 if (n < 1) {
114 goto out;
115 }
116 done += n;
117 }
118
119 if (done != DAEMON_RESPONSE_LEN) {
120 // The daemon should always write |DAEMON_RESPONSE_LEN| bytes on every
121 // connection.
122 goto out;
123 }
124
125 assert(out_entropy_len <= DAEMON_RESPONSE_LEN);
126 OPENSSL_memcpy(out_entropy, buffer, out_entropy_len);
127 ret = 1;
128
129 out:
130 if (socket_history == socket_history_t::socket_not_yet_attempted) {
131 socket_history_t expected = socket_history_t::socket_not_yet_attempted;
132 // If another thread has already updated |g_socket_history| then we defer
133 // to their value.
134 g_socket_history.compare_exchange_strong(
135 expected,
136 (ret == 0) ? socket_history_t::socket_failed
137 : socket_history_t::socket_success,
138 std::memory_order_release, std::memory_order_relaxed);
139 }
140
141 close(sock);
142 return ret;
143 }
144
145 #else
146
get_seed_from_daemon(uint8_t * out_entropy,size_t out_entropy_len)147 static int get_seed_from_daemon(uint8_t *out_entropy, size_t out_entropy_len) {
148 return 0;
149 }
150
151 #endif // OPENSSL_ANDROID
152
153 // RAND_need_entropy is called by the FIPS module when it has blocked because of
154 // a lack of entropy. This signal is used as an indication to feed it more.
RAND_need_entropy(size_t bytes_needed)155 void RAND_need_entropy(size_t bytes_needed) {
156 uint8_t buf[ENTROPY_READ_LEN];
157 size_t todo = sizeof(buf);
158 if (todo > bytes_needed) {
159 todo = bytes_needed;
160 }
161
162 int want_additional_input;
163 if (get_seed_from_daemon(buf, todo)) {
164 want_additional_input = 1;
165 } else {
166 passive_get_seed_entropy(buf, todo, &want_additional_input);
167 }
168
169 if (boringssl_fips_break_test("CRNG")) {
170 // This breaks the "continuous random number generator test" defined in FIPS
171 // 140-2, section 4.9.2, and implemented in |rand_get_seed|.
172 OPENSSL_memset(buf, 0, todo);
173 }
174
175 BCM_rand_load_entropy(buf, todo, want_additional_input);
176 }
177
178 #endif // FIPS
179