"Fossies" - the Fresh Open Source Software Archive

Member "libzip-1.6.0/lib/zip_crypto_win.c" (24 Jan 2020, 15147 Bytes) of package /linux/misc/libzip-1.6.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "zip_crypto_win.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.5.2_vs_1.6.0.

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