1/*
2 * Copyright (c) 2007-2015, Cameron Rich
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * * Neither the name of the axTLS project nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * Some misc. routines to help things out
33 */
34
35#include <stdlib.h>
36#include <string.h>
37#include <stdarg.h>
38#include <stdio.h>
39#include "os_port.h"
40#include "crypto_misc.h"
41#ifdef CONFIG_WIN32_USE_CRYPTO_LIB
42#include "wincrypt.h"
43#endif
44
45#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM)
46static int rng_fd = -1;
47#elif defined(CONFIG_WIN32_USE_CRYPTO_LIB)
48static HCRYPTPROV gCryptProv;
49#endif
50
51#if (!defined(CONFIG_USE_DEV_URANDOM) && !defined(CONFIG_WIN32_USE_CRYPTO_LIB))
52/* change to processor registers as appropriate */
53#define ENTROPY_POOL_SIZE 32
54#define ENTROPY_COUNTER1 ((((uint64_t)tv.tv_sec)<<32) | tv.tv_usec)
55#define ENTROPY_COUNTER2 rand()
56static uint8_t entropy_pool[ENTROPY_POOL_SIZE];
57#endif
58
59const char * const unsupported_str = "Error: Feature not supported\n";
60
61#ifndef CONFIG_SSL_SKELETON_MODE
62/**
63 * Retrieve a file and put it into memory
64 * @return The size of the file, or -1 on failure.
65 */
66int get_file(const char *filename, uint8_t **buf)
67{
68 int total_bytes = 0;
69 int bytes_read = 0;
70 int filesize;
71 FILE *stream = fopen(filename, "rb");
72
73 if (stream == NULL)
74 {
75#ifdef CONFIG_SSL_FULL_MODE
76 printf("file '%s' does not exist\n", filename); TTY_FLUSH();
77#endif
78 return -1;
79 }
80
81 /* Win CE doesn't support stat() */
82 fseek(stream, 0, SEEK_END);
83 filesize = ftell(stream);
84 *buf = (uint8_t *)malloc(filesize);
85 fseek(stream, 0, SEEK_SET);
86
87 do
88 {
89 bytes_read = fread(*buf+total_bytes, 1, filesize-total_bytes, stream);
90 total_bytes += bytes_read;
91 } while (total_bytes < filesize && bytes_read > 0);
92
93 fclose(stream);
94 return filesize;
95}
96#endif
97
98/**
99 * Initialise the Random Number Generator engine.
100 * - On Win32 use the platform SDK's crypto engine.
101 * - On Linux use /dev/urandom
102 * - If none of these work then use a custom RNG.
103 */
104EXP_FUNC void STDCALL RNG_initialize()
105{
106#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM)
107 rng_fd = open("/dev/urandom", O_RDONLY);
108#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB)
109 if (!CryptAcquireContext(&gCryptProv,
110 NULL, NULL, PROV_RSA_FULL, 0))
111 {
112 if (GetLastError() == NTE_BAD_KEYSET &&
113 !CryptAcquireContext(&gCryptProv,
114 NULL,
115 NULL,
116 PROV_RSA_FULL,
117 CRYPT_NEWKEYSET))
118 {
119 printf("CryptoLib: %x\n", unsupported_str, GetLastError());
120 exit(1);
121 }
122 }
123#elif !defined(__ets__)
124 /* start of with a stack to copy across */
125 int i;
126 memcpy(entropy_pool, &i, ENTROPY_POOL_SIZE);
127 rand_r((unsigned int *)entropy_pool);
128#endif
129}
130
131/**
132 * If no /dev/urandom, then initialise the RNG with something interesting.
133 */
134EXP_FUNC void STDCALL RNG_custom_init(const uint8_t *seed_buf, int size)
135{
136#if defined(WIN32) || defined(CONFIG_WIN32_USE_CRYPTO_LIB)
137 int i;
138
139 for (i = 0; i < ENTROPY_POOL_SIZE && i < size; i++)
140 entropy_pool[i] ^= seed_buf[i];
141#endif
142}
143
144/**
145 * Terminate the RNG engine.
146 */
147EXP_FUNC void STDCALL RNG_terminate(void)
148{
149#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM)
150 close(rng_fd);
151#elif defined(CONFIG_WIN32_USE_CRYPTO_LIB)
152 CryptReleaseContext(gCryptProv, 0);
153#endif
154}
155
156/**
157 * Set a series of bytes with a random number. Individual bytes can be 0
158 */
159EXP_FUNC int STDCALL get_random(int num_rand_bytes, uint8_t *rand_data)
160{
161#ifdef __ets__
162// see http://esp8266-re.foogod.com/wiki/Random_Number_Generator
163#define WDEV_HWRNG ((volatile uint32_t*)0x3ff20e44)
164 while (num_rand_bytes--) {
165 *rand_data++ = *WDEV_HWRNG;
166 }
167#elif !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM)
168 /* use the Linux default - read from /dev/urandom */
169 if (read(rng_fd, rand_data, num_rand_bytes) < 0)
170 return -1;
171#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB)
172 /* use Microsoft Crypto Libraries */
173 CryptGenRandom(gCryptProv, num_rand_bytes, rand_data);
174#else /* nothing else to use, so use a custom RNG */
175 /* The method we use when we've got nothing better. Use RC4, time
176 and a couple of random seeds to generate a random sequence */
177 AES_CTX rng_ctx;
178 struct timeval tv;
179 MD5_CTX rng_digest_ctx;
180 uint8_t digest[MD5_SIZE];
181 uint64_t *ep;
182 int i;
183
184 /* A proper implementation would use counters etc for entropy */
185 gettimeofday(&tv, NULL);
186 ep = (uint64_t *)entropy_pool;
187 ep[0] ^= ENTROPY_COUNTER1;
188 ep[1] ^= ENTROPY_COUNTER2;
189
190 /* use a digested version of the entropy pool as a key */
191 MD5_Init(&rng_digest_ctx);
192 MD5_Update(&rng_digest_ctx, entropy_pool, ENTROPY_POOL_SIZE);
193 MD5_Final(digest, &rng_digest_ctx);
194
195 /* come up with the random sequence */
196 AES_set_key(&rng_ctx, digest, (const uint8_t *)ep, AES_MODE_128); /* use as a key */
197 memcpy(rand_data, entropy_pool, num_rand_bytes < ENTROPY_POOL_SIZE ?
198 num_rand_bytes : ENTROPY_POOL_SIZE);
199 AES_cbc_encrypt(&rng_ctx, rand_data, rand_data, num_rand_bytes);
200
201 /* move things along */
202 for (i = ENTROPY_POOL_SIZE-1; i >= MD5_SIZE ; i--)
203 entropy_pool[i] = entropy_pool[i-MD5_SIZE];
204
205 /* insert the digest at the start of the entropy pool */
206 memcpy(entropy_pool, digest, MD5_SIZE);
207#endif
208 return 0;
209}
210
211/**
212 * Set a series of bytes with a random number. Individual bytes are not zero.
213 */
214int get_random_NZ(int num_rand_bytes, uint8_t *rand_data)
215{
216 int i;
217 if (get_random(num_rand_bytes, rand_data))
218 return -1;
219
220 for (i = 0; i < num_rand_bytes; i++)
221 {
222 while (rand_data[i] == 0) /* can't be 0 */
223 rand_data[i] = (uint8_t)(rand());
224 }
225
226 return 0;
227}
228
229/**
230 * Some useful diagnostic routines
231 */
232#if defined(CONFIG_SSL_DIAGNOSTICS) || defined(CONFIG_DEBUG)
233int hex_finish;
234int hex_index;
235
236static void print_hex_init(int finish)
237{
238 hex_finish = finish;
239 hex_index = 0;
240}
241
242static void print_hex(uint8_t hex)
243{
244 static int column;
245
246 if (hex_index == 0)
247 {
248 column = 0;
249 }
250
251 printf("%02x ", hex);
252 if (++column == 8)
253 {
254 printf(": ");
255 }
256 else if (column >= 16)
257 {
258 printf("\n");
259 column = 0;
260 }
261
262 if (++hex_index >= hex_finish && column > 0)
263 {
264 printf("\n");
265 }
266}
267
268/**
269 * Spit out a blob of data for diagnostics. The data is is a nice column format
270 * for easy reading.
271 *
272 * @param format [in] The string (with possible embedded format characters)
273 * @param size [in] The number of numbers to print
274 * @param data [in] The start of data to use
275 * @param ... [in] Any additional arguments
276 */
277EXP_FUNC void STDCALL print_blob(const char *format,
278 const uint8_t *data, int size, ...)
279{
280 int i;
281 char tmp[80];
282 va_list(ap);
283
284 va_start(ap, size);
285 snprintf(tmp, sizeof(tmp), "%s\n", format);
286 vprintf(tmp, ap);
287 print_hex_init(size);
288 for (i = 0; i < size; i++)
289 {
290 print_hex(data[i]);
291 }
292
293 va_end(ap);
294 TTY_FLUSH();
295}
296#elif defined(WIN32)
297/* VC6.0 doesn't handle variadic macros */
298EXP_FUNC void STDCALL print_blob(const char *format, const unsigned char *data,
299 int size, ...) {}
300#endif
301
302#if defined(CONFIG_SSL_HAS_PEM) || defined(CONFIG_HTTP_HAS_AUTHORIZATION)
303/* base64 to binary lookup table */
304static const uint8_t map[128] =
305{
306 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
307 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
308 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
309 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
310 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
311 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
312 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
313 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
314 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
315 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
316 49, 50, 51, 255, 255, 255, 255, 255
317};
318
319EXP_FUNC int STDCALL base64_decode(const char *in, int len,
320 uint8_t *out, int *outlen)
321{
322 int g, t, x, y, z;
323 uint8_t c;
324 int ret = -1;
325
326 g = 3;
327 for (x = y = z = t = 0; x < len; x++)
328 {
329 if ((c = map[in[x]&0x7F]) == 0xff)
330 continue;
331
332 if (c == 254) /* this is the end... */
333 {
334 c = 0;
335
336 if (--g < 0)
337 goto error;
338 }
339 else if (g != 3) /* only allow = at end */
340 goto error;
341
342 t = (t<<6) | c;
343
344 if (++y == 4)
345 {
346 out[z++] = (uint8_t)((t>>16)&255);
347
348 if (g > 1)
349 out[z++] = (uint8_t)((t>>8)&255);
350
351 if (g > 2)
352 out[z++] = (uint8_t)(t&255);
353
354 y = t = 0;
355 }
356
357 /* check that we don't go past the output buffer */
358 if (z > *outlen)
359 goto error;
360 }
361
362 if (y != 0)
363 goto error;
364
365 *outlen = z;
366 ret = 0;
367
368error:
369#ifdef CONFIG_SSL_FULL_MODE
370 if (ret < 0)
371 printf("Error: Invalid base64\n"); TTY_FLUSH();
372#endif
373 TTY_FLUSH();
374 return ret;
375
376}
377#endif
378
379