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