2ff32a99fa15977b4b2fe21b76e31aeb6e7a6ed8
[mono.git] / mono / utils / mono-rand-windows.c
1 /*
2  * mono-rand-windows.c: Windows rand support for Mono.
3  *
4  * Copyright 2016 Microsoft
5  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
6 */
7 #include <config.h>
8 #include <glib.h>
9 #include "mono-error.h"
10 #include "mono-error-internals.h"
11 #include "mono-rand.h"
12
13 #if defined(HOST_WIN32)
14 #include <windows.h>
15 #include "mono/utils/mono-rand-windows.h"
16
17 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
18 #ifndef PROV_INTEL_SEC
19 #define PROV_INTEL_SEC          22
20 #endif
21 #ifndef CRYPT_VERIFY_CONTEXT
22 #define CRYPT_VERIFY_CONTEXT    0xF0000000
23 #endif
24
25 MONO_WIN32_CRYPT_PROVIDER_HANDLE
26 mono_rand_win_open_provider (void)
27 {
28         MONO_WIN32_CRYPT_PROVIDER_HANDLE provider = 0;
29
30         /* There is no need to create a container for just random data,
31          * so we can use CRYPT_VERIFY_CONTEXT (one call) see:
32          * http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */
33
34         /* We first try to use the Intel PIII RNG if drivers are present */
35         if (!CryptAcquireContext (&provider, NULL, NULL, PROV_INTEL_SEC, CRYPT_VERIFY_CONTEXT)) {
36                 /* not a PIII or no drivers available, use default RSA CSP */
37                 if (!CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT)) {
38                         /* exception will be thrown in managed code */
39                         provider = 0;
40                 }
41         }
42
43         return provider;
44 }
45
46 void
47 mono_rand_win_close_provider (MONO_WIN32_CRYPT_PROVIDER_HANDLE provider)
48 {
49         CryptReleaseContext (provider, 0);
50 }
51
52 gboolean
53 mono_rand_win_gen (MONO_WIN32_CRYPT_PROVIDER_HANDLE provider, guchar *buffer, size_t buffer_size)
54 {
55         return CryptGenRandom (provider, (DWORD) buffer_size, buffer);
56 }
57
58 gboolean
59 mono_rand_win_seed (MONO_WIN32_CRYPT_PROVIDER_HANDLE provider, guchar *seed, size_t seed_size)
60 {
61         /* add seeding material to the RNG */
62         return CryptGenRandom (provider, (DWORD) seed_size, seed);
63 }
64 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
65
66 /**
67  * mono_rand_open:
68  *
69  * Returns: True if random source is global, false if mono_rand_init can be called repeatedly to get randomness instances.
70  *
71  * Initializes entire RNG system. Must be called once per process before calling mono_rand_init.
72  */
73 gboolean
74 mono_rand_open (void)
75 {
76         return FALSE;
77 }
78
79 /**
80  * mono_rand_init:
81  * @seed: A string containing seed data
82  * @seed_size: Length of seed string
83  *
84  * Returns: On success, a non-NULL handle which can be used to fetch random data from mono_rand_try_get_bytes. On failure, NULL.
85  *
86  * Initializes an RNG client.
87  */
88 gpointer
89 mono_rand_init (guchar *seed, gint seed_size)
90 {
91         MONO_WIN32_CRYPT_PROVIDER_HANDLE provider = 0;
92
93         /* try to open crypto provider. */
94         provider = mono_rand_win_open_provider ();
95
96         /* seed the CSP with the supplied buffer (if present) */
97         if (provider != 0 && seed != NULL) {
98                 /* the call we replace the seed with random - this isn't what is
99                  * expected from the class library user */
100                 guchar *data = g_malloc (seed_size);
101                 if (data != NULL) {
102                         memcpy (data, seed, seed_size);
103                         /* add seeding material to the RNG */
104                         mono_rand_win_seed (provider, data, seed_size);
105                         /* zeroize and free */
106                         memset (data, 0, seed_size);
107                         g_free (data);
108                 }
109         }
110
111         return (gpointer) provider;
112 }
113
114 /**
115  * mono_rand_try_get_bytes:
116  * @handle: A pointer to an RNG handle. Handle is set to NULL on failure.
117  * @buffer: A buffer into which to write random data.
118  * @buffer_size: Number of bytes to write into buffer.
119  * @error: Set on error.
120  *
121  * Returns: FALSE on failure and sets @error, TRUE on success.
122  *
123  * Extracts bytes from an RNG handle.
124  */
125 gboolean
126 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
127 {
128         MONO_WIN32_CRYPT_PROVIDER_HANDLE provider;
129
130         mono_error_init (error);
131
132         g_assert (handle);
133         provider = (MONO_WIN32_CRYPT_PROVIDER_HANDLE) *handle;
134
135         /* generate random bytes */
136         if (!mono_rand_win_gen (provider, buffer, buffer_size)) {
137                 mono_rand_win_close_provider (provider);
138                 /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */
139                 provider = mono_rand_win_open_provider ();
140                 if (provider != 0) {
141
142                         /* retry generate of random bytes */
143                         if (!mono_rand_win_gen (provider, buffer, buffer_size)) {
144                                 /* failure, close provider */
145                                 mono_rand_win_close_provider (provider);
146                                 provider = 0;
147                         }
148                 }
149
150                 /* make sure client gets new opened provider handle or NULL on failure */
151                 *handle = (gpointer) provider;
152                 if (*handle == 0) {
153                         /* exception will be thrown in managed code */
154                         mono_error_set_execution_engine (error, "Failed to gen random bytes (%d)", GetLastError ());
155                         return FALSE;
156                 }
157         }
158         return TRUE;
159 }
160
161 /**
162  * mono_rand_close:
163  * @handle: An RNG handle.
164  *
165  * Releases an RNG handle.
166  */
167 void
168 mono_rand_close (gpointer handle)
169 {
170         mono_rand_win_close_provider ((MONO_WIN32_CRYPT_PROVIDER_HANDLE) handle);
171 }
172 #endif /* HOST_WIN32 */