• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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