1 /*
2 zip_crypto_win.c -- Windows Crypto API wrapper.
3 Copyright (C) 2018-2019 Dieter Baron and Thomas Klausner
4
5 This file is part of libzip, a library to manipulate ZIP archives.
6 The authors can be contacted at <libzip@nih.at>
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in
15 the documentation and/or other materials provided with the
16 distribution.
17 3. The names of the authors may not be used to endorse or promote
18 products derived from this software without specific prior
19 written permission.
20
21 THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 #include <stdlib.h>
34
35 #include "zipint.h"
36
37 #include "zip_crypto.h"
38
39 #define WIN32_LEAN_AND_MEAN
40 #define NOCRYPT
41
42 #include <windows.h>
43
44 #include <bcrypt.h>
45
46 #pragma comment(lib, "bcrypt.lib")
47
48 /*
49
50 This code is using the Cryptography API: Next Generation (CNG)
51 https://docs.microsoft.com/en-us/windows/desktop/seccng/cng-portal
52
53 This API is supported on
54 - Windows Vista or later (client OS)
55 - Windows Server 2008 (server OS)
56 - Windows Embedded Compact 2013 (don't know about Windows Embedded Compact 7)
57
58 The code was developed for Windows Embedded Compact 2013 (WEC2013),
59 but should be working for all of the above mentioned OSes.
60
61 There are 2 restrictions for WEC2013, Windows Vista and Windows Server 2008:
62
63 1.) The function "BCryptDeriveKeyPBKDF2" is not available
64
65 I found some code which is implementing this function using the deprecated Crypto API here:
66 https://www.idrix.fr/Root/content/view/37/54/
67
68 I took this code and converted it to the newer CNG API. The original code was more
69 flexible, but this is not needed here so i refactored it a bit and just kept what is needed.
70
71 The define "HAS_BCRYPTDERIVEKEYPBKDF2" controls whether "BCryptDeriveKeyPBKDF2"
72 of the CNG API is used or not. This define must not be set if you are compiling for WEC2013 or Windows Vista.
73
74
75 2.) "BCryptCreateHash" can't manage the memory needed for the hash object internally
76
77 On Windows 7 or later it is possible to pass NULL for the hash object buffer.
78 This is not supported on WEC2013, so we have to handle the memory allocation/deallocation ourselves.
79 There is no #ifdef to control that, because this is working for all supported OSes.
80
81 */
82
83 #if !defined(WINCE) && !defined(__MINGW32__)
84 #define HAS_BCRYPTDERIVEKEYPBKDF2
85 #endif
86
87 #ifdef HAS_BCRYPTDERIVEKEYPBKDF2
88
89 bool
_zip_crypto_pbkdf2(const zip_uint8_t * key,zip_uint64_t key_length,const zip_uint8_t * salt,zip_uint16_t salt_length,zip_uint16_t iterations,zip_uint8_t * output,zip_uint16_t output_length)90 _zip_crypto_pbkdf2(const zip_uint8_t *key, zip_uint64_t key_length, const zip_uint8_t *salt, zip_uint16_t salt_length, zip_uint16_t iterations, zip_uint8_t *output, zip_uint16_t output_length) {
91 BCRYPT_ALG_HANDLE hAlgorithm = NULL;
92
93 if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG))) {
94 return false;
95 }
96
97 bool result = BCRYPT_SUCCESS(BCryptDeriveKeyPBKDF2(hAlgorithm, (PUCHAR)key, (ULONG)key_length, (PUCHAR)salt, salt_length, iterations, output, output_length, 0));
98
99 BCryptCloseAlgorithmProvider(hAlgorithm, 0);
100
101 return result;
102 }
103
104 #else
105
106 #include <math.h>
107
108 #define DIGEST_SIZE 20
109 #define BLOCK_SIZE 64
110
111 typedef struct {
112 BCRYPT_ALG_HANDLE hAlgorithm;
113 BCRYPT_HASH_HANDLE hInnerHash;
114 BCRYPT_HASH_HANDLE hOuterHash;
115 ULONG cbHashObject;
116 PUCHAR pbInnerHash;
117 PUCHAR pbOuterHash;
118 } PRF_CTX;
119
120 static void
hmacFree(PRF_CTX * pContext)121 hmacFree(PRF_CTX *pContext) {
122 if (pContext->hOuterHash)
123 BCryptDestroyHash(pContext->hOuterHash);
124 if (pContext->hInnerHash)
125 BCryptDestroyHash(pContext->hInnerHash);
126 free(pContext->pbOuterHash);
127 free(pContext->pbInnerHash);
128 if (pContext->hAlgorithm)
129 BCryptCloseAlgorithmProvider(pContext->hAlgorithm, 0);
130 }
131
132 static BOOL
hmacPrecomputeDigest(BCRYPT_HASH_HANDLE hHash,PUCHAR pbPassword,DWORD cbPassword,BYTE mask)133 hmacPrecomputeDigest(BCRYPT_HASH_HANDLE hHash, PUCHAR pbPassword, DWORD cbPassword, BYTE mask) {
134 BYTE buffer[BLOCK_SIZE];
135 DWORD i;
136
137 if (cbPassword > BLOCK_SIZE) {
138 return FALSE;
139 }
140
141 memset(buffer, mask, sizeof(buffer));
142
143 for (i = 0; i < cbPassword; ++i) {
144 buffer[i] = (char)(pbPassword[i] ^ mask);
145 }
146
147 return BCRYPT_SUCCESS(BCryptHashData(hHash, buffer, sizeof(buffer), 0));
148 }
149
150 static BOOL
hmacInit(PRF_CTX * pContext,PUCHAR pbPassword,DWORD cbPassword)151 hmacInit(PRF_CTX *pContext, PUCHAR pbPassword, DWORD cbPassword) {
152 BOOL bStatus = FALSE;
153 ULONG cbResult;
154 BYTE key[DIGEST_SIZE];
155
156 if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&pContext->hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, 0)) || !BCRYPT_SUCCESS(BCryptGetProperty(pContext->hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&pContext->cbHashObject, sizeof(pContext->cbHashObject), &cbResult, 0)) || ((pContext->pbInnerHash = malloc(pContext->cbHashObject)) == NULL) || ((pContext->pbOuterHash = malloc(pContext->cbHashObject)) == NULL) || !BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &pContext->hInnerHash, pContext->pbInnerHash, pContext->cbHashObject, NULL, 0, 0)) || !BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &pContext->hOuterHash, pContext->pbOuterHash, pContext->cbHashObject, NULL, 0, 0))) {
157 goto hmacInit_end;
158 }
159
160 if (cbPassword > BLOCK_SIZE) {
161 BCRYPT_HASH_HANDLE hHash = NULL;
162 PUCHAR pbHashObject = malloc(pContext->cbHashObject);
163 if (pbHashObject == NULL) {
164 goto hmacInit_end;
165 }
166
167 bStatus = BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &hHash, pbHashObject, pContext->cbHashObject, NULL, 0, 0)) && BCRYPT_SUCCESS(BCryptHashData(hHash, pbPassword, cbPassword, 0)) && BCRYPT_SUCCESS(BCryptGetProperty(hHash, BCRYPT_HASH_LENGTH, (PUCHAR)&cbPassword, sizeof(cbPassword), &cbResult, 0)) && BCRYPT_SUCCESS(BCryptFinishHash(hHash, key, cbPassword, 0));
168
169 if (hHash)
170 BCryptDestroyHash(hHash);
171 free(pbHashObject);
172
173 if (!bStatus) {
174 goto hmacInit_end;
175 }
176
177 pbPassword = key;
178 }
179
180 bStatus = hmacPrecomputeDigest(pContext->hInnerHash, pbPassword, cbPassword, 0x36) && hmacPrecomputeDigest(pContext->hOuterHash, pbPassword, cbPassword, 0x5C);
181
182 hmacInit_end:
183
184 if (bStatus == FALSE)
185 hmacFree(pContext);
186
187 return bStatus;
188 }
189
190 static BOOL
hmacCalculateInternal(BCRYPT_HASH_HANDLE hHashTemplate,PUCHAR pbData,DWORD cbData,PUCHAR pbOutput,DWORD cbOutput,DWORD cbHashObject)191 hmacCalculateInternal(BCRYPT_HASH_HANDLE hHashTemplate, PUCHAR pbData, DWORD cbData, PUCHAR pbOutput, DWORD cbOutput, DWORD cbHashObject) {
192 BOOL success = FALSE;
193 BCRYPT_HASH_HANDLE hHash = NULL;
194 PUCHAR pbHashObject = malloc(cbHashObject);
195
196 if (pbHashObject == NULL) {
197 return FALSE;
198 }
199
200 if (BCRYPT_SUCCESS(BCryptDuplicateHash(hHashTemplate, &hHash, pbHashObject, cbHashObject, 0))) {
201 success = BCRYPT_SUCCESS(BCryptHashData(hHash, pbData, cbData, 0)) && BCRYPT_SUCCESS(BCryptFinishHash(hHash, pbOutput, cbOutput, 0));
202
203 BCryptDestroyHash(hHash);
204 }
205
206 free(pbHashObject);
207
208 return success;
209 }
210
211 static BOOL
hmacCalculate(PRF_CTX * pContext,PUCHAR pbData,DWORD cbData,PUCHAR pbDigest)212 hmacCalculate(PRF_CTX *pContext, PUCHAR pbData, DWORD cbData, PUCHAR pbDigest) {
213 DWORD cbResult;
214 DWORD cbHashObject;
215
216 return BCRYPT_SUCCESS(BCryptGetProperty(pContext->hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&cbHashObject, sizeof(cbHashObject), &cbResult, 0)) && hmacCalculateInternal(pContext->hInnerHash, pbData, cbData, pbDigest, DIGEST_SIZE, cbHashObject) && hmacCalculateInternal(pContext->hOuterHash, pbDigest, DIGEST_SIZE, pbDigest, DIGEST_SIZE, cbHashObject);
217 }
218
219 static void
myxor(LPBYTE ptr1,LPBYTE ptr2,DWORD dwLen)220 myxor(LPBYTE ptr1, LPBYTE ptr2, DWORD dwLen) {
221 while (dwLen--)
222 *ptr1++ ^= *ptr2++;
223 }
224
225 BOOL
pbkdf2(PUCHAR pbPassword,ULONG cbPassword,PUCHAR pbSalt,ULONG cbSalt,DWORD cIterations,PUCHAR pbDerivedKey,ULONG cbDerivedKey)226 pbkdf2(PUCHAR pbPassword, ULONG cbPassword, PUCHAR pbSalt, ULONG cbSalt, DWORD cIterations, PUCHAR pbDerivedKey, ULONG cbDerivedKey) {
227 BOOL bStatus = FALSE;
228 DWORD l, r, dwULen, i, j;
229 BYTE Ti[DIGEST_SIZE];
230 BYTE V[DIGEST_SIZE];
231 LPBYTE U = malloc(max((cbSalt + 4), DIGEST_SIZE));
232 PRF_CTX prfCtx = {0};
233
234 if (U == NULL) {
235 return FALSE;
236 }
237
238 if (pbPassword == NULL || cbPassword == 0 || pbSalt == NULL || cbSalt == 0 || cIterations == 0 || pbDerivedKey == NULL || cbDerivedKey == 0) {
239 free(U);
240 return FALSE;
241 }
242
243 if (!hmacInit(&prfCtx, pbPassword, cbPassword)) {
244 goto PBKDF2_end;
245 }
246
247 l = (DWORD)ceil((double)cbDerivedKey / (double)DIGEST_SIZE);
248 r = cbDerivedKey - (l - 1) * DIGEST_SIZE;
249
250 for (i = 1; i <= l; i++) {
251 ZeroMemory(Ti, DIGEST_SIZE);
252 for (j = 0; j < cIterations; j++) {
253 if (j == 0) {
254 /* construct first input for PRF */
255 memcpy(U, pbSalt, cbSalt);
256 U[cbSalt] = (BYTE)((i & 0xFF000000) >> 24);
257 U[cbSalt + 1] = (BYTE)((i & 0x00FF0000) >> 16);
258 U[cbSalt + 2] = (BYTE)((i & 0x0000FF00) >> 8);
259 U[cbSalt + 3] = (BYTE)((i & 0x000000FF));
260 dwULen = cbSalt + 4;
261 }
262 else {
263 memcpy(U, V, DIGEST_SIZE);
264 dwULen = DIGEST_SIZE;
265 }
266
267 if (!hmacCalculate(&prfCtx, U, dwULen, V)) {
268 goto PBKDF2_end;
269 }
270
271 myxor(Ti, V, DIGEST_SIZE);
272 }
273
274 if (i != l) {
275 memcpy(&pbDerivedKey[(i - 1) * DIGEST_SIZE], Ti, DIGEST_SIZE);
276 }
277 else {
278 /* Take only the first r bytes */
279 memcpy(&pbDerivedKey[(i - 1) * DIGEST_SIZE], Ti, r);
280 }
281 }
282
283 bStatus = TRUE;
284
285 PBKDF2_end:
286
287 hmacFree(&prfCtx);
288 free(U);
289 return bStatus;
290 }
291
292 bool
_zip_crypto_pbkdf2(const zip_uint8_t * key,zip_uint64_t key_length,const zip_uint8_t * salt,zip_uint16_t salt_length,zip_uint16_t iterations,zip_uint8_t * output,zip_uint16_t output_length)293 _zip_crypto_pbkdf2(const zip_uint8_t *key, zip_uint64_t key_length, const zip_uint8_t *salt, zip_uint16_t salt_length, zip_uint16_t iterations, zip_uint8_t *output, zip_uint16_t output_length) {
294 return (key_length <= ZIP_UINT32_MAX) && pbkdf2((PUCHAR)key, (ULONG)key_length, (PUCHAR)salt, salt_length, iterations, output, output_length);
295 }
296
297 #endif
298
299
300 struct _zip_crypto_aes_s {
301 BCRYPT_ALG_HANDLE hAlgorithm;
302 BCRYPT_KEY_HANDLE hKey;
303 ULONG cbKeyObject;
304 PUCHAR pbKeyObject;
305 };
306
307 _zip_crypto_aes_t *
_zip_crypto_aes_new(const zip_uint8_t * key,zip_uint16_t key_size,zip_error_t * error)308 _zip_crypto_aes_new(const zip_uint8_t *key, zip_uint16_t key_size, zip_error_t *error) {
309 _zip_crypto_aes_t *aes = (_zip_crypto_aes_t *)calloc(1, sizeof(*aes));
310
311 ULONG cbResult;
312 ULONG key_length = key_size / 8;
313
314 if (aes == NULL) {
315 zip_error_set(error, ZIP_ER_MEMORY, 0);
316 return NULL;
317 }
318
319 if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&aes->hAlgorithm, BCRYPT_AES_ALGORITHM, NULL, 0))) {
320 _zip_crypto_aes_free(aes);
321 return NULL;
322 }
323
324 if (!BCRYPT_SUCCESS(BCryptSetProperty(aes->hAlgorithm, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0))) {
325 _zip_crypto_aes_free(aes);
326 return NULL;
327 }
328
329 if (!BCRYPT_SUCCESS(BCryptGetProperty(aes->hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&aes->cbKeyObject, sizeof(aes->cbKeyObject), &cbResult, 0))) {
330 _zip_crypto_aes_free(aes);
331 return NULL;
332 }
333
334 aes->pbKeyObject = malloc(aes->cbKeyObject);
335 if (aes->pbKeyObject == NULL) {
336 _zip_crypto_aes_free(aes);
337 zip_error_set(error, ZIP_ER_MEMORY, 0);
338 return NULL;
339 }
340
341 if (!BCRYPT_SUCCESS(BCryptGenerateSymmetricKey(aes->hAlgorithm, &aes->hKey, aes->pbKeyObject, aes->cbKeyObject, (PUCHAR)key, key_length, 0))) {
342 _zip_crypto_aes_free(aes);
343 return NULL;
344 }
345
346 return aes;
347 }
348
349 void
_zip_crypto_aes_free(_zip_crypto_aes_t * aes)350 _zip_crypto_aes_free(_zip_crypto_aes_t *aes) {
351 if (aes == NULL) {
352 return;
353 }
354
355 if (aes->hKey != NULL) {
356 BCryptDestroyKey(aes->hKey);
357 }
358
359 if (aes->pbKeyObject != NULL) {
360 free(aes->pbKeyObject);
361 }
362
363 if (aes->hAlgorithm != NULL) {
364 BCryptCloseAlgorithmProvider(aes->hAlgorithm, 0);
365 }
366
367 free(aes);
368 }
369
370 bool
_zip_crypto_aes_encrypt_block(_zip_crypto_aes_t * aes,const zip_uint8_t * in,zip_uint8_t * out)371 _zip_crypto_aes_encrypt_block(_zip_crypto_aes_t *aes, const zip_uint8_t *in, zip_uint8_t *out) {
372 ULONG cbResult;
373 NTSTATUS status = BCryptEncrypt(aes->hKey, (PUCHAR)in, ZIP_CRYPTO_AES_BLOCK_LENGTH, NULL, NULL, 0, (PUCHAR)out, ZIP_CRYPTO_AES_BLOCK_LENGTH, &cbResult, 0);
374 return BCRYPT_SUCCESS(status);
375 }
376
377 struct _zip_crypto_hmac_s {
378 BCRYPT_ALG_HANDLE hAlgorithm;
379 BCRYPT_HASH_HANDLE hHash;
380 DWORD cbHashObject;
381 PUCHAR pbHashObject;
382 DWORD cbHash;
383 PUCHAR pbHash;
384 };
385
386 /* https://code.msdn.microsoft.com/windowsdesktop/Hmac-Computation-Sample-11fe8ec1/sourcecode?fileId=42820&pathId=283874677 */
387
388 _zip_crypto_hmac_t *
_zip_crypto_hmac_new(const zip_uint8_t * secret,zip_uint64_t secret_length,zip_error_t * error)389 _zip_crypto_hmac_new(const zip_uint8_t *secret, zip_uint64_t secret_length, zip_error_t *error) {
390 NTSTATUS status;
391 ULONG cbResult;
392 _zip_crypto_hmac_t *hmac;
393
394 if (secret_length > INT_MAX) {
395 zip_error_set(error, ZIP_ER_INVAL, 0);
396 return NULL;
397 }
398
399 hmac = (_zip_crypto_hmac_t *)calloc(1, sizeof(*hmac));
400
401 if (hmac == NULL) {
402 zip_error_set(error, ZIP_ER_MEMORY, 0);
403 return NULL;
404 }
405
406 status = BCryptOpenAlgorithmProvider(&hmac->hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG);
407 if (!BCRYPT_SUCCESS(status)) {
408 _zip_crypto_hmac_free(hmac);
409 return NULL;
410 }
411
412 status = BCryptGetProperty(hmac->hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hmac->cbHashObject, sizeof(hmac->cbHashObject), &cbResult, 0);
413 if (!BCRYPT_SUCCESS(status)) {
414 _zip_crypto_hmac_free(hmac);
415 return NULL;
416 }
417
418 hmac->pbHashObject = malloc(hmac->cbHashObject);
419 if (hmac->pbHashObject == NULL) {
420 _zip_crypto_hmac_free(hmac);
421 zip_error_set(error, ZIP_ER_MEMORY, 0);
422 return NULL;
423 }
424
425 status = BCryptGetProperty(hmac->hAlgorithm, BCRYPT_HASH_LENGTH, (PUCHAR)&hmac->cbHash, sizeof(hmac->cbHash), &cbResult, 0);
426 if (!BCRYPT_SUCCESS(status)) {
427 _zip_crypto_hmac_free(hmac);
428 return NULL;
429 }
430
431 hmac->pbHash = malloc(hmac->cbHash);
432 if (hmac->pbHash == NULL) {
433 _zip_crypto_hmac_free(hmac);
434 zip_error_set(error, ZIP_ER_MEMORY, 0);
435 return NULL;
436 }
437
438 status = BCryptCreateHash(hmac->hAlgorithm, &hmac->hHash, hmac->pbHashObject, hmac->cbHashObject, (PUCHAR)secret, (ULONG)secret_length, 0);
439 if (!BCRYPT_SUCCESS(status)) {
440 _zip_crypto_hmac_free(hmac);
441 return NULL;
442 }
443
444 return hmac;
445 }
446
447 void
_zip_crypto_hmac_free(_zip_crypto_hmac_t * hmac)448 _zip_crypto_hmac_free(_zip_crypto_hmac_t *hmac) {
449 if (hmac == NULL) {
450 return;
451 }
452
453 if (hmac->hHash != NULL) {
454 BCryptDestroyHash(hmac->hHash);
455 }
456
457 if (hmac->pbHash != NULL) {
458 free(hmac->pbHash);
459 }
460
461 if (hmac->pbHashObject != NULL) {
462 free(hmac->pbHashObject);
463 }
464
465 if (hmac->hAlgorithm) {
466 BCryptCloseAlgorithmProvider(hmac->hAlgorithm, 0);
467 }
468
469 free(hmac);
470 }
471
472 bool
_zip_crypto_hmac(_zip_crypto_hmac_t * hmac,zip_uint8_t * data,zip_uint64_t length)473 _zip_crypto_hmac(_zip_crypto_hmac_t *hmac, zip_uint8_t *data, zip_uint64_t length) {
474 if (hmac == NULL || length > ULONG_MAX) {
475 return false;
476 }
477
478 return BCRYPT_SUCCESS(BCryptHashData(hmac->hHash, data, (ULONG)length, 0));
479 }
480
481 bool
_zip_crypto_hmac_output(_zip_crypto_hmac_t * hmac,zip_uint8_t * data)482 _zip_crypto_hmac_output(_zip_crypto_hmac_t *hmac, zip_uint8_t *data) {
483 if (hmac == NULL) {
484 return false;
485 }
486
487 return BCRYPT_SUCCESS(BCryptFinishHash(hmac->hHash, data, hmac->cbHash, 0));
488 }
489
490 ZIP_EXTERN bool
zip_secure_random(zip_uint8_t * buffer,zip_uint16_t length)491 zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length) {
492 return BCRYPT_SUCCESS(BCryptGenRandom(NULL, buffer, length, BCRYPT_USE_SYSTEM_PREFERRED_RNG));
493 }
494