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