1a8e1175bSopenharmony_ci/*
2a8e1175bSopenharmony_ci *  Platform-specific and custom entropy polling functions
3a8e1175bSopenharmony_ci *
4a8e1175bSopenharmony_ci *  Copyright The Mbed TLS Contributors
5a8e1175bSopenharmony_ci *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
6a8e1175bSopenharmony_ci */
7a8e1175bSopenharmony_ci
8a8e1175bSopenharmony_ci#if (defined(__linux__) || defined(__midipix__)) && !defined(_GNU_SOURCE)
9a8e1175bSopenharmony_ci/* Ensure that syscall() is available even when compiling with -std=c99 */
10a8e1175bSopenharmony_ci#define _GNU_SOURCE
11a8e1175bSopenharmony_ci#endif
12a8e1175bSopenharmony_ci
13a8e1175bSopenharmony_ci#include "common.h"
14a8e1175bSopenharmony_ci
15a8e1175bSopenharmony_ci#include <string.h>
16a8e1175bSopenharmony_ci
17a8e1175bSopenharmony_ci#if defined(MBEDTLS_ENTROPY_C)
18a8e1175bSopenharmony_ci
19a8e1175bSopenharmony_ci#include "mbedtls/entropy.h"
20a8e1175bSopenharmony_ci#include "entropy_poll.h"
21a8e1175bSopenharmony_ci#include "mbedtls/error.h"
22a8e1175bSopenharmony_ci
23a8e1175bSopenharmony_ci#if defined(MBEDTLS_TIMING_C)
24a8e1175bSopenharmony_ci#include "mbedtls/timing.h"
25a8e1175bSopenharmony_ci#endif
26a8e1175bSopenharmony_ci#include "mbedtls/platform.h"
27a8e1175bSopenharmony_ci
28a8e1175bSopenharmony_ci#if !defined(MBEDTLS_NO_PLATFORM_ENTROPY)
29a8e1175bSopenharmony_ci
30a8e1175bSopenharmony_ci#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \
31a8e1175bSopenharmony_ci    !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \
32a8e1175bSopenharmony_ci    !defined(__HAIKU__) && !defined(__midipix__) && !defined(__MVS__)
33a8e1175bSopenharmony_ci#error \
34a8e1175bSopenharmony_ci    "Platform entropy sources only work on Unix and Windows, see MBEDTLS_NO_PLATFORM_ENTROPY in mbedtls_config.h"
35a8e1175bSopenharmony_ci#endif
36a8e1175bSopenharmony_ci
37a8e1175bSopenharmony_ci#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
38a8e1175bSopenharmony_ci
39a8e1175bSopenharmony_ci#include <windows.h>
40a8e1175bSopenharmony_ci#include <bcrypt.h>
41a8e1175bSopenharmony_ci#include <intsafe.h>
42a8e1175bSopenharmony_ci
43a8e1175bSopenharmony_ciint mbedtls_platform_entropy_poll(void *data, unsigned char *output, size_t len,
44a8e1175bSopenharmony_ci                                  size_t *olen)
45a8e1175bSopenharmony_ci{
46a8e1175bSopenharmony_ci    ((void) data);
47a8e1175bSopenharmony_ci    *olen = 0;
48a8e1175bSopenharmony_ci
49a8e1175bSopenharmony_ci    /*
50a8e1175bSopenharmony_ci     * BCryptGenRandom takes ULONG for size, which is smaller than size_t on
51a8e1175bSopenharmony_ci     * 64-bit Windows platforms. Extract entropy in chunks of len (dependent
52a8e1175bSopenharmony_ci     * on ULONG_MAX) size.
53a8e1175bSopenharmony_ci     */
54a8e1175bSopenharmony_ci    while (len != 0) {
55a8e1175bSopenharmony_ci        unsigned long ulong_bytes =
56a8e1175bSopenharmony_ci            (len > ULONG_MAX) ? ULONG_MAX : (unsigned long) len;
57a8e1175bSopenharmony_ci
58a8e1175bSopenharmony_ci        if (!BCRYPT_SUCCESS(BCryptGenRandom(NULL, output, ulong_bytes,
59a8e1175bSopenharmony_ci                                            BCRYPT_USE_SYSTEM_PREFERRED_RNG))) {
60a8e1175bSopenharmony_ci            return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
61a8e1175bSopenharmony_ci        }
62a8e1175bSopenharmony_ci
63a8e1175bSopenharmony_ci        *olen += ulong_bytes;
64a8e1175bSopenharmony_ci        len -= ulong_bytes;
65a8e1175bSopenharmony_ci    }
66a8e1175bSopenharmony_ci
67a8e1175bSopenharmony_ci    return 0;
68a8e1175bSopenharmony_ci}
69a8e1175bSopenharmony_ci#else /* _WIN32 && !EFIX64 && !EFI32 */
70a8e1175bSopenharmony_ci
71a8e1175bSopenharmony_ci/*
72a8e1175bSopenharmony_ci * Test for Linux getrandom() support.
73a8e1175bSopenharmony_ci * Since there is no wrapper in the libc yet, use the generic syscall wrapper
74a8e1175bSopenharmony_ci * available in GNU libc and compatible libc's (eg uClibc).
75a8e1175bSopenharmony_ci */
76a8e1175bSopenharmony_ci#if ((defined(__linux__) && defined(__GLIBC__)) || defined(__midipix__))
77a8e1175bSopenharmony_ci#include <unistd.h>
78a8e1175bSopenharmony_ci#include <sys/syscall.h>
79a8e1175bSopenharmony_ci#if defined(SYS_getrandom)
80a8e1175bSopenharmony_ci#define HAVE_GETRANDOM
81a8e1175bSopenharmony_ci#include <errno.h>
82a8e1175bSopenharmony_ci
83a8e1175bSopenharmony_cistatic int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags)
84a8e1175bSopenharmony_ci{
85a8e1175bSopenharmony_ci    /* MemSan cannot understand that the syscall writes to the buffer */
86a8e1175bSopenharmony_ci#if defined(__has_feature)
87a8e1175bSopenharmony_ci#if __has_feature(memory_sanitizer)
88a8e1175bSopenharmony_ci    memset(buf, 0, buflen);
89a8e1175bSopenharmony_ci#endif
90a8e1175bSopenharmony_ci#endif
91a8e1175bSopenharmony_ci    return (int) syscall(SYS_getrandom, buf, buflen, flags);
92a8e1175bSopenharmony_ci}
93a8e1175bSopenharmony_ci#endif /* SYS_getrandom */
94a8e1175bSopenharmony_ci#endif /* __linux__ || __midipix__ */
95a8e1175bSopenharmony_ci
96a8e1175bSopenharmony_ci#if defined(__FreeBSD__) || defined(__DragonFly__)
97a8e1175bSopenharmony_ci#include <sys/param.h>
98a8e1175bSopenharmony_ci#if (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || \
99a8e1175bSopenharmony_ci    (defined(__DragonFly__) && __DragonFly_version >= 500700)
100a8e1175bSopenharmony_ci#include <errno.h>
101a8e1175bSopenharmony_ci#include <sys/random.h>
102a8e1175bSopenharmony_ci#define HAVE_GETRANDOM
103a8e1175bSopenharmony_cistatic int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags)
104a8e1175bSopenharmony_ci{
105a8e1175bSopenharmony_ci    return (int) getrandom(buf, buflen, flags);
106a8e1175bSopenharmony_ci}
107a8e1175bSopenharmony_ci#endif /* (__FreeBSD__ && __FreeBSD_version >= 1200000) ||
108a8e1175bSopenharmony_ci          (__DragonFly__ && __DragonFly_version >= 500700) */
109a8e1175bSopenharmony_ci#endif /* __FreeBSD__ || __DragonFly__ */
110a8e1175bSopenharmony_ci
111a8e1175bSopenharmony_ci/*
112a8e1175bSopenharmony_ci * Some BSD systems provide KERN_ARND.
113a8e1175bSopenharmony_ci * This is equivalent to reading from /dev/urandom, only it doesn't require an
114a8e1175bSopenharmony_ci * open file descriptor, and provides up to 256 bytes per call (basically the
115a8e1175bSopenharmony_ci * same as getentropy(), but with a longer history).
116a8e1175bSopenharmony_ci *
117a8e1175bSopenharmony_ci * Documentation: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7
118a8e1175bSopenharmony_ci */
119a8e1175bSopenharmony_ci#if (defined(__FreeBSD__) || defined(__NetBSD__)) && !defined(HAVE_GETRANDOM)
120a8e1175bSopenharmony_ci#include <sys/param.h>
121a8e1175bSopenharmony_ci#include <sys/sysctl.h>
122a8e1175bSopenharmony_ci#if defined(KERN_ARND)
123a8e1175bSopenharmony_ci#define HAVE_SYSCTL_ARND
124a8e1175bSopenharmony_ci
125a8e1175bSopenharmony_cistatic int sysctl_arnd_wrapper(unsigned char *buf, size_t buflen)
126a8e1175bSopenharmony_ci{
127a8e1175bSopenharmony_ci    int name[2];
128a8e1175bSopenharmony_ci    size_t len;
129a8e1175bSopenharmony_ci
130a8e1175bSopenharmony_ci    name[0] = CTL_KERN;
131a8e1175bSopenharmony_ci    name[1] = KERN_ARND;
132a8e1175bSopenharmony_ci
133a8e1175bSopenharmony_ci    while (buflen > 0) {
134a8e1175bSopenharmony_ci        len = buflen > 256 ? 256 : buflen;
135a8e1175bSopenharmony_ci        if (sysctl(name, 2, buf, &len, NULL, 0) == -1) {
136a8e1175bSopenharmony_ci            return -1;
137a8e1175bSopenharmony_ci        }
138a8e1175bSopenharmony_ci        buflen -= len;
139a8e1175bSopenharmony_ci        buf += len;
140a8e1175bSopenharmony_ci    }
141a8e1175bSopenharmony_ci    return 0;
142a8e1175bSopenharmony_ci}
143a8e1175bSopenharmony_ci#endif /* KERN_ARND */
144a8e1175bSopenharmony_ci#endif /* __FreeBSD__ || __NetBSD__ */
145a8e1175bSopenharmony_ci
146a8e1175bSopenharmony_ci#include <stdio.h>
147a8e1175bSopenharmony_ci
148a8e1175bSopenharmony_ciint mbedtls_platform_entropy_poll(void *data,
149a8e1175bSopenharmony_ci                                  unsigned char *output, size_t len, size_t *olen)
150a8e1175bSopenharmony_ci{
151a8e1175bSopenharmony_ci    FILE *file;
152a8e1175bSopenharmony_ci    size_t read_len;
153a8e1175bSopenharmony_ci    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
154a8e1175bSopenharmony_ci    ((void) data);
155a8e1175bSopenharmony_ci
156a8e1175bSopenharmony_ci#if defined(HAVE_GETRANDOM)
157a8e1175bSopenharmony_ci    ret = getrandom_wrapper(output, len, 0);
158a8e1175bSopenharmony_ci    if (ret >= 0) {
159a8e1175bSopenharmony_ci        *olen = (size_t) ret;
160a8e1175bSopenharmony_ci        return 0;
161a8e1175bSopenharmony_ci    } else if (errno != ENOSYS) {
162a8e1175bSopenharmony_ci        return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
163a8e1175bSopenharmony_ci    }
164a8e1175bSopenharmony_ci    /* Fall through if the system call isn't known. */
165a8e1175bSopenharmony_ci#else
166a8e1175bSopenharmony_ci    ((void) ret);
167a8e1175bSopenharmony_ci#endif /* HAVE_GETRANDOM */
168a8e1175bSopenharmony_ci
169a8e1175bSopenharmony_ci#if defined(HAVE_SYSCTL_ARND)
170a8e1175bSopenharmony_ci    ((void) file);
171a8e1175bSopenharmony_ci    ((void) read_len);
172a8e1175bSopenharmony_ci    if (sysctl_arnd_wrapper(output, len) == -1) {
173a8e1175bSopenharmony_ci        return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
174a8e1175bSopenharmony_ci    }
175a8e1175bSopenharmony_ci    *olen = len;
176a8e1175bSopenharmony_ci    return 0;
177a8e1175bSopenharmony_ci#else
178a8e1175bSopenharmony_ci
179a8e1175bSopenharmony_ci    *olen = 0;
180a8e1175bSopenharmony_ci
181a8e1175bSopenharmony_ci    file = fopen("/dev/urandom", "rb");
182a8e1175bSopenharmony_ci    if (file == NULL) {
183a8e1175bSopenharmony_ci        return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
184a8e1175bSopenharmony_ci    }
185a8e1175bSopenharmony_ci
186a8e1175bSopenharmony_ci    /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */
187a8e1175bSopenharmony_ci    mbedtls_setbuf(file, NULL);
188a8e1175bSopenharmony_ci
189a8e1175bSopenharmony_ci    read_len = fread(output, 1, len, file);
190a8e1175bSopenharmony_ci    if (read_len != len) {
191a8e1175bSopenharmony_ci        fclose(file);
192a8e1175bSopenharmony_ci        return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
193a8e1175bSopenharmony_ci    }
194a8e1175bSopenharmony_ci
195a8e1175bSopenharmony_ci    fclose(file);
196a8e1175bSopenharmony_ci    *olen = len;
197a8e1175bSopenharmony_ci
198a8e1175bSopenharmony_ci    return 0;
199a8e1175bSopenharmony_ci#endif /* HAVE_SYSCTL_ARND */
200a8e1175bSopenharmony_ci}
201a8e1175bSopenharmony_ci#endif /* _WIN32 && !EFIX64 && !EFI32 */
202a8e1175bSopenharmony_ci#endif /* !MBEDTLS_NO_PLATFORM_ENTROPY */
203a8e1175bSopenharmony_ci
204a8e1175bSopenharmony_ci#if defined(MBEDTLS_ENTROPY_NV_SEED)
205a8e1175bSopenharmony_ciint mbedtls_nv_seed_poll(void *data,
206a8e1175bSopenharmony_ci                         unsigned char *output, size_t len, size_t *olen)
207a8e1175bSopenharmony_ci{
208a8e1175bSopenharmony_ci    unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE];
209a8e1175bSopenharmony_ci    size_t use_len = MBEDTLS_ENTROPY_BLOCK_SIZE;
210a8e1175bSopenharmony_ci    ((void) data);
211a8e1175bSopenharmony_ci
212a8e1175bSopenharmony_ci    memset(buf, 0, MBEDTLS_ENTROPY_BLOCK_SIZE);
213a8e1175bSopenharmony_ci
214a8e1175bSopenharmony_ci    if (mbedtls_nv_seed_read(buf, MBEDTLS_ENTROPY_BLOCK_SIZE) < 0) {
215a8e1175bSopenharmony_ci        return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
216a8e1175bSopenharmony_ci    }
217a8e1175bSopenharmony_ci
218a8e1175bSopenharmony_ci    if (len < use_len) {
219a8e1175bSopenharmony_ci        use_len = len;
220a8e1175bSopenharmony_ci    }
221a8e1175bSopenharmony_ci
222a8e1175bSopenharmony_ci    memcpy(output, buf, use_len);
223a8e1175bSopenharmony_ci    *olen = use_len;
224a8e1175bSopenharmony_ci
225a8e1175bSopenharmony_ci    return 0;
226a8e1175bSopenharmony_ci}
227a8e1175bSopenharmony_ci#endif /* MBEDTLS_ENTROPY_NV_SEED */
228a8e1175bSopenharmony_ci
229a8e1175bSopenharmony_ci#endif /* MBEDTLS_ENTROPY_C */
230