blob: 7608ab4ac41587cff97c14412477383a7ee72391 [file] [edit]
/*
* OpenSSL-based implementation of bcrypt.dll cryptographic provider.
*
* Copyright 2009 Henri Verbeet for CodeWeavers
* Copyright 2018 Hans Leidekker for CodeWeavers
* Copyright 2024 Google LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
#if 0
#pragma makedep unix
#endif
#include "config.h"
#if defined(HAVE_OPENSSL)
#include <stdarg.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <dlfcn.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/ecdsa.h>
#include <openssl/dh.h>
#include <openssl/dsa.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/objects.h>
#include <openssl/provider.h>
/* Every asymmetric algorithm here is implemented on the low-level RSA/DSA/DH/
* EC_KEY APIs, which OpenSSL 3.x deprecated. MAKE_FUNCPTR needs their
* declarations, so a libcrypto whose headers omit them must fail the build
* rather than degrade to STATUS_NOT_SUPPORTED for everything at runtime. */
#if defined(OPENSSL_NO_DEPRECATED_3_0) || defined(OPENSSL_NO_DEPRECATED)
#error "the bcrypt OpenSSL backend requires the deprecated low-level RSA/DSA/DH/EC_KEY APIs"
#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winbase.h"
#include "winternl.h"
#include "ntsecapi.h"
#include "wincrypt.h"
#include "bcrypt.h"
#include "bcrypt_internal.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(bcrypt);
WINE_DECLARE_DEBUG_CHANNEL(winediag);
struct key_symmetric_openssl
{
EVP_CIPHER_CTX *cipher;
/* OpenSSL rejects EVP_CTRL_GCM_GET_TAG on a decrypt context, so when decrypting GCM
* we run a parallel encrypt context over the recovered plaintext and read the tag off
* that instead. GnuTLS could read the tag straight off its single direction-agnostic
* handle and needed no such shadow. */
EVP_CIPHER_CTX *tag_cipher;
UCHAR *auth_data;
ULONG auth_data_len;
BOOL is_decrypt;
};
union key_data
{
struct key_symmetric_openssl *s;
struct
{
void *key; /* RSA*, EC_KEY*, DSA*, or DH* */
void *pubkey; /* Optional public key / parameters */
void *reserved;
} a;
};
C_ASSERT( sizeof(union key_data) <= sizeof(((struct key *)0)->private) );
static union key_data *key_data( struct key *key )
{
return (union key_data *)key->private;
}
static void *libcrypto_handle;
static OSSL_PROVIDER *legacy_provider;
static OSSL_PROVIDER *default_provider;
#define MAKE_FUNCPTR(f) static typeof(f) * p##f
MAKE_FUNCPTR(OSSL_PROVIDER_load);
MAKE_FUNCPTR(OSSL_PROVIDER_unload);
/* EVP cipher functions */
MAKE_FUNCPTR(EVP_CIPHER_CTX_new);
MAKE_FUNCPTR(EVP_CIPHER_CTX_free);
MAKE_FUNCPTR(EVP_CIPHER_CTX_ctrl);
MAKE_FUNCPTR(EVP_CIPHER_CTX_set_padding);
MAKE_FUNCPTR(EVP_CIPHER_CTX_set_key_length);
MAKE_FUNCPTR(EVP_CIPHER_get_iv_length);
MAKE_FUNCPTR(EVP_CIPHER_get_key_length);
MAKE_FUNCPTR(EVP_EncryptInit_ex);
MAKE_FUNCPTR(EVP_EncryptUpdate);
MAKE_FUNCPTR(EVP_EncryptFinal_ex);
MAKE_FUNCPTR(EVP_DecryptInit_ex);
MAKE_FUNCPTR(EVP_DecryptUpdate);
MAKE_FUNCPTR(EVP_DecryptFinal_ex);
MAKE_FUNCPTR(EVP_aes_128_cbc);
MAKE_FUNCPTR(EVP_aes_192_cbc);
MAKE_FUNCPTR(EVP_aes_256_cbc);
MAKE_FUNCPTR(EVP_aes_128_ecb);
MAKE_FUNCPTR(EVP_aes_192_ecb);
MAKE_FUNCPTR(EVP_aes_256_ecb);
MAKE_FUNCPTR(EVP_aes_128_gcm);
MAKE_FUNCPTR(EVP_aes_192_gcm);
MAKE_FUNCPTR(EVP_aes_256_gcm);
MAKE_FUNCPTR(EVP_aes_128_cfb8);
MAKE_FUNCPTR(EVP_aes_192_cfb8);
MAKE_FUNCPTR(EVP_aes_256_cfb8);
MAKE_FUNCPTR(EVP_des_ede3_cbc);
static const EVP_CIPHER * (*pEVP_rc4)(void);
/* Error & utility functions */
MAKE_FUNCPTR(CRYPTO_free);
MAKE_FUNCPTR(CRYPTO_malloc);
MAKE_FUNCPTR(CRYPTO_memcmp);
MAKE_FUNCPTR(ERR_clear_error);
MAKE_FUNCPTR(ERR_peek_last_error);
MAKE_FUNCPTR(OPENSSL_cleanse);
MAKE_FUNCPTR(OPENSSL_init_crypto);
#ifndef OPENSSL_INIT_NO_LOAD_CONFIG
#define OPENSSL_INIT_NO_LOAD_CONFIG 0x00000080L
#endif
#ifndef RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING
#define RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING 8
#endif
#ifndef RSA_R_UNKNOWN_PADDING_TYPE
#define RSA_R_UNKNOWN_PADDING_TYPE 114
#endif
/* BIGNUM functions */
MAKE_FUNCPTR(BN_new);
MAKE_FUNCPTR(BN_free);
MAKE_FUNCPTR(BN_clear_free);
MAKE_FUNCPTR(BN_bin2bn);
MAKE_FUNCPTR(BN_bn2bin);
MAKE_FUNCPTR(BN_num_bits);
MAKE_FUNCPTR(BN_set_word);
MAKE_FUNCPTR(BN_get_word);
MAKE_FUNCPTR(BN_CTX_new);
MAKE_FUNCPTR(BN_CTX_free);
MAKE_FUNCPTR(BN_sub);
MAKE_FUNCPTR(BN_mul);
MAKE_FUNCPTR(BN_div);
MAKE_FUNCPTR(BN_mod_inverse);
MAKE_FUNCPTR(BN_mod_exp);
MAKE_FUNCPTR(BN_value_one);
MAKE_FUNCPTR(BN_dup);
MAKE_FUNCPTR(BN_cmp);
static inline void ossl_bn_clear_free( BIGNUM *bn )
{
if (bn) pBN_clear_free( bn );
}
static inline int bn_num_bytes( const BIGNUM *bn )
{
return bn ? (pBN_num_bits( bn ) + 7) / 8 : 0;
}
/* RSA functions */
MAKE_FUNCPTR(RSA_new);
MAKE_FUNCPTR(RSA_free);
MAKE_FUNCPTR(RSA_generate_key_ex);
MAKE_FUNCPTR(RSA_get0_key);
MAKE_FUNCPTR(RSA_get0_factors);
MAKE_FUNCPTR(RSA_get0_crt_params);
MAKE_FUNCPTR(RSA_set0_key);
MAKE_FUNCPTR(RSA_set0_factors);
MAKE_FUNCPTR(RSA_set0_crt_params);
MAKE_FUNCPTR(RSA_public_encrypt);
MAKE_FUNCPTR(RSA_private_decrypt);
MAKE_FUNCPTR(RSA_private_encrypt);
MAKE_FUNCPTR(RSA_public_decrypt);
MAKE_FUNCPTR(RSA_sign);
MAKE_FUNCPTR(RSA_verify);
MAKE_FUNCPTR(RSA_padding_add_PKCS1_PSS_mgf1);
MAKE_FUNCPTR(RSA_verify_PKCS1_PSS_mgf1);
MAKE_FUNCPTR(RSA_padding_add_PKCS1_OAEP_mgf1);
MAKE_FUNCPTR(RSAPublicKey_dup);
MAKE_FUNCPTR(RSAPrivateKey_dup);
/* EC functions */
MAKE_FUNCPTR(EC_KEY_new_by_curve_name);
MAKE_FUNCPTR(EC_KEY_free);
MAKE_FUNCPTR(EC_KEY_dup);
MAKE_FUNCPTR(EC_KEY_generate_key);
MAKE_FUNCPTR(EC_KEY_get0_group);
MAKE_FUNCPTR(EC_KEY_get0_public_key);
MAKE_FUNCPTR(EC_KEY_set_public_key);
MAKE_FUNCPTR(EC_KEY_set_public_key_affine_coordinates);
MAKE_FUNCPTR(EC_KEY_get0_private_key);
MAKE_FUNCPTR(EC_KEY_set_private_key);
MAKE_FUNCPTR(EC_KEY_check_key);
MAKE_FUNCPTR(EC_POINT_new);
MAKE_FUNCPTR(EC_POINT_free);
MAKE_FUNCPTR(EC_POINT_get_affine_coordinates_GFp);
MAKE_FUNCPTR(EC_GROUP_get_curve_name);
MAKE_FUNCPTR(ECDSA_do_sign);
MAKE_FUNCPTR(ECDSA_do_verify);
MAKE_FUNCPTR(ECDSA_SIG_new);
MAKE_FUNCPTR(ECDSA_SIG_free);
MAKE_FUNCPTR(ECDSA_SIG_get0);
MAKE_FUNCPTR(ECDSA_SIG_set0);
MAKE_FUNCPTR(ECDH_compute_key);
/* DSA functions */
MAKE_FUNCPTR(DSA_new);
MAKE_FUNCPTR(DSA_free);
MAKE_FUNCPTR(DSA_generate_parameters_ex);
MAKE_FUNCPTR(DSA_generate_key);
MAKE_FUNCPTR(DSA_get0_pqg);
MAKE_FUNCPTR(DSA_get0_key);
MAKE_FUNCPTR(DSA_set0_pqg);
MAKE_FUNCPTR(DSA_set0_key);
MAKE_FUNCPTR(DSA_do_sign);
MAKE_FUNCPTR(DSA_do_verify);
MAKE_FUNCPTR(DSA_SIG_new);
MAKE_FUNCPTR(DSA_SIG_free);
MAKE_FUNCPTR(DSA_SIG_get0);
MAKE_FUNCPTR(DSA_SIG_set0);
/* DH functions */
MAKE_FUNCPTR(DH_new);
MAKE_FUNCPTR(DH_free);
MAKE_FUNCPTR(DH_generate_parameters_ex);
MAKE_FUNCPTR(DH_generate_key);
MAKE_FUNCPTR(DH_get0_pqg);
MAKE_FUNCPTR(DH_get0_key);
MAKE_FUNCPTR(DH_set0_pqg);
MAKE_FUNCPTR(DH_set0_key);
MAKE_FUNCPTR(DH_check_pub_key);
MAKE_FUNCPTR(DH_compute_key);
MAKE_FUNCPTR(DH_get_1024_160);
MAKE_FUNCPTR(DH_get_2048_256);
/* EVP Message Digest & PKEY */
MAKE_FUNCPTR(EVP_MD_get_size);
MAKE_FUNCPTR(EVP_PKEY_CTX_new_id);
MAKE_FUNCPTR(EVP_PKEY_CTX_new);
MAKE_FUNCPTR(EVP_PKEY_CTX_free);
MAKE_FUNCPTR(EVP_PKEY_CTX_set0_rsa_oaep_label);
MAKE_FUNCPTR(EVP_PKEY_CTX_set_rsa_mgf1_md);
MAKE_FUNCPTR(EVP_PKEY_CTX_set_rsa_oaep_md);
MAKE_FUNCPTR(EVP_PKEY_CTX_set_rsa_padding);
MAKE_FUNCPTR(EVP_PKEY_decrypt);
MAKE_FUNCPTR(EVP_PKEY_decrypt_init);
MAKE_FUNCPTR(EVP_PKEY_free);
MAKE_FUNCPTR(EVP_PKEY_keygen_init);
MAKE_FUNCPTR(EVP_PKEY_keygen);
MAKE_FUNCPTR(EVP_PKEY_get_raw_public_key);
MAKE_FUNCPTR(EVP_PKEY_get_raw_private_key);
MAKE_FUNCPTR(EVP_PKEY_new);
MAKE_FUNCPTR(EVP_PKEY_new_raw_public_key);
MAKE_FUNCPTR(EVP_PKEY_new_raw_private_key);
MAKE_FUNCPTR(EVP_PKEY_set1_RSA);
MAKE_FUNCPTR(EVP_PKEY_derive_init);
MAKE_FUNCPTR(EVP_PKEY_derive_set_peer);
MAKE_FUNCPTR(EVP_PKEY_derive);
MAKE_FUNCPTR(EVP_sha1);
MAKE_FUNCPTR(EVP_sha256);
MAKE_FUNCPTR(EVP_sha384);
MAKE_FUNCPTR(EVP_sha512);
MAKE_FUNCPTR(EVP_md5);
static const EVP_MD * (*pEVP_md4)(void);
static const EVP_MD * (*pEVP_md2)(void);
#undef MAKE_FUNCPTR
/* The OpenSSL we want is bundled with the app, not the one in /system or /apex:
* Android ships BoringSSL under the same soname, and the app-process linker
* namespace can hand it to us. Walk LD_LIBRARY_PATH ourselves and skip the
* platform directories before falling back to the plain soname. */
static void *load_crypto_lib( const char *name )
{
const char *ld_path = getenv("LD_LIBRARY_PATH");
char path[4096];
void *handle;
if (ld_path)
{
const char *start = ld_path;
while (*start)
{
const char *end = strchr(start, ':');
size_t len = end ? (size_t)(end - start) : strlen(start);
if (len > 0 && len + strlen(name) + 2 <= sizeof(path) &&
strncmp(start, "/system", 7) && strncmp(start, "/apex", 5))
{
memcpy(path, start, len);
path[len] = '/';
strcpy(path + len + 1, name);
if ((handle = dlopen(path, RTLD_NOW))) return handle;
}
if (!end) break;
start = end + 1;
}
}
return dlopen(name, RTLD_NOW);
}
/* Whether the deprecated low-level family backing an algorithm was resolved by
* LOAD_FUNCPTR_OPT. One sentinel per family is enough: OpenSSL deprecates and
* removes them as a unit. */
static BOOL alg_family_available( const struct key *key )
{
if (key->alg_id == ALG_ID_ECDH && key->u.a.curve_id == ECC_CURVE_25519)
return TRUE;
switch (key->alg_id)
{
case ALG_ID_RSA:
case ALG_ID_RSA_SIGN:
return pRSA_new != NULL;
case ALG_ID_DSA:
return pDSA_new != NULL;
case ALG_ID_DH:
return pDH_new != NULL;
case ALG_ID_ECDH:
case ALG_ID_ECDH_P256:
case ALG_ID_ECDH_P384:
case ALG_ID_ECDH_P521:
case ALG_ID_ECDSA:
case ALG_ID_ECDSA_P256:
case ALG_ID_ECDSA_P384:
case ALG_ID_ECDSA_P521:
return pEC_KEY_new_by_curve_name != NULL;
default:
return TRUE;
}
}
static NTSTATUS process_attach( void *args )
{
/* Force OPENSSL_CONF to /dev/null so ambient host/container environment
* variables cannot cause OpenSSL to load external configuration files. */
setenv( "OPENSSL_CONF", "/dev/null", 1 );
if (!(libcrypto_handle = load_crypto_lib( SONAME_LIBCRYPTO )))
{
ERR_(winediag)( "failed to load libcrypto, no support for encryption\n" );
return STATUS_DLL_NOT_FOUND;
}
#define LOAD_FUNCPTR(f) \
if (!(p##f = dlsym( libcrypto_handle, #f ))) \
{ \
ERR( "failed to load %s\n", #f ); \
goto fail; \
}
/* The low-level RSA/DSA/DH/EC_KEY families are deprecated in OpenSSL 3.x and a
* libcrypto may be built without them. Losing one must not take down the whole
* provider, so they are loaded optionally and each algorithm family is gated on
* its sentinel by alg_family_available() below. */
#define LOAD_FUNCPTR_OPT(f) \
if (!(p##f = dlsym( libcrypto_handle, #f ))) \
WARN( "failed to load optional %s\n", #f );
LOAD_FUNCPTR(EVP_CIPHER_CTX_new)
LOAD_FUNCPTR(EVP_CIPHER_CTX_free)
LOAD_FUNCPTR(EVP_CIPHER_CTX_ctrl)
LOAD_FUNCPTR(EVP_CIPHER_CTX_set_padding)
LOAD_FUNCPTR(EVP_CIPHER_CTX_set_key_length)
LOAD_FUNCPTR(EVP_CIPHER_get_iv_length)
LOAD_FUNCPTR(EVP_CIPHER_get_key_length)
LOAD_FUNCPTR(EVP_EncryptInit_ex)
LOAD_FUNCPTR(EVP_EncryptUpdate)
LOAD_FUNCPTR(EVP_EncryptFinal_ex)
LOAD_FUNCPTR(EVP_DecryptInit_ex)
LOAD_FUNCPTR(EVP_DecryptUpdate)
LOAD_FUNCPTR(EVP_DecryptFinal_ex)
LOAD_FUNCPTR(EVP_aes_128_cbc)
LOAD_FUNCPTR(EVP_aes_192_cbc)
LOAD_FUNCPTR(EVP_aes_256_cbc)
LOAD_FUNCPTR(EVP_aes_128_ecb)
LOAD_FUNCPTR(EVP_aes_192_ecb)
LOAD_FUNCPTR(EVP_aes_256_ecb)
LOAD_FUNCPTR(EVP_aes_128_gcm)
LOAD_FUNCPTR(EVP_aes_192_gcm)
LOAD_FUNCPTR(EVP_aes_256_gcm)
LOAD_FUNCPTR(EVP_aes_128_cfb8)
LOAD_FUNCPTR(EVP_aes_192_cfb8)
LOAD_FUNCPTR(EVP_aes_256_cfb8)
LOAD_FUNCPTR(EVP_des_ede3_cbc)
LOAD_FUNCPTR(CRYPTO_free)
LOAD_FUNCPTR(CRYPTO_malloc)
LOAD_FUNCPTR(CRYPTO_memcmp)
LOAD_FUNCPTR(ERR_clear_error)
LOAD_FUNCPTR(ERR_peek_last_error)
LOAD_FUNCPTR(OPENSSL_cleanse)
LOAD_FUNCPTR(OPENSSL_init_crypto)
LOAD_FUNCPTR(BN_new)
LOAD_FUNCPTR(BN_free)
LOAD_FUNCPTR(BN_clear_free)
LOAD_FUNCPTR(BN_bin2bn)
LOAD_FUNCPTR(BN_bn2bin)
LOAD_FUNCPTR(BN_num_bits)
LOAD_FUNCPTR(BN_set_word)
LOAD_FUNCPTR(BN_get_word)
LOAD_FUNCPTR(BN_CTX_new)
LOAD_FUNCPTR(BN_CTX_free)
LOAD_FUNCPTR(BN_sub)
LOAD_FUNCPTR(BN_mul)
LOAD_FUNCPTR(BN_div)
LOAD_FUNCPTR(BN_mod_inverse)
LOAD_FUNCPTR(BN_mod_exp)
LOAD_FUNCPTR(BN_value_one)
LOAD_FUNCPTR(BN_dup)
LOAD_FUNCPTR(BN_cmp)
LOAD_FUNCPTR_OPT(RSA_new)
LOAD_FUNCPTR_OPT(RSA_free)
LOAD_FUNCPTR_OPT(RSA_generate_key_ex)
LOAD_FUNCPTR_OPT(RSA_get0_key)
LOAD_FUNCPTR_OPT(RSA_get0_factors)
LOAD_FUNCPTR_OPT(RSA_get0_crt_params)
LOAD_FUNCPTR_OPT(RSA_set0_key)
LOAD_FUNCPTR_OPT(RSA_set0_factors)
LOAD_FUNCPTR_OPT(RSA_set0_crt_params)
LOAD_FUNCPTR_OPT(RSA_private_encrypt)
LOAD_FUNCPTR_OPT(RSA_public_decrypt)
LOAD_FUNCPTR_OPT(RSA_public_encrypt)
LOAD_FUNCPTR_OPT(RSA_private_decrypt)
LOAD_FUNCPTR_OPT(RSA_sign)
LOAD_FUNCPTR_OPT(RSA_verify)
LOAD_FUNCPTR_OPT(RSA_padding_add_PKCS1_PSS_mgf1)
LOAD_FUNCPTR_OPT(RSA_verify_PKCS1_PSS_mgf1)
LOAD_FUNCPTR_OPT(RSA_padding_add_PKCS1_OAEP_mgf1)
LOAD_FUNCPTR_OPT(RSAPublicKey_dup)
LOAD_FUNCPTR_OPT(RSAPrivateKey_dup)
LOAD_FUNCPTR_OPT(EC_KEY_new_by_curve_name)
LOAD_FUNCPTR_OPT(EC_KEY_free)
LOAD_FUNCPTR_OPT(EC_KEY_dup)
LOAD_FUNCPTR_OPT(EC_KEY_generate_key)
LOAD_FUNCPTR_OPT(EC_KEY_get0_group)
LOAD_FUNCPTR_OPT(EC_KEY_get0_public_key)
LOAD_FUNCPTR_OPT(EC_KEY_set_public_key)
LOAD_FUNCPTR_OPT(EC_KEY_set_public_key_affine_coordinates)
LOAD_FUNCPTR_OPT(EC_KEY_get0_private_key)
LOAD_FUNCPTR_OPT(EC_KEY_set_private_key)
LOAD_FUNCPTR_OPT(EC_KEY_check_key)
LOAD_FUNCPTR_OPT(EC_POINT_new)
LOAD_FUNCPTR_OPT(EC_POINT_free)
LOAD_FUNCPTR_OPT(EC_POINT_get_affine_coordinates_GFp)
LOAD_FUNCPTR_OPT(EC_GROUP_get_curve_name)
LOAD_FUNCPTR_OPT(ECDSA_do_sign)
LOAD_FUNCPTR_OPT(ECDSA_do_verify)
LOAD_FUNCPTR_OPT(ECDSA_SIG_new)
LOAD_FUNCPTR_OPT(ECDSA_SIG_free)
LOAD_FUNCPTR_OPT(ECDSA_SIG_get0)
LOAD_FUNCPTR_OPT(ECDSA_SIG_set0)
LOAD_FUNCPTR_OPT(ECDH_compute_key)
LOAD_FUNCPTR_OPT(DSA_new)
LOAD_FUNCPTR_OPT(DSA_free)
LOAD_FUNCPTR_OPT(DSA_generate_parameters_ex)
LOAD_FUNCPTR_OPT(DSA_generate_key)
LOAD_FUNCPTR_OPT(DSA_get0_pqg)
LOAD_FUNCPTR_OPT(DSA_get0_key)
LOAD_FUNCPTR_OPT(DSA_set0_pqg)
LOAD_FUNCPTR_OPT(DSA_set0_key)
LOAD_FUNCPTR_OPT(DSA_do_sign)
LOAD_FUNCPTR_OPT(DSA_do_verify)
LOAD_FUNCPTR_OPT(DSA_SIG_new)
LOAD_FUNCPTR_OPT(DSA_SIG_free)
LOAD_FUNCPTR_OPT(DSA_SIG_get0)
LOAD_FUNCPTR_OPT(DSA_SIG_set0)
LOAD_FUNCPTR_OPT(DH_new)
LOAD_FUNCPTR_OPT(DH_free)
LOAD_FUNCPTR_OPT(DH_generate_parameters_ex)
LOAD_FUNCPTR_OPT(DH_generate_key)
LOAD_FUNCPTR_OPT(DH_get0_pqg)
LOAD_FUNCPTR_OPT(DH_get0_key)
LOAD_FUNCPTR_OPT(DH_set0_pqg)
LOAD_FUNCPTR_OPT(DH_set0_key)
LOAD_FUNCPTR_OPT(DH_check_pub_key)
LOAD_FUNCPTR_OPT(DH_compute_key)
LOAD_FUNCPTR_OPT(DH_get_1024_160)
LOAD_FUNCPTR_OPT(DH_get_2048_256)
LOAD_FUNCPTR(EVP_MD_get_size)
LOAD_FUNCPTR(EVP_PKEY_CTX_new_id)
LOAD_FUNCPTR(EVP_PKEY_CTX_new)
LOAD_FUNCPTR(EVP_PKEY_CTX_free)
LOAD_FUNCPTR(EVP_PKEY_CTX_set0_rsa_oaep_label)
LOAD_FUNCPTR(EVP_PKEY_CTX_set_rsa_mgf1_md)
LOAD_FUNCPTR(EVP_PKEY_CTX_set_rsa_oaep_md)
LOAD_FUNCPTR(EVP_PKEY_CTX_set_rsa_padding)
LOAD_FUNCPTR(EVP_PKEY_decrypt)
LOAD_FUNCPTR(EVP_PKEY_decrypt_init)
LOAD_FUNCPTR(EVP_PKEY_free)
LOAD_FUNCPTR(EVP_PKEY_keygen_init)
LOAD_FUNCPTR(EVP_PKEY_keygen)
LOAD_FUNCPTR(EVP_PKEY_get_raw_public_key)
LOAD_FUNCPTR(EVP_PKEY_get_raw_private_key)
LOAD_FUNCPTR(EVP_PKEY_new)
LOAD_FUNCPTR(EVP_PKEY_new_raw_public_key)
LOAD_FUNCPTR(EVP_PKEY_new_raw_private_key)
LOAD_FUNCPTR_OPT(EVP_PKEY_set1_RSA)
LOAD_FUNCPTR(EVP_PKEY_derive_init)
LOAD_FUNCPTR(EVP_PKEY_derive_set_peer)
LOAD_FUNCPTR(EVP_PKEY_derive)
LOAD_FUNCPTR(EVP_sha1)
LOAD_FUNCPTR(EVP_sha256)
LOAD_FUNCPTR(EVP_sha384)
LOAD_FUNCPTR(EVP_sha512)
LOAD_FUNCPTR(EVP_md5)
LOAD_FUNCPTR(OSSL_PROVIDER_load)
LOAD_FUNCPTR(OSSL_PROVIDER_unload)
#undef LOAD_FUNCPTR
#undef LOAD_FUNCPTR_OPT
pOPENSSL_init_crypto( OPENSSL_INIT_NO_LOAD_CONFIG, NULL );
/* RC4, MD4 and MD2 are optional: OpenSSL can be configured without them,
* and every caller already handles their absence. */
pEVP_rc4 = dlsym( libcrypto_handle, "EVP_rc4" );
pEVP_md4 = dlsym( libcrypto_handle, "EVP_md4" );
pEVP_md2 = dlsym( libcrypto_handle, "EVP_md2" );
/* Loading a provider explicitly disables the implicit default fallback, so
* the default provider has to be loaded explicitly alongside the legacy one. */
if (!(default_provider = pOSSL_PROVIDER_load( NULL, "default" )))
{
ERR( "failed to load the default OpenSSL provider\n" );
goto fail;
}
/* The legacy provider supplies RC2/RC4/MD4/DES; it is not always present. */
if (!(legacy_provider = pOSSL_PROVIDER_load( NULL, "legacy" )))
{
WARN( "failed to load the legacy OpenSSL provider\n" );
pERR_clear_error();
}
return STATUS_SUCCESS;
fail:
dlclose( libcrypto_handle );
libcrypto_handle = NULL;
return STATUS_DLL_NOT_FOUND;
}
static NTSTATUS process_detach( void *args )
{
/* No OPENSSL_cleanup() here, unlike gnutls.c's gnutls_global_deinit(): that deinit is
* refcounted, OPENSSL_cleanup() is not, and it would tear down library state that
* secur32 and crypt32 are still using in this process. */
if (legacy_provider) pOSSL_PROVIDER_unload( legacy_provider );
if (default_provider) pOSSL_PROVIDER_unload( default_provider );
legacy_provider = NULL;
default_provider = NULL;
if (libcrypto_handle)
{
dlclose( libcrypto_handle );
libcrypto_handle = NULL;
}
return STATUS_SUCCESS;
}
static const EVP_CIPHER *get_evp_cipher( const struct key *key )
{
switch (key->alg_id)
{
case ALG_ID_3DES:
if (key->u.s.mode == CHAIN_MODE_CBC) return pEVP_des_ede3_cbc();
break;
/* Addition beyond gnutls.c, which had no ALG_ID_RC4 case at all. */
case ALG_ID_RC4:
if (pEVP_rc4) return pEVP_rc4();
break;
case ALG_ID_AES:
switch (key->u.s.mode)
{
case CHAIN_MODE_GCM:
if (key->u.s.secret_len == 16) return pEVP_aes_128_gcm();
/* Addition beyond gnutls.c, whose GCM branch listed only 16 and 32. */
if (key->u.s.secret_len == 24) return pEVP_aes_192_gcm();
if (key->u.s.secret_len == 32) return pEVP_aes_256_gcm();
break;
case CHAIN_MODE_ECB:
if (key->u.s.secret_len == 16) return pEVP_aes_128_ecb();
if (key->u.s.secret_len == 24) return pEVP_aes_192_ecb();
if (key->u.s.secret_len == 32) return pEVP_aes_256_ecb();
break;
case CHAIN_MODE_CBC:
if (key->u.s.secret_len == 16) return pEVP_aes_128_cbc();
if (key->u.s.secret_len == 24) return pEVP_aes_192_cbc();
if (key->u.s.secret_len == 32) return pEVP_aes_256_cbc();
break;
case CHAIN_MODE_CFB:
if (key->u.s.secret_len == 16) return pEVP_aes_128_cfb8();
if (key->u.s.secret_len == 24) return pEVP_aes_192_cfb8();
if (key->u.s.secret_len == 32) return pEVP_aes_256_cfb8();
break;
default:
break;
}
FIXME( "AES mode %u with key length %u not supported\n", key->u.s.mode, key->u.s.secret_len );
break;
default:
FIXME( "algorithm %u not supported\n", key->alg_id );
break;
}
return NULL;
}
static NTSTATUS key_symmetric_vector_reset( void *args )
{
struct key *key = args;
struct key_symmetric_openssl *s = key_data(key)->s;
if (s)
{
if (s->cipher) pEVP_CIPHER_CTX_free( s->cipher );
if (s->tag_cipher) pEVP_CIPHER_CTX_free( s->tag_cipher );
if (s->auth_data)
{
pOPENSSL_cleanse( s->auth_data, s->auth_data_len );
free( s->auth_data );
}
free( s );
key_data(key)->s = NULL;
}
return STATUS_SUCCESS;
}
static NTSTATUS init_cipher_ctx( struct key *key, BOOL encrypt )
{
const EVP_CIPHER *cipher;
struct key_symmetric_openssl *s;
int len;
if (!key_data(key)->s)
{
if (!(key_data(key)->s = calloc( 1, sizeof(struct key_symmetric_openssl) )))
return STATUS_NO_MEMORY;
}
s = key_data(key)->s;
if (s->cipher)
{
if (s->is_decrypt == !encrypt) return STATUS_SUCCESS;
pEVP_CIPHER_CTX_free( s->cipher );
s->cipher = NULL;
if (s->tag_cipher)
{
pEVP_CIPHER_CTX_free( s->tag_cipher );
s->tag_cipher = NULL;
}
}
if (!(cipher = get_evp_cipher( key ))) return STATUS_NOT_SUPPORTED;
if (key->alg_id == ALG_ID_RC4)
{
if (key->u.s.secret_len < 1 || key->u.s.secret_len > 256)
return STATUS_INVALID_PARAMETER;
}
else
{
int expected_key_len = pEVP_CIPHER_get_key_length( cipher );
if (expected_key_len > 0 && key->u.s.secret_len != (ULONG)expected_key_len)
return STATUS_INVALID_PARAMETER;
}
/* EVP_{En,De}cryptInit_ex() takes the IV as a bare pointer and reads as many bytes as the
* cipher requires (or as EVP_CTRL_GCM_SET_IVLEN declares below), while bcrypt_main.c
* allocates u.s.vector at exactly the caller's length without validating it. A short IV is
* therefore an out-of-bounds heap read. The GnuTLS backend passed a sized gnutls_datum_t
* and gnutls_cipher_init() rejected the mismatch for us; reproduce that check explicitly.
* Divergence from gnutls.c: it surfaced this as STATUS_INTERNAL_ERROR, but Windows reports
* a bad IV length as STATUS_INVALID_PARAMETER. */
if (key->u.s.vector)
{
int iv_len = pEVP_CIPHER_get_iv_length( cipher );
if (key->u.s.mode == CHAIN_MODE_GCM && key->u.s.vector_len) iv_len = key->u.s.vector_len;
if (iv_len > 0 && key->u.s.vector_len != (ULONG)iv_len) return STATUS_INVALID_PARAMETER;
}
s->cipher = pEVP_CIPHER_CTX_new();
if (!s->cipher) return STATUS_NO_MEMORY;
if (encrypt)
{
if (!pEVP_EncryptInit_ex( s->cipher, cipher, NULL, NULL, NULL ))
goto error;
if (key->alg_id == ALG_ID_RC4 &&
!pEVP_CIPHER_CTX_set_key_length( s->cipher, key->u.s.secret_len ))
goto error;
if (key->u.s.mode == CHAIN_MODE_GCM && key->u.s.vector_len &&
!pEVP_CIPHER_CTX_ctrl( s->cipher, EVP_CTRL_GCM_SET_IVLEN, key->u.s.vector_len, NULL ))
goto error;
if (!pEVP_EncryptInit_ex( s->cipher, NULL, NULL, key->u.s.secret, key->u.s.vector ))
goto error;
if (key->u.s.mode == CHAIN_MODE_GCM && s->auth_data && s->auth_data_len &&
!pEVP_EncryptUpdate( s->cipher, NULL, &len, s->auth_data, s->auth_data_len ))
goto error;
s->is_decrypt = FALSE;
}
else
{
if (!pEVP_DecryptInit_ex( s->cipher, cipher, NULL, NULL, NULL ))
goto error;
if (key->alg_id == ALG_ID_RC4 &&
!pEVP_CIPHER_CTX_set_key_length( s->cipher, key->u.s.secret_len ))
goto error;
if (key->u.s.mode == CHAIN_MODE_GCM && key->u.s.vector_len &&
!pEVP_CIPHER_CTX_ctrl( s->cipher, EVP_CTRL_GCM_SET_IVLEN, key->u.s.vector_len, NULL ))
goto error;
if (!pEVP_DecryptInit_ex( s->cipher, NULL, NULL, key->u.s.secret, key->u.s.vector ))
goto error;
if (key->u.s.mode == CHAIN_MODE_GCM && s->auth_data && s->auth_data_len &&
!pEVP_DecryptUpdate( s->cipher, NULL, &len, s->auth_data, s->auth_data_len ))
goto error;
s->is_decrypt = TRUE;
}
pEVP_CIPHER_CTX_set_padding( s->cipher, 0 );
if (!encrypt && key->u.s.mode == CHAIN_MODE_GCM && !s->tag_cipher)
{
/* Shadow encrypt context used to recompute the tag while decrypting. */
if (!(s->tag_cipher = pEVP_CIPHER_CTX_new())) goto error;
if (!pEVP_EncryptInit_ex( s->tag_cipher, cipher, NULL, NULL, NULL ) ||
(key->u.s.vector_len &&
!pEVP_CIPHER_CTX_ctrl( s->tag_cipher, EVP_CTRL_GCM_SET_IVLEN, key->u.s.vector_len, NULL )) ||
!pEVP_EncryptInit_ex( s->tag_cipher, NULL, NULL, key->u.s.secret, key->u.s.vector ))
{
pEVP_CIPHER_CTX_free( s->tag_cipher );
s->tag_cipher = NULL;
goto error;
}
pEVP_CIPHER_CTX_set_padding( s->tag_cipher, 0 );
if (s->auth_data && s->auth_data_len &&
!pEVP_EncryptUpdate( s->tag_cipher, NULL, &len, s->auth_data, s->auth_data_len ))
{
pEVP_CIPHER_CTX_free( s->tag_cipher );
s->tag_cipher = NULL;
goto error;
}
}
return STATUS_SUCCESS;
error:
pERR_clear_error();
if (s->cipher) pEVP_CIPHER_CTX_free( s->cipher );
s->cipher = NULL;
if (s->tag_cipher) pEVP_CIPHER_CTX_free( s->tag_cipher );
s->tag_cipher = NULL;
return STATUS_INTERNAL_ERROR;
}
static NTSTATUS key_symmetric_set_auth_data( void *args )
{
const struct key_symmetric_set_auth_data_params *params = args;
struct key_symmetric_openssl *s;
UCHAR *auth_data;
int len;
/* Addition beyond gnutls.c: the EVP_*Update() length parameter is an int,
* where gnutls_cipher_add_auth() took a size_t, so an over-large length
* would be truncated rather than rejected. STATUS_INVALID_PARAMETER is
* BCrypt's conventional status for an unusable buffer length. */
if (params->len > INT_MAX) return STATUS_INVALID_PARAMETER;
if (!params->auth_data || !params->len) return STATUS_SUCCESS;
if (!key_data(params->key)->s)
{
if (!(key_data(params->key)->s = calloc( 1, sizeof(struct key_symmetric_openssl) )))
return STATUS_NO_MEMORY;
}
s = key_data(params->key)->s;
if (params->len > (ULONG)INT_MAX - s->auth_data_len) return STATUS_INVALID_PARAMETER;
/* gnutls_cipher_add_auth() accumulated across calls, so two calls produced the AAD
* A||B rather than just B. Match that. */
if (!(auth_data = realloc( s->auth_data, s->auth_data_len + params->len )))
return STATUS_NO_MEMORY;
s->auth_data = auth_data;
memcpy( s->auth_data + s->auth_data_len, params->auth_data, params->len );
s->auth_data_len += params->len;
/* If a context already exists it has been seeded with the earlier AAD, so only the
* new bytes go in. Otherwise init_cipher_ctx() will feed the whole buffer. */
if (s->cipher)
{
if (s->is_decrypt)
{
if (!pEVP_DecryptUpdate( s->cipher, NULL, &len, params->auth_data, params->len ))
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
}
else if (!pEVP_EncryptUpdate( s->cipher, NULL, &len, params->auth_data, params->len ))
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
}
if (s->tag_cipher && !pEVP_EncryptUpdate( s->tag_cipher, NULL, &len, params->auth_data, params->len ))
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
return STATUS_SUCCESS;
}
static NTSTATUS key_symmetric_encrypt( void *args )
{
const struct key_symmetric_encrypt_params *params = args;
struct key_symmetric_openssl *s;
NTSTATUS status;
int len = 0;
if (params->input_len > INT_MAX || params->output_len > INT_MAX) return STATUS_INVALID_PARAMETER;
/* gnutls_cipher_encrypt2() failed with GNUTLS_E_SHORT_MEMORY_BUFFER, which the GnuTLS
* backend reported as STATUS_INTERNAL_ERROR. */
if (params->output_len < params->input_len) return STATUS_INTERNAL_ERROR;
if ((status = init_cipher_ctx( params->key, TRUE ))) return status;
s = key_data(params->key)->s;
if (params->input_len > 0)
{
if (!params->input || !params->output) return STATUS_INVALID_PARAMETER;
if (!pEVP_EncryptUpdate( s->cipher, params->output, &len, params->input, params->input_len ))
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
}
return STATUS_SUCCESS;
}
static NTSTATUS key_symmetric_decrypt( void *args )
{
const struct key_symmetric_decrypt_params *params = args;
struct key_symmetric_openssl *s;
NTSTATUS status;
int len = 0;
if (params->input_len > INT_MAX || params->output_len > INT_MAX) return STATUS_INVALID_PARAMETER;
if (params->output_len < params->input_len) return STATUS_INTERNAL_ERROR;
if ((status = init_cipher_ctx( params->key, FALSE ))) return status;
s = key_data(params->key)->s;
if (params->input_len > 0)
{
if (!params->input || !params->output) return STATUS_INVALID_PARAMETER;
if (!pEVP_DecryptUpdate( s->cipher, params->output, &len, params->input, params->input_len ))
{
pOPENSSL_cleanse( params->output, params->input_len );
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
/* Feed the recovered plaintext straight into the shadow context so its GHASH
* state tracks ours; nothing has to be buffered. */
if (s->tag_cipher && len > 0)
{
unsigned char scratch[1024];
int offset = 0, outlen;
while (offset < len)
{
int chunk = min( len - offset, (int)sizeof(scratch) );
if (!pEVP_EncryptUpdate( s->tag_cipher, scratch, &outlen, params->output + offset, chunk ))
{
pOPENSSL_cleanse( scratch, sizeof(scratch) );
pOPENSSL_cleanse( params->output, params->input_len );
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
offset += chunk;
}
pOPENSSL_cleanse( scratch, sizeof(scratch) );
}
}
return STATUS_SUCCESS;
}
static NTSTATUS key_symmetric_get_tag( void *args )
{
const struct key_symmetric_get_tag_params *params = args;
struct key_symmetric_openssl *s;
unsigned char final[EVP_MAX_BLOCK_LENGTH];
EVP_CIPHER_CTX *ctx;
NTSTATUS status;
int len = 0;
if (!params->tag) return STATUS_INVALID_PARAMETER;
if (params->len > INT_MAX) return STATUS_INVALID_PARAMETER;
if (params->key->u.s.mode != CHAIN_MODE_GCM) return STATUS_INVALID_PARAMETER;
if (!key_data(params->key)->s && (status = init_cipher_ctx( params->key, TRUE ))) return status;
s = key_data(params->key)->s;
if (!s) return STATUS_INTERNAL_ERROR;
ctx = s->is_decrypt ? s->tag_cipher : s->cipher;
if (!ctx) return STATUS_INTERNAL_ERROR;
if (!pEVP_EncryptFinal_ex( ctx, final, &len ))
{
pOPENSSL_cleanse( final, sizeof(final) );
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
pOPENSSL_cleanse( final, sizeof(final) );
if (!pEVP_CIPHER_CTX_ctrl( ctx, EVP_CTRL_GCM_GET_TAG, params->len, params->tag ))
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
/* gnutls_cipher_tag() left the handle untouched; the caller invalidates it through
* key_symmetric_vector_reset() before the next operation. */
return STATUS_SUCCESS;
}
static NTSTATUS key_symmetric_destroy( void *args )
{
return key_symmetric_vector_reset( args );
}
/* Counterpart of gnutls.c's export_gnutls_datum(). It deliberately does not
* reproduce that function's behaviour when the value is longer than the
* destination: gnutls.c computes `offset = buflen - size`, which underflows and
* either trips its assert() or copies out of bounds. Return
* STATUS_INVALID_PARAMETER instead. */
static NTSTATUS export_bn( UCHAR *dst, ULONG buflen, const BIGNUM *bn, BOOL zero_pad, ULONG *written )
{
int bytes;
if (!bn)
{
if (zero_pad)
{
if (dst) memset( dst, 0, buflen );
if (written) *written = buflen;
}
else
{
if (written) *written = 0;
}
return STATUS_SUCCESS;
}
bytes = bn_num_bytes( bn );
if (bytes <= 0)
{
if (zero_pad)
{
if (dst) memset( dst, 0, buflen );
if (written) *written = buflen;
}
else
{
if (written) *written = 0;
}
return STATUS_SUCCESS;
}
if (zero_pad)
{
if (dst)
{
if ((ULONG)bytes <= buflen)
{
if ((ULONG)bytes < buflen)
memset( dst, 0, buflen - bytes );
pBN_bn2bin( bn, dst + (buflen - bytes) );
}
else
{
UCHAR stack_buf[512];
UCHAR *tmp = stack_buf;
ULONG i;
if ((ULONG)bytes > sizeof(stack_buf))
{
if (!(tmp = malloc( bytes )))
return STATUS_NO_MEMORY;
}
pBN_bn2bin( bn, tmp );
for (i = 0; i < (ULONG)bytes - buflen; i++)
{
if (tmp[i] != 0)
{
pOPENSSL_cleanse( tmp, bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_INVALID_PARAMETER;
}
}
memcpy( dst, tmp + (bytes - buflen), buflen );
pOPENSSL_cleanse( tmp, bytes );
if (tmp != stack_buf) free( tmp );
}
}
if (written) *written = buflen;
return STATUS_SUCCESS;
}
if (dst)
{
if ((ULONG)bytes > buflen) return STATUS_BUFFER_TOO_SMALL;
pBN_bn2bin( bn, dst );
}
if (written) *written = (ULONG)bytes;
return STATUS_SUCCESS;
}
/* The key_export_* helpers below deliberately diverge from the GnuTLS backend:
* GnuTLS only reports the required size and silently returns STATUS_SUCCESS when
* the caller supplies a buffer that is too small, whereas these return
* STATUS_BUFFER_TOO_SMALL. The latter matches Windows, and bcrypt_main.c relies
* on it, so the divergence is intentional. */
static NTSTATUS key_export_rsa_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf;
RSA *rsa = key_data(key)->a.key ? key_data(key)->a.key : key_data(key)->a.pubkey;
const BIGNUM *n, *e, *d;
ULONG size = key->u.a.bitlen / 8;
UCHAR *dst;
if (!rsa) return STATUS_INVALID_PARAMETER;
pRSA_get0_key( rsa, &n, &e, &d );
*ret_len = sizeof(*rsa_blob) + bn_num_bytes( e ) + size;
if (buf && len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (len >= *ret_len && buf)
{
ULONG exp_len, mod_len;
NTSTATUS status;
dst = (UCHAR *)(rsa_blob + 1);
if ((status = export_bn( dst, bn_num_bytes( e ), e, FALSE, &exp_len ))) return status;
rsa_blob->cbPublicExp = exp_len;
dst += rsa_blob->cbPublicExp;
if ((status = export_bn( dst, size, n, TRUE, &mod_len ))) return status;
rsa_blob->cbModulus = mod_len;
rsa_blob->Magic = BCRYPT_RSAPUBLIC_MAGIC;
rsa_blob->BitLength = key->u.a.bitlen;
rsa_blob->cbPrime1 = 0;
rsa_blob->cbPrime2 = 0;
}
return STATUS_SUCCESS;
}
static BOOL is_x25519_key( const struct key *key )
{
return key->alg_id == ALG_ID_ECDH && key->u.a.curve_id == ECC_CURVE_25519;
}
static int get_ecc_curve_nid( const struct key *key, ULONG *size_ret )
{
switch (key->alg_id)
{
case ALG_ID_ECDH:
case ALG_ID_ECDSA:
switch (key->u.a.curve_id)
{
case ECC_CURVE_P256R1:
if (size_ret) *size_ret = 32;
return NID_X9_62_prime256v1;
case ECC_CURVE_P384R1:
if (size_ret) *size_ret = 48;
return NID_secp384r1;
case ECC_CURVE_P521R1:
if (size_ret) *size_ret = 66;
return NID_secp521r1;
default:
return NID_undef;
}
case ALG_ID_ECDH_P256:
case ALG_ID_ECDSA_P256:
if (size_ret) *size_ret = 32;
return NID_X9_62_prime256v1;
case ALG_ID_ECDH_P384:
case ALG_ID_ECDSA_P384:
if (size_ret) *size_ret = 48;
return NID_secp384r1;
case ALG_ID_ECDH_P521:
case ALG_ID_ECDSA_P521:
if (size_ret) *size_ret = 66;
return NID_secp521r1;
default:
return NID_undef;
}
}
static NTSTATUS key_export_ecc_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf;
EC_KEY *ec_key = key_data(key)->a.key ? key_data(key)->a.key : key_data(key)->a.pubkey;
const EC_POINT *point;
const EC_GROUP *group;
BIGNUM *x, *y;
DWORD magic, size;
UCHAR *dst;
int curve;
if (is_x25519_key( key ))
{
EVP_PKEY *pkey = key_data(key)->a.key ? key_data(key)->a.key : key_data(key)->a.pubkey;
if (!pkey) return STATUS_INVALID_PARAMETER;
size = 32;
magic = BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC;
*ret_len = sizeof(*ecc_blob) + size * 2;
if (buf && len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (len >= *ret_len && buf)
{
size_t raw_len = size;
ecc_blob->dwMagic = magic;
ecc_blob->cbKey = size;
dst = (UCHAR *)(ecc_blob + 1);
if (pEVP_PKEY_get_raw_public_key( pkey, dst, &raw_len ) <= 0)
return STATUS_INTERNAL_ERROR;
memset( dst + size, 0, size );
}
return STATUS_SUCCESS;
}
switch (key->alg_id)
{
case ALG_ID_ECDH:
switch (key->u.a.curve_id)
{
case ECC_CURVE_P256R1: magic = BCRYPT_ECDH_PUBLIC_P256_MAGIC; size = 32; break;
case ECC_CURVE_P384R1: magic = BCRYPT_ECDH_PUBLIC_P384_MAGIC; size = 48; break;
case ECC_CURVE_P521R1: magic = BCRYPT_ECDH_PUBLIC_P521_MAGIC; size = 66; break;
default: return STATUS_NOT_IMPLEMENTED;
}
break;
case ALG_ID_ECDSA:
switch (key->u.a.curve_id)
{
case ECC_CURVE_P256R1: magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC; size = 32; break;
case ECC_CURVE_P384R1: magic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC; size = 48; break;
case ECC_CURVE_P521R1: magic = BCRYPT_ECDSA_PUBLIC_P521_MAGIC; size = 66; break;
default: return STATUS_NOT_IMPLEMENTED;
}
break;
case ALG_ID_ECDH_P256: magic = BCRYPT_ECDH_PUBLIC_P256_MAGIC; size = 32; break;
case ALG_ID_ECDH_P384: magic = BCRYPT_ECDH_PUBLIC_P384_MAGIC; size = 48; break;
case ALG_ID_ECDH_P521: magic = BCRYPT_ECDH_PUBLIC_P521_MAGIC; size = 66; break;
case ALG_ID_ECDSA_P256: magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC; size = 32; break;
case ALG_ID_ECDSA_P384: magic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC; size = 48; break;
case ALG_ID_ECDSA_P521: magic = BCRYPT_ECDSA_PUBLIC_P521_MAGIC; size = 66; break;
default:
FIXME( "algorithm %u not supported\n", key->alg_id );
return STATUS_NOT_IMPLEMENTED;
}
if (!ec_key || !(point = pEC_KEY_get0_public_key( ec_key )) ||
!(group = pEC_KEY_get0_group( ec_key )))
return STATUS_INVALID_PARAMETER;
curve = pEC_GROUP_get_curve_name( group );
if (curve != NID_X9_62_prime256v1 && curve != NID_secp384r1 && curve != NID_secp521r1)
{
FIXME( "curve %d not supported\n", curve );
return STATUS_NOT_IMPLEMENTED;
}
x = pBN_new();
y = pBN_new();
if (!x || !y)
{
if (x) pBN_free( x );
if (y) pBN_free( y );
return STATUS_NO_MEMORY;
}
if (!pEC_POINT_get_affine_coordinates_GFp( group, point, x, y, NULL ))
{
pBN_free( x ); pBN_free( y );
return STATUS_INTERNAL_ERROR;
}
*ret_len = sizeof(*ecc_blob) + size * 2;
if (buf && len < *ret_len)
{
pBN_free( x ); pBN_free( y );
return STATUS_BUFFER_TOO_SMALL;
}
if (len >= *ret_len && buf)
{
NTSTATUS status;
ecc_blob->dwMagic = magic;
ecc_blob->cbKey = size;
dst = (UCHAR *)(ecc_blob + 1);
if ((status = export_bn( dst, size, x, TRUE, NULL )))
{
pBN_free( x ); pBN_free( y );
return status;
}
dst += size;
if ((status = export_bn( dst, size, y, TRUE, NULL )))
{
pBN_free( x ); pBN_free( y );
return status;
}
}
pBN_free( x ); pBN_free( y );
return STATUS_SUCCESS;
}
static NTSTATUS key_export_dsa_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BCRYPT_DSA_KEY_BLOB *dsa_blob = (BCRYPT_DSA_KEY_BLOB *)buf;
DSA *dsa = key_data(key)->a.key ? key_data(key)->a.key : key_data(key)->a.pubkey;
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
ULONG size = key->u.a.bitlen / 8;
UCHAR *dst;
if (key->u.a.bitlen > 1024)
{
FIXME( "bitlen > 1024 not supported\n" );
return STATUS_NOT_IMPLEMENTED;
}
if (!dsa) return STATUS_INVALID_PARAMETER;
pDSA_get0_pqg( dsa, &p, &q, &g );
pDSA_get0_key( dsa, &pub_key, &priv_key );
/* Reject before *ret_len is published, so the size query and the fill call
* agree. gnutls.c performs the same check at the same point. */
if (!q || (size_t)bn_num_bytes( q ) > sizeof(dsa_blob->q)) return STATUS_INVALID_PARAMETER;
*ret_len = sizeof(*dsa_blob) + size * 3;
if (buf && len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (len >= *ret_len && buf)
{
NTSTATUS status;
dst = (UCHAR *)(dsa_blob + 1);
if ((status = export_bn( dst, size, p, TRUE, NULL ))) return status;
dst += size;
if ((status = export_bn( dst, size, g, TRUE, NULL ))) return status;
dst += size;
if ((status = export_bn( dst, size, pub_key, TRUE, NULL ))) return status;
if ((status = export_bn( dsa_blob->q, sizeof(dsa_blob->q), q, TRUE, NULL ))) return status;
dsa_blob->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC;
dsa_blob->cbKey = size;
memset( dsa_blob->Count, 0, sizeof(dsa_blob->Count) );
memset( dsa_blob->Seed, 0, sizeof(dsa_blob->Seed) );
}
return STATUS_SUCCESS;
}
static void reverse_bytes( UCHAR *buf, ULONG len )
{
unsigned int i;
UCHAR tmp;
for (i = 0; i < len / 2; ++i)
{
tmp = buf[i];
buf[i] = buf[len - i - 1];
buf[len - i - 1] = tmp;
}
}
#define Q_SIZE 20
static NTSTATUS key_export_dsa_capi_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BLOBHEADER *hdr = (BLOBHEADER *)buf;
DSSPUBKEY *dsskey;
DSA *dsa = key_data(key)->a.key ? key_data(key)->a.key : key_data(key)->a.pubkey;
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
ULONG size = key->u.a.bitlen / 8;
UCHAR *dst;
if (key->u.a.bitlen > 1024)
{
FIXME( "bitlen > 1024 not supported\n" );
return STATUS_NOT_IMPLEMENTED;
}
if (!dsa) return STATUS_INVALID_PARAMETER;
pDSA_get0_pqg( dsa, &p, &q, &g );
pDSA_get0_key( dsa, &pub_key, &priv_key );
/* Reject before *ret_len is published, matching gnutls.c. */
if (!q || bn_num_bytes( q ) > Q_SIZE) return STATUS_INVALID_PARAMETER;
*ret_len = sizeof(*hdr) + sizeof(*dsskey) + sizeof(key->u.a.dss_seed) + size * 3 + Q_SIZE;
if (buf && len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (len >= *ret_len && buf)
{
NTSTATUS status;
hdr->bType = PUBLICKEYBLOB;
hdr->bVersion = 2;
hdr->reserved = 0;
hdr->aiKeyAlg = CALG_DSS_SIGN;
dsskey = (DSSPUBKEY *)(hdr + 1);
dsskey->magic = MAGIC_DSS1;
dsskey->bitlen = key->u.a.bitlen;
dst = (UCHAR *)(dsskey + 1);
if ((status = export_bn( dst, size, p, TRUE, NULL ))) return status;
reverse_bytes( dst, size );
dst += size;
if ((status = export_bn( dst, Q_SIZE, q, TRUE, NULL ))) return status;
reverse_bytes( dst, Q_SIZE );
dst += Q_SIZE;
if ((status = export_bn( dst, size, g, TRUE, NULL ))) return status;
reverse_bytes( dst, size );
dst += size;
if ((status = export_bn( dst, size, pub_key, TRUE, NULL ))) return status;
reverse_bytes( dst, size );
dst += size;
memcpy( dst, &key->u.a.dss_seed, sizeof(key->u.a.dss_seed) );
}
return STATUS_SUCCESS;
}
static NTSTATUS key_asymmetric_generate( void *args )
{
struct key *key = args;
/* Gating the two key-creation entry points is sufficient: every other
* asymmetric operation acts on a key that had to be generated or imported
* first, so an unavailable family is unreachable beyond here. */
if (!alg_family_available( key )) return STATUS_NOT_SUPPORTED;
switch (key->alg_id)
{
case ALG_ID_RSA:
case ALG_ID_RSA_SIGN:
{
RSA *rsa = pRSA_new();
BIGNUM *e = pBN_new();
if (!rsa || !e)
{
if (e) pBN_free( e );
if (rsa) pRSA_free( rsa );
return STATUS_NO_MEMORY;
}
if (!pBN_set_word( e, 65537 ) || !pRSA_generate_key_ex( rsa, key->u.a.bitlen, e, NULL ))
{
pERR_clear_error();
pBN_free( e ); pRSA_free( rsa );
return STATUS_INTERNAL_ERROR;
}
pBN_free( e );
if (key_data(key)->a.key) pRSA_free( key_data(key)->a.key );
key_data(key)->a.key = rsa;
break;
}
case ALG_ID_DH:
{
DH *dh = pDH_new();
if (!dh) return STATUS_NO_MEMORY;
if (key_data(key)->a.pubkey)
{
const BIGNUM *p, *q, *g;
BIGNUM *cp, *cq, *cg;
pDH_get0_pqg( key_data(key)->a.pubkey, &p, &q, &g );
cp = p ? pBN_dup( p ) : NULL;
cq = q ? pBN_dup( q ) : NULL;
cg = g ? pBN_dup( g ) : NULL;
if ((p && !cp) || (q && !cq) || (g && !cg) || !pDH_set0_pqg( dh, cp, cq, cg ))
{
pERR_clear_error();
if (cp) pBN_free( cp );
if (cq) pBN_free( cq );
if (cg) pBN_free( cg );
pDH_free( dh );
return STATUS_NO_MEMORY;
}
pDH_free( key_data(key)->a.pubkey );
key_data(key)->a.pubkey = NULL;
}
/* gnutls.c passed key->u.a.bitlen straight to gnutls_privkey_generate(),
* which honours it, so a substituted group must never be weaker than the
* caller asked for. The RFC 5114 groups below have p of exactly 2048 and
* 1024 bits respectively, so they are used only on an exact match; any
* other size is generated, which is slow but correct. */
else if (key->u.a.bitlen == 2048)
{
DH *named = pDH_get_2048_256 ? pDH_get_2048_256() : NULL;
if (named)
{
const BIGNUM *p, *q, *g;
BIGNUM *cp, *cq, *cg;
pDH_get0_pqg( named, &p, &q, &g );
cp = p ? pBN_dup( p ) : NULL;
cq = q ? pBN_dup( q ) : NULL;
cg = g ? pBN_dup( g ) : NULL;
if ((p && !cp) || (q && !cq) || (g && !cg) || !pDH_set0_pqg( dh, cp, cq, cg ))
{
pERR_clear_error();
if (cp) pBN_free( cp );
if (cq) pBN_free( cq );
if (cg) pBN_free( cg );
pDH_free( named );
pDH_free( dh );
return STATUS_NO_MEMORY;
}
pDH_free( named );
}
else
{
pDH_free( dh );
return STATUS_INTERNAL_ERROR;
}
}
else if (key->u.a.bitlen == 1024)
{
DH *named = pDH_get_1024_160 ? pDH_get_1024_160() : NULL;
if (named)
{
const BIGNUM *p, *q, *g;
BIGNUM *cp, *cq, *cg;
pDH_get0_pqg( named, &p, &q, &g );
cp = p ? pBN_dup( p ) : NULL;
cq = q ? pBN_dup( q ) : NULL;
cg = g ? pBN_dup( g ) : NULL;
if ((p && !cp) || (q && !cq) || (g && !cg) || !pDH_set0_pqg( dh, cp, cq, cg ))
{
pERR_clear_error();
if (cp) pBN_free( cp );
if (cq) pBN_free( cq );
if (cg) pBN_free( cg );
pDH_free( named );
pDH_free( dh );
return STATUS_NO_MEMORY;
}
pDH_free( named );
}
else
{
pDH_free( dh );
return STATUS_INTERNAL_ERROR;
}
}
else
{
if (!pDH_generate_parameters_ex( dh, key->u.a.bitlen, 2, NULL ))
{
pERR_clear_error();
pDH_free( dh );
return STATUS_INTERNAL_ERROR;
}
}
if (!pDH_generate_key( dh ))
{
pERR_clear_error();
pDH_free( dh );
return STATUS_INTERNAL_ERROR;
}
if (key_data(key)->a.key) pDH_free( key_data(key)->a.key );
key_data(key)->a.key = dh;
break;
}
case ALG_ID_DSA:
{
DSA *dsa = pDSA_new();
if (!dsa) return STATUS_NO_MEMORY;
if (!pDSA_generate_parameters_ex( dsa, key->u.a.bitlen, NULL, 0, NULL, NULL, NULL ) ||
!pDSA_generate_key( dsa ))
{
pERR_clear_error();
pDSA_free( dsa );
return STATUS_INTERNAL_ERROR;
}
if (key_data(key)->a.key) pDSA_free( key_data(key)->a.key );
key_data(key)->a.key = dsa;
break;
}
case ALG_ID_ECDH:
if (key->u.a.curve_id == ECC_CURVE_25519)
{
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey = NULL;
if (!(ctx = pEVP_PKEY_CTX_new_id( EVP_PKEY_X25519, NULL )))
return STATUS_NO_MEMORY;
if (pEVP_PKEY_keygen_init( ctx ) <= 0 || pEVP_PKEY_keygen( ctx, &pkey ) <= 0)
{
pERR_clear_error();
pEVP_PKEY_CTX_free( ctx );
return STATUS_INTERNAL_ERROR;
}
pEVP_PKEY_CTX_free( ctx );
if (key_data(key)->a.key) pEVP_PKEY_free( key_data(key)->a.key );
key_data(key)->a.key = pkey;
break;
}
/* fall through */
case ALG_ID_ECDSA:
case ALG_ID_ECDH_P256:
case ALG_ID_ECDSA_P256:
case ALG_ID_ECDH_P384:
case ALG_ID_ECDSA_P384:
case ALG_ID_ECDH_P521:
case ALG_ID_ECDSA_P521:
{
EC_KEY *ec;
int nid = get_ecc_curve_nid( key, NULL );
if (nid == NID_undef) return STATUS_INVALID_PARAMETER;
if (!(ec = pEC_KEY_new_by_curve_name( nid )))
return STATUS_NO_MEMORY;
if (!pEC_KEY_generate_key( ec ))
{
pERR_clear_error();
pEC_KEY_free( ec );
return STATUS_INTERNAL_ERROR;
}
if (key_data(key)->a.key) pEC_KEY_free( key_data(key)->a.key );
key_data(key)->a.key = ec;
break;
}
default:
FIXME( "algorithm %u not yet supported\n", key->alg_id );
return STATUS_NOT_IMPLEMENTED;
}
return STATUS_SUCCESS;
}
static NTSTATUS key_export_ecc( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf;
EC_KEY *ec_key = key_data(key)->a.key;
const EC_POINT *point;
const EC_GROUP *group;
const BIGNUM *d;
BIGNUM *x, *y;
DWORD magic, size;
UCHAR *dst;
if (is_x25519_key( key ))
{
EVP_PKEY *pkey = key_data(key)->a.key;
if (!pkey) return STATUS_INVALID_PARAMETER;
size = 32;
magic = BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC;
*ret_len = sizeof(*ecc_blob) + size * 3;
if (buf && len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (len >= *ret_len && buf)
{
size_t raw_len = size;
ecc_blob->dwMagic = magic;
ecc_blob->cbKey = size;
dst = (UCHAR *)(ecc_blob + 1);
if (pEVP_PKEY_get_raw_public_key( pkey, dst, &raw_len ) <= 0)
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
memset( dst + size, 0, size );
raw_len = size;
if (pEVP_PKEY_get_raw_private_key( pkey, dst + 2 * size, &raw_len ) <= 0)
{
pOPENSSL_cleanse( dst, size * 3 );
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
}
return STATUS_SUCCESS;
}
switch (key->alg_id)
{
case ALG_ID_ECDH:
switch (key->u.a.curve_id)
{
case ECC_CURVE_P256R1: magic = BCRYPT_ECDH_PRIVATE_P256_MAGIC; size = 32; break;
case ECC_CURVE_P384R1: magic = BCRYPT_ECDH_PRIVATE_P384_MAGIC; size = 48; break;
case ECC_CURVE_P521R1: magic = BCRYPT_ECDH_PRIVATE_P521_MAGIC; size = 66; break;
default: return STATUS_NOT_IMPLEMENTED;
}
break;
case ALG_ID_ECDSA:
switch (key->u.a.curve_id)
{
case ECC_CURVE_P256R1: magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC; size = 32; break;
case ECC_CURVE_P384R1: magic = BCRYPT_ECDSA_PRIVATE_P384_MAGIC; size = 48; break;
case ECC_CURVE_P521R1: magic = BCRYPT_ECDSA_PRIVATE_P521_MAGIC; size = 66; break;
default: return STATUS_NOT_IMPLEMENTED;
}
break;
case ALG_ID_ECDH_P256: magic = BCRYPT_ECDH_PRIVATE_P256_MAGIC; size = 32; break;
case ALG_ID_ECDH_P384: magic = BCRYPT_ECDH_PRIVATE_P384_MAGIC; size = 48; break;
case ALG_ID_ECDH_P521: magic = BCRYPT_ECDH_PRIVATE_P521_MAGIC; size = 66; break;
case ALG_ID_ECDSA_P256: magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC; size = 32; break;
case ALG_ID_ECDSA_P384: magic = BCRYPT_ECDSA_PRIVATE_P384_MAGIC; size = 48; break;
case ALG_ID_ECDSA_P521: magic = BCRYPT_ECDSA_PRIVATE_P521_MAGIC; size = 66; break;
default:
FIXME( "algorithm %u not yet supported\n", key->alg_id );
return STATUS_NOT_IMPLEMENTED;
}
if (!ec_key || !(d = pEC_KEY_get0_private_key( ec_key )) ||
!(point = pEC_KEY_get0_public_key( ec_key )) ||
!(group = pEC_KEY_get0_group( ec_key )))
return STATUS_INVALID_PARAMETER;
x = pBN_new();
y = pBN_new();
if (!x || !y)
{
if (x) pBN_free( x );
if (y) pBN_free( y );
return STATUS_NO_MEMORY;
}
if (!pEC_POINT_get_affine_coordinates_GFp( group, point, x, y, NULL ))
{
pERR_clear_error();
pBN_free( x ); pBN_free( y );
return STATUS_INTERNAL_ERROR;
}
*ret_len = sizeof(*ecc_blob) + size * 3;
if (buf && len < *ret_len)
{
pBN_free( x ); pBN_free( y );
return STATUS_BUFFER_TOO_SMALL;
}
if (len >= *ret_len && buf)
{
NTSTATUS status;
ecc_blob->dwMagic = magic;
ecc_blob->cbKey = size;
dst = (UCHAR *)(ecc_blob + 1);
if ((status = export_bn( dst, size, x, TRUE, NULL )))
{
pBN_free( x ); pBN_free( y );
return status;
}
dst += size;
if ((status = export_bn( dst, size, y, TRUE, NULL )))
{
pBN_free( x ); pBN_free( y );
return status;
}
dst += size;
if ((status = export_bn( dst, size, d, TRUE, NULL )))
{
pBN_free( x ); pBN_free( y );
return status;
}
}
pBN_free( x ); pBN_free( y );
return STATUS_SUCCESS;
}
static NTSTATUS key_import_ecc( struct key *key, ULONG flags, UCHAR *buf, ULONG len )
{
BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf;
enum ecc_curve_id target_curve = ECC_CURVE_NONE;
ULONG target_bitlen = 0;
EC_KEY *ec_key;
BIGNUM *x, *y, *d = NULL;
UCHAR *src;
int nid;
if (!buf || len < sizeof(*ecc_blob)) return STATUS_INVALID_PARAMETER;
if (ecc_blob->dwMagic == BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC)
{
EVP_PKEY *pkey;
const UCHAR *d_ptr;
if (key->alg_id != ALG_ID_ECDH ||
(key->u.a.curve_id != ECC_CURVE_NONE && key->u.a.curve_id != ECC_CURVE_25519))
return STATUS_NOT_SUPPORTED;
if (ecc_blob->cbKey != 32 || (ULONG64)len < sizeof(*ecc_blob) + 3ULL * 32) return STATUS_INVALID_PARAMETER;
d_ptr = (const UCHAR *)(ecc_blob + 1) + 2 * 32;
if (!(pkey = pEVP_PKEY_new_raw_private_key( EVP_PKEY_X25519, NULL, d_ptr, 32 )))
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
if (key_data(key)->a.key)
{
if (is_x25519_key( key )) pEVP_PKEY_free( key_data(key)->a.key );
else pEC_KEY_free( key_data(key)->a.key );
}
key_data(key)->a.key = pkey;
key->u.a.curve_id = ECC_CURVE_25519;
key->u.a.bitlen = 256;
return STATUS_SUCCESS;
}
switch (ecc_blob->dwMagic)
{
case BCRYPT_ECDH_PRIVATE_P256_MAGIC:
if (key->alg_id != ALG_ID_ECDH_P256 && !(key->alg_id == ALG_ID_ECDH && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P256R1))) return STATUS_NOT_SUPPORTED;
target_curve = ECC_CURVE_P256R1;
target_bitlen = 256;
nid = NID_X9_62_prime256v1;
if (ecc_blob->cbKey != 32 || (ULONG64)len < sizeof(*ecc_blob) + 3ULL * ecc_blob->cbKey)
return STATUS_INVALID_PARAMETER;
break;
case BCRYPT_ECDSA_PRIVATE_P256_MAGIC:
if (key->alg_id != ALG_ID_ECDSA_P256 && !(key->alg_id == ALG_ID_ECDSA && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P256R1))) return STATUS_NOT_SUPPORTED;
target_curve = ECC_CURVE_P256R1;
target_bitlen = 256;
nid = NID_X9_62_prime256v1;
if (ecc_blob->cbKey != 32 || (ULONG64)len < sizeof(*ecc_blob) + 3ULL * ecc_blob->cbKey)
return STATUS_INVALID_PARAMETER;
break;
case BCRYPT_ECDH_PRIVATE_P384_MAGIC:
if (key->alg_id != ALG_ID_ECDH_P384 && !(key->alg_id == ALG_ID_ECDH && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P384R1))) return STATUS_NOT_SUPPORTED;
target_curve = ECC_CURVE_P384R1;
target_bitlen = 384;
nid = NID_secp384r1;
if (ecc_blob->cbKey != 48 || (ULONG64)len < sizeof(*ecc_blob) + 3ULL * ecc_blob->cbKey)
return STATUS_INVALID_PARAMETER;
break;
case BCRYPT_ECDSA_PRIVATE_P384_MAGIC:
if (key->alg_id != ALG_ID_ECDSA_P384 && !(key->alg_id == ALG_ID_ECDSA && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P384R1))) return STATUS_NOT_SUPPORTED;
target_curve = ECC_CURVE_P384R1;
target_bitlen = 384;
nid = NID_secp384r1;
if (ecc_blob->cbKey != 48 || (ULONG64)len < sizeof(*ecc_blob) + 3ULL * ecc_blob->cbKey)
return STATUS_INVALID_PARAMETER;
break;
case BCRYPT_ECDH_PRIVATE_P521_MAGIC:
if (key->alg_id != ALG_ID_ECDH_P521 && !(key->alg_id == ALG_ID_ECDH && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P521R1))) return STATUS_NOT_SUPPORTED;
target_curve = ECC_CURVE_P521R1;
target_bitlen = 521;
nid = NID_secp521r1;
if (ecc_blob->cbKey != 66 || (ULONG64)len < sizeof(*ecc_blob) + 3ULL * ecc_blob->cbKey)
return STATUS_INVALID_PARAMETER;
break;
case BCRYPT_ECDSA_PRIVATE_P521_MAGIC:
if (key->alg_id != ALG_ID_ECDSA_P521 && !(key->alg_id == ALG_ID_ECDSA && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P521R1))) return STATUS_NOT_SUPPORTED;
target_curve = ECC_CURVE_P521R1;
target_bitlen = 521;
nid = NID_secp521r1;
if (ecc_blob->cbKey != 66 || (ULONG64)len < sizeof(*ecc_blob) + 3ULL * ecc_blob->cbKey)
return STATUS_INVALID_PARAMETER;
break;
default:
return STATUS_NOT_SUPPORTED;
}
src = (UCHAR *)(ecc_blob + 1);
x = pBN_bin2bn( src, ecc_blob->cbKey, NULL );
src += ecc_blob->cbKey;
y = pBN_bin2bn( src, ecc_blob->cbKey, NULL );
src += ecc_blob->cbKey;
if (ecc_blob->dwMagic == BCRYPT_ECDH_PRIVATE_P256_MAGIC ||
ecc_blob->dwMagic == BCRYPT_ECDH_PRIVATE_P384_MAGIC ||
ecc_blob->dwMagic == BCRYPT_ECDH_PRIVATE_P521_MAGIC ||
ecc_blob->dwMagic == BCRYPT_ECDSA_PRIVATE_P256_MAGIC ||
ecc_blob->dwMagic == BCRYPT_ECDSA_PRIVATE_P384_MAGIC ||
ecc_blob->dwMagic == BCRYPT_ECDSA_PRIVATE_P521_MAGIC)
{
d = pBN_bin2bn( src, ecc_blob->cbKey, NULL );
if (!d)
{
if (x) pBN_free( x );
if (y) pBN_free( y );
return STATUS_NO_MEMORY;
}
}
if (!x || !y)
{
if (x) pBN_free( x );
if (y) pBN_free( y );
if (d) ossl_bn_clear_free( d );
return STATUS_NO_MEMORY;
}
ec_key = pEC_KEY_new_by_curve_name( nid );
if (!ec_key)
{
pERR_clear_error();
pBN_free( x ); pBN_free( y ); if (d) ossl_bn_clear_free( d );
return STATUS_NO_MEMORY;
}
if (!pEC_KEY_set_public_key_affine_coordinates( ec_key, x, y ))
{
pERR_clear_error();
pBN_free( x ); pBN_free( y ); if (d) ossl_bn_clear_free( d );
pEC_KEY_free( ec_key );
return STATUS_INVALID_PARAMETER;
}
pBN_free( x ); pBN_free( y );
if (d)
{
if (!pEC_KEY_set_private_key( ec_key, d ) || !pEC_KEY_check_key( ec_key ))
{
pERR_clear_error();
ossl_bn_clear_free( d );
pEC_KEY_free( ec_key );
return STATUS_INVALID_PARAMETER;
}
ossl_bn_clear_free( d );
}
else if (!pEC_KEY_check_key( ec_key ))
{
pERR_clear_error();
pEC_KEY_free( ec_key );
return STATUS_INVALID_PARAMETER;
}
if (key_data(key)->a.key)
{
if (is_x25519_key( key )) pEVP_PKEY_free( key_data(key)->a.key );
else pEC_KEY_free( key_data(key)->a.key );
}
key->u.a.curve_id = target_curve;
key->u.a.bitlen = target_bitlen;
key_data(key)->a.key = ec_key;
return STATUS_SUCCESS;
}
static NTSTATUS key_export_rsa( struct key *key, ULONG flags, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf;
RSA *rsa = key_data(key)->a.key;
const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
ULONG size = key->u.a.bitlen / 8;
BOOL full = !!(flags & KEY_EXPORT_FLAG_RSA_FULL);
UCHAR *dst;
if (!rsa) return STATUS_INVALID_PARAMETER;
pRSA_get0_key( rsa, &n, &e, &d );
pRSA_get0_factors( rsa, &p, &q );
pRSA_get0_crt_params( rsa, &dmp1, &dmq1, &iqmp );
if (!n || !e || !d || !p || !q) return STATUS_INVALID_PARAMETER;
if (full && (!dmp1 || !dmq1 || !iqmp)) return STATUS_INVALID_PARAMETER;
*ret_len = sizeof(*rsa_blob) + bn_num_bytes( e ) + size + (size / 2) + (size / 2);
if (full) *ret_len += (size / 2) + (size / 2) + (size / 2) + size;
if (buf && len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (len >= *ret_len && buf)
{
ULONG exp_len, mod_len, p1_len, p2_len;
NTSTATUS status;
dst = (UCHAR *)(rsa_blob + 1);
if ((status = export_bn( dst, bn_num_bytes( e ), e, FALSE, &exp_len ))) return status;
rsa_blob->cbPublicExp = exp_len;
dst += rsa_blob->cbPublicExp;
if ((status = export_bn( dst, size, n, TRUE, &mod_len ))) return status;
rsa_blob->cbModulus = mod_len;
dst += rsa_blob->cbModulus;
if ((status = export_bn( dst, size / 2, p, TRUE, &p1_len ))) return status;
rsa_blob->cbPrime1 = p1_len;
dst += rsa_blob->cbPrime1;
if ((status = export_bn( dst, size / 2, q, TRUE, &p2_len ))) return status;
rsa_blob->cbPrime2 = p2_len;
dst += rsa_blob->cbPrime2;
if (full)
{
if ((status = export_bn( dst, rsa_blob->cbPrime1, dmp1, TRUE, NULL ))) return status;
dst += rsa_blob->cbPrime1;
if ((status = export_bn( dst, rsa_blob->cbPrime2, dmq1, TRUE, NULL ))) return status;
dst += rsa_blob->cbPrime2;
if ((status = export_bn( dst, rsa_blob->cbPrime1, iqmp, TRUE, NULL ))) return status;
dst += rsa_blob->cbPrime1;
if ((status = export_bn( dst, rsa_blob->cbModulus, d, TRUE, NULL ))) return status;
rsa_blob->Magic = BCRYPT_RSAFULLPRIVATE_MAGIC;
}
else
{
rsa_blob->Magic = BCRYPT_RSAPRIVATE_MAGIC;
}
rsa_blob->BitLength = key->u.a.bitlen;
}
return STATUS_SUCCESS;
}
static NTSTATUS key_import_rsa( struct key *key, ULONG flags, UCHAR *buf, ULONG len )
{
BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf;
RSA *rsa = NULL;
BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL, *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
UCHAR *src;
NTSTATUS status = STATUS_NO_MEMORY;
if (!buf || len < sizeof(*rsa_blob)) return STATUS_INVALID_PARAMETER;
if (!rsa_blob->cbPublicExp || !rsa_blob->cbModulus ||
!rsa_blob->cbPrime1 || !rsa_blob->cbPrime2)
return STATUS_INVALID_PARAMETER;
if (rsa_blob->Magic == BCRYPT_RSAPRIVATE_MAGIC)
{
if ((ULONG64)len < (ULONG64)sizeof(*rsa_blob) + rsa_blob->cbPublicExp + (ULONG64)rsa_blob->cbModulus +
(ULONG64)rsa_blob->cbPrime1 + (ULONG64)rsa_blob->cbPrime2)
return STATUS_INVALID_PARAMETER;
}
else if (rsa_blob->Magic == BCRYPT_RSAFULLPRIVATE_MAGIC)
{
ULONG64 required = (ULONG64)sizeof(*rsa_blob) + rsa_blob->cbPublicExp + 2 * (ULONG64)rsa_blob->cbModulus +
3 * (ULONG64)rsa_blob->cbPrime1 + 2 * (ULONG64)rsa_blob->cbPrime2;
if ((ULONG64)len < required)
return STATUS_INVALID_PARAMETER;
}
else return STATUS_NOT_SUPPORTED;
pERR_clear_error();
src = (UCHAR *)(rsa_blob + 1);
e = pBN_bin2bn( src, rsa_blob->cbPublicExp, NULL );
src += rsa_blob->cbPublicExp;
n = pBN_bin2bn( src, rsa_blob->cbModulus, NULL );
src += rsa_blob->cbModulus;
if (!e || !n) goto fail;
if (rsa_blob->Magic == BCRYPT_RSAPRIVATE_MAGIC || rsa_blob->Magic == BCRYPT_RSAFULLPRIVATE_MAGIC)
{
p = pBN_bin2bn( src, rsa_blob->cbPrime1, NULL );
src += rsa_blob->cbPrime1;
q = pBN_bin2bn( src, rsa_blob->cbPrime2, NULL );
src += rsa_blob->cbPrime2;
if (!p || !q) { status = STATUS_INVALID_PARAMETER; goto fail; }
if (rsa_blob->Magic == BCRYPT_RSAFULLPRIVATE_MAGIC)
{
dmp1 = pBN_bin2bn( src, rsa_blob->cbPrime1, NULL ); src += rsa_blob->cbPrime1;
dmq1 = pBN_bin2bn( src, rsa_blob->cbPrime2, NULL ); src += rsa_blob->cbPrime2;
iqmp = pBN_bin2bn( src, rsa_blob->cbPrime1, NULL ); src += rsa_blob->cbPrime1;
d = pBN_bin2bn( src, rsa_blob->cbModulus, NULL ); src += rsa_blob->cbModulus;
if (!dmp1 || !dmq1 || !iqmp || !d) { status = STATUS_INVALID_PARAMETER; goto fail; }
}
else
{
BN_CTX *ctx = pBN_CTX_new();
BIGNUM *p1, *q1, *phi;
const BIGNUM *one = pBN_value_one();
if (!ctx) goto fail;
p1 = pBN_new();
q1 = pBN_new();
phi = pBN_new();
d = pBN_new();
dmp1 = pBN_new();
dmq1 = pBN_new();
iqmp = pBN_new();
if (!p1 || !q1 || !phi || !d || !dmp1 || !dmq1 || !iqmp)
{
if (p1) ossl_bn_clear_free( p1 );
if (q1) ossl_bn_clear_free( q1 );
if (phi) ossl_bn_clear_free( phi );
pBN_CTX_free( ctx );
goto fail;
}
if (!pBN_sub( p1, p, one ) ||
!pBN_sub( q1, q, one ) ||
!pBN_mul( phi, p1, q1, ctx ) ||
!pBN_mod_inverse( d, e, phi, ctx ) ||
!pBN_div( NULL, dmp1, d, p1, ctx ) ||
!pBN_div( NULL, dmq1, d, q1, ctx ) ||
!pBN_mod_inverse( iqmp, q, p, ctx ))
{
ossl_bn_clear_free( p1 );
ossl_bn_clear_free( q1 );
ossl_bn_clear_free( phi );
pBN_CTX_free( ctx );
status = STATUS_INVALID_PARAMETER;
goto fail;
}
ossl_bn_clear_free( p1 );
ossl_bn_clear_free( q1 );
ossl_bn_clear_free( phi );
pBN_CTX_free( ctx );
}
}
if (!d) { status = STATUS_INVALID_PARAMETER; goto fail; }
rsa = pRSA_new();
if (!rsa) goto fail;
if (!pRSA_set0_key( rsa, n, e, d )) goto fail;
n = e = d = NULL;
if (p && q)
{
if (!pRSA_set0_factors( rsa, p, q )) goto fail;
p = q = NULL;
}
if (dmp1 && dmq1 && iqmp)
{
if (!pRSA_set0_crt_params( rsa, dmp1, dmq1, iqmp )) goto fail;
dmp1 = dmq1 = iqmp = NULL;
}
key->u.a.bitlen = rsa_blob->BitLength;
if (key_data(key)->a.key) pRSA_free( key_data(key)->a.key );
key_data(key)->a.key = rsa;
return STATUS_SUCCESS;
fail:
pERR_clear_error();
if (rsa) pRSA_free( rsa );
if (n) pBN_free( n );
if (e) pBN_free( e );
if (d) ossl_bn_clear_free( d );
if (p) ossl_bn_clear_free( p );
if (q) ossl_bn_clear_free( q );
if (dmp1) ossl_bn_clear_free( dmp1 );
if (dmq1) ossl_bn_clear_free( dmq1 );
if (iqmp) ossl_bn_clear_free( iqmp );
return status;
}
static NTSTATUS key_export_dsa_capi( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BLOBHEADER *hdr = (BLOBHEADER *)buf;
DSSPUBKEY *dsskey;
DSA *dsa = key_data(key)->a.key;
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
ULONG size = key->u.a.bitlen / 8;
UCHAR *dst;
if (!dsa) return STATUS_INVALID_PARAMETER;
pDSA_get0_pqg( dsa, &p, &q, &g );
pDSA_get0_key( dsa, &pub_key, &priv_key );
if (!p || !q || !g || !priv_key) return STATUS_INVALID_PARAMETER;
/* Reject before *ret_len is published, matching gnutls.c, which likewise
* reports STATUS_NOT_SUPPORTED rather than STATUS_INVALID_PARAMETER here.
* gnutls compared against 21 because its datum may carry a leading zero
* byte; bn_num_bytes() already excludes that, so the bound is Q_SIZE. */
if (bn_num_bytes( q ) > Q_SIZE || bn_num_bytes( priv_key ) > Q_SIZE)
{
ERR( "can't export key in this format\n" );
return STATUS_NOT_SUPPORTED;
}
*ret_len = sizeof(*hdr) + sizeof(*dsskey) + sizeof(key->u.a.dss_seed) + size * 2 + Q_SIZE * 2;
if (buf && len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (len >= *ret_len && buf)
{
NTSTATUS status;
hdr->bType = PRIVATEKEYBLOB;
hdr->bVersion = 2;
hdr->reserved = 0;
hdr->aiKeyAlg = CALG_DSS_SIGN;
dsskey = (DSSPUBKEY *)(hdr + 1);
dsskey->magic = MAGIC_DSS2;
dsskey->bitlen = key->u.a.bitlen;
dst = (UCHAR *)(dsskey + 1);
if ((status = export_bn( dst, size, p, TRUE, NULL ))) return status;
reverse_bytes( dst, size );
dst += size;
if ((status = export_bn( dst, Q_SIZE, q, TRUE, NULL ))) return status;
reverse_bytes( dst, Q_SIZE );
dst += Q_SIZE;
if ((status = export_bn( dst, size, g, TRUE, NULL ))) return status;
reverse_bytes( dst, size );
dst += size;
if ((status = export_bn( dst, Q_SIZE, priv_key, TRUE, NULL ))) return status;
reverse_bytes( dst, Q_SIZE );
dst += Q_SIZE;
memcpy( dst, &key->u.a.dss_seed, sizeof(key->u.a.dss_seed) );
}
return STATUS_SUCCESS;
}
static NTSTATUS key_import_dsa_capi( struct key *key, UCHAR *buf, ULONG len )
{
BLOBHEADER *hdr = (BLOBHEADER *)buf;
DSSPUBKEY *pubkey;
DSA *dsa = NULL;
BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL, *priv_key = NULL;
BN_CTX *ctx;
UCHAR *src, p_data[128], q_data[20], g_data[128], x_data[20];
ULONG size, i;
if (!buf || len < sizeof(*hdr) + sizeof(*pubkey)) return STATUS_INVALID_PARAMETER;
if (hdr->bType != PRIVATEKEYBLOB) return STATUS_NOT_SUPPORTED;
pubkey = (DSSPUBKEY *)(hdr + 1);
if (pubkey->magic != MAGIC_DSS2) return STATUS_NOT_SUPPORTED;
size = pubkey->bitlen / 8;
if (!size || size > sizeof(p_data) || len < sizeof(*hdr) + sizeof(*pubkey) + size * 2 + 40 + sizeof(key->u.a.dss_seed))
return STATUS_INVALID_PARAMETER;
src = (UCHAR *)(pubkey + 1);
for (i = 0; i < size; i++) p_data[i] = src[size - i - 1];
src += size;
for (i = 0; i < 20; i++) q_data[i] = src[20 - i - 1];
src += 20;
for (i = 0; i < size; i++) g_data[i] = src[size - i - 1];
src += size;
for (i = 0; i < 20; i++) x_data[i] = src[20 - i - 1];
src += 20;
p = pBN_bin2bn( p_data, size, NULL );
q = pBN_bin2bn( q_data, 20, NULL );
g = pBN_bin2bn( g_data, size, NULL );
priv_key = pBN_bin2bn( x_data, 20, NULL );
pOPENSSL_cleanse( x_data, sizeof(x_data) );
pub_key = pBN_new();
ctx = pBN_CTX_new();
if (!pub_key || !ctx || !p || !q || !g || !priv_key)
{
if (ctx) pBN_CTX_free( ctx );
goto fail;
}
if (!pBN_mod_exp( pub_key, g, priv_key, p, ctx ))
{
pBN_CTX_free( ctx );
goto fail;
}
pBN_CTX_free( ctx );
dsa = pDSA_new();
if (!dsa) goto fail;
if (!pDSA_set0_pqg( dsa, p, q, g )) goto fail;
p = q = g = NULL;
if (!pDSA_set0_key( dsa, pub_key, priv_key )) goto fail;
pub_key = priv_key = NULL;
key->u.a.bitlen = pubkey->bitlen;
memcpy( &key->u.a.dss_seed, src, sizeof(key->u.a.dss_seed) );
if (key_data(key)->a.key) pDSA_free( key_data(key)->a.key );
key_data(key)->a.key = dsa;
return STATUS_SUCCESS;
fail:
pERR_clear_error();
if (dsa) pDSA_free( dsa );
if (p) pBN_free( p );
if (q) pBN_free( q );
if (g) pBN_free( g );
if (priv_key) ossl_bn_clear_free( priv_key );
if (pub_key) pBN_free( pub_key );
return STATUS_NO_MEMORY;
}
static NTSTATUS key_import_ecc_public( struct key *key, UCHAR *buf, ULONG len )
{
BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf;
enum ecc_curve_id target_curve = ECC_CURVE_NONE;
ULONG target_bitlen = 0;
EC_KEY *ec_key;
BIGNUM *x, *y;
UCHAR *src;
int nid;
if (!buf || len < sizeof(*ecc_blob)) return STATUS_INVALID_PARAMETER;
if (ecc_blob->dwMagic == BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC ||
(ecc_blob->dwMagic == BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC && key->alg_id == ALG_ID_ECDH))
{
EVP_PKEY *pkey;
const UCHAR *x_ptr;
if (key->alg_id != ALG_ID_ECDH ||
(key->u.a.curve_id != ECC_CURVE_NONE && key->u.a.curve_id != ECC_CURVE_25519))
return STATUS_NOT_SUPPORTED;
if (ecc_blob->cbKey != 32 || (ULONG64)len < sizeof(*ecc_blob) + 2ULL * 32) return STATUS_INVALID_PARAMETER;
x_ptr = (const UCHAR *)(ecc_blob + 1);
if (!(pkey = pEVP_PKEY_new_raw_public_key( EVP_PKEY_X25519, NULL, x_ptr, 32 )))
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
if (key_data(key)->a.pubkey)
{
if (is_x25519_key( key )) pEVP_PKEY_free( key_data(key)->a.pubkey );
else pEC_KEY_free( key_data(key)->a.pubkey );
}
key_data(key)->a.pubkey = pkey;
key->u.a.curve_id = ECC_CURVE_25519;
key->u.a.bitlen = 256;
return STATUS_SUCCESS;
}
switch (ecc_blob->dwMagic)
{
case BCRYPT_ECDH_PUBLIC_P256_MAGIC:
if (key->alg_id != ALG_ID_ECDH_P256 && !(key->alg_id == ALG_ID_ECDH && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P256R1))) return STATUS_NOT_SUPPORTED;
if (ecc_blob->cbKey != 32) return STATUS_INVALID_PARAMETER;
target_curve = ECC_CURVE_P256R1;
target_bitlen = 256;
nid = NID_X9_62_prime256v1;
break;
case BCRYPT_ECDSA_PUBLIC_P256_MAGIC:
if (key->alg_id != ALG_ID_ECDSA_P256 && !(key->alg_id == ALG_ID_ECDSA && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P256R1))) return STATUS_NOT_SUPPORTED;
if (ecc_blob->cbKey != 32) return STATUS_INVALID_PARAMETER;
target_curve = ECC_CURVE_P256R1;
target_bitlen = 256;
nid = NID_X9_62_prime256v1;
break;
case BCRYPT_ECDH_PUBLIC_P384_MAGIC:
if (key->alg_id != ALG_ID_ECDH_P384 && !(key->alg_id == ALG_ID_ECDH && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P384R1))) return STATUS_NOT_SUPPORTED;
if (ecc_blob->cbKey != 48) return STATUS_INVALID_PARAMETER;
target_curve = ECC_CURVE_P384R1;
target_bitlen = 384;
nid = NID_secp384r1;
break;
case BCRYPT_ECDSA_PUBLIC_P384_MAGIC:
if (key->alg_id != ALG_ID_ECDSA_P384 && !(key->alg_id == ALG_ID_ECDSA && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P384R1))) return STATUS_NOT_SUPPORTED;
if (ecc_blob->cbKey != 48) return STATUS_INVALID_PARAMETER;
target_curve = ECC_CURVE_P384R1;
target_bitlen = 384;
nid = NID_secp384r1;
break;
case BCRYPT_ECDH_PUBLIC_P521_MAGIC:
if (key->alg_id != ALG_ID_ECDH_P521 && !(key->alg_id == ALG_ID_ECDH && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P521R1))) return STATUS_NOT_SUPPORTED;
if (ecc_blob->cbKey != 66) return STATUS_INVALID_PARAMETER;
target_curve = ECC_CURVE_P521R1;
target_bitlen = 521;
nid = NID_secp521r1;
break;
case BCRYPT_ECDSA_PUBLIC_P521_MAGIC:
if (key->alg_id != ALG_ID_ECDSA_P521 && !(key->alg_id == ALG_ID_ECDSA && (key->u.a.curve_id == ECC_CURVE_NONE || key->u.a.curve_id == ECC_CURVE_P521R1))) return STATUS_NOT_SUPPORTED;
if (ecc_blob->cbKey != 66) return STATUS_INVALID_PARAMETER;
target_curve = ECC_CURVE_P521R1;
target_bitlen = 521;
nid = NID_secp521r1;
break;
default:
return STATUS_NOT_SUPPORTED;
}
if ((ULONG64)len < sizeof(*ecc_blob) + 2ULL * ecc_blob->cbKey)
return STATUS_INVALID_PARAMETER;
src = (UCHAR *)(ecc_blob + 1);
x = pBN_bin2bn( src, ecc_blob->cbKey, NULL );
src += ecc_blob->cbKey;
y = pBN_bin2bn( src, ecc_blob->cbKey, NULL );
if (!x || !y)
{
if (x) pBN_free( x );
if (y) pBN_free( y );
return STATUS_NO_MEMORY;
}
ec_key = pEC_KEY_new_by_curve_name( nid );
if (!ec_key)
{
pERR_clear_error();
if (x) pBN_free( x );
if (y) pBN_free( y );
return STATUS_NO_MEMORY;
}
if (!pEC_KEY_set_public_key_affine_coordinates( ec_key, x, y ))
{
pERR_clear_error();
pBN_free( x ); pBN_free( y );
pEC_KEY_free( ec_key );
return STATUS_INVALID_PARAMETER;
}
pBN_free( x ); pBN_free( y );
if (!pEC_KEY_check_key( ec_key ))
{
pERR_clear_error();
pEC_KEY_free( ec_key );
return STATUS_INVALID_PARAMETER;
}
if (key_data(key)->a.pubkey)
{
if (is_x25519_key( key )) pEVP_PKEY_free( key_data(key)->a.pubkey );
else pEC_KEY_free( key_data(key)->a.pubkey );
}
key->u.a.curve_id = target_curve;
key->u.a.bitlen = target_bitlen;
key_data(key)->a.pubkey = ec_key;
return STATUS_SUCCESS;
}
static NTSTATUS key_import_rsa_public( struct key *key, UCHAR *buf, ULONG len )
{
BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf;
RSA *rsa;
BIGNUM *n, *e;
UCHAR *src;
if (!buf || len < sizeof(*rsa_blob)) return STATUS_INVALID_PARAMETER;
if (rsa_blob->Magic != BCRYPT_RSAPUBLIC_MAGIC) return STATUS_NOT_SUPPORTED;
if (!rsa_blob->cbPublicExp || !rsa_blob->cbModulus)
return STATUS_INVALID_PARAMETER;
if ((ULONG64)len < (ULONG64)sizeof(*rsa_blob) + rsa_blob->cbPublicExp + (ULONG64)rsa_blob->cbModulus)
return STATUS_INVALID_PARAMETER;
src = (UCHAR *)(rsa_blob + 1);
e = pBN_bin2bn( src, rsa_blob->cbPublicExp, NULL );
src += rsa_blob->cbPublicExp;
n = pBN_bin2bn( src, rsa_blob->cbModulus, NULL );
rsa = pRSA_new();
if (!rsa || !e || !n || !pRSA_set0_key( rsa, n, e, NULL ))
{
pERR_clear_error();
if (rsa) pRSA_free( rsa );
if (e) pBN_free( e );
if (n) pBN_free( n );
return STATUS_NO_MEMORY;
}
key->u.a.bitlen = rsa_blob->BitLength;
if (key_data(key)->a.pubkey) pRSA_free( key_data(key)->a.pubkey );
key_data(key)->a.pubkey = rsa;
return STATUS_SUCCESS;
}
static NTSTATUS key_import_dsa_public( struct key *key, UCHAR *buf, ULONG len )
{
BCRYPT_DSA_KEY_BLOB *dsa_blob = (BCRYPT_DSA_KEY_BLOB *)buf;
DSA *dsa = NULL;
BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL;
UCHAR *src;
ULONG size;
if (!buf || len < sizeof(*dsa_blob)) return STATUS_INVALID_PARAMETER;
if (dsa_blob->dwMagic != BCRYPT_DSA_PUBLIC_MAGIC) return STATUS_NOT_SUPPORTED;
if (!dsa_blob->cbKey) return STATUS_INVALID_PARAMETER;
size = dsa_blob->cbKey;
/* size is caller-controlled and unbounded by bcrypt_main.c, so widen to avoid wrapping. */
if ((ULONG64)len < (ULONG64)sizeof(*dsa_blob) + 3 * (ULONG64)size) return STATUS_INVALID_PARAMETER;
src = (UCHAR *)(dsa_blob + 1);
p = pBN_bin2bn( src, size, NULL ); src += size;
g = pBN_bin2bn( src, size, NULL ); src += size;
pub_key = pBN_bin2bn( src, size, NULL );
q = pBN_bin2bn( dsa_blob->q, sizeof(dsa_blob->q), NULL );
dsa = pDSA_new();
if (!dsa || !p || !q || !g || !pub_key) goto fail;
if (!pDSA_set0_pqg( dsa, p, q, g )) goto fail;
p = q = g = NULL;
if (!pDSA_set0_key( dsa, pub_key, NULL )) goto fail;
pub_key = NULL;
key->u.a.bitlen = dsa_blob->cbKey * 8;
if (key_data(key)->a.pubkey) pDSA_free( key_data(key)->a.pubkey );
key_data(key)->a.pubkey = dsa;
return STATUS_SUCCESS;
fail:
pERR_clear_error();
if (dsa) pDSA_free( dsa );
if (p) pBN_free( p );
if (q) pBN_free( q );
if (g) pBN_free( g );
if (pub_key) pBN_free( pub_key );
return STATUS_NO_MEMORY;
}
static NTSTATUS key_import_dsa_capi_public( struct key *key, UCHAR *buf, ULONG len )
{
BLOBHEADER *hdr = (BLOBHEADER *)buf;
DSSPUBKEY *pubkey;
DSA *dsa = NULL;
BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL;
UCHAR *src, p_data[128], q_data[20], g_data[128], y_data[128];
ULONG size, i;
if (!buf || len < sizeof(*hdr) + sizeof(*pubkey)) return STATUS_INVALID_PARAMETER;
if (hdr->bType != PUBLICKEYBLOB) return STATUS_NOT_SUPPORTED;
pubkey = (DSSPUBKEY *)(hdr + 1);
if (pubkey->magic != MAGIC_DSS1) return STATUS_NOT_SUPPORTED;
size = pubkey->bitlen / 8;
if (!size || size > sizeof(p_data) || len < sizeof(*hdr) + sizeof(*pubkey) + size * 3 + 20 + sizeof(key->u.a.dss_seed))
return STATUS_INVALID_PARAMETER;
src = (UCHAR *)(pubkey + 1);
for (i = 0; i < size; i++) p_data[i] = src[size - i - 1];
src += size;
for (i = 0; i < 20; i++) q_data[i] = src[20 - i - 1];
src += 20;
for (i = 0; i < size; i++) g_data[i] = src[size - i - 1];
src += size;
for (i = 0; i < size; i++) y_data[i] = src[size - i - 1];
src += size;
p = pBN_bin2bn( p_data, size, NULL );
q = pBN_bin2bn( q_data, 20, NULL );
g = pBN_bin2bn( g_data, size, NULL );
pub_key = pBN_bin2bn( y_data, size, NULL );
dsa = pDSA_new();
if (!dsa || !p || !q || !g || !pub_key) goto fail;
if (!pDSA_set0_pqg( dsa, p, q, g )) goto fail;
p = q = g = NULL;
if (!pDSA_set0_key( dsa, pub_key, NULL )) goto fail;
pub_key = NULL;
key->u.a.bitlen = pubkey->bitlen;
memcpy( &key->u.a.dss_seed, src, sizeof(key->u.a.dss_seed) );
if (key_data(key)->a.pubkey) pDSA_free( key_data(key)->a.pubkey );
key_data(key)->a.pubkey = dsa;
return STATUS_SUCCESS;
fail:
pERR_clear_error();
if (dsa) pDSA_free( dsa );
if (p) pBN_free( p );
if (q) pBN_free( q );
if (g) pBN_free( g );
if (pub_key) pBN_free( pub_key );
return STATUS_NO_MEMORY;
}
static NTSTATUS key_export_dh_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf;
DH *dh = key_data(key)->a.key ? key_data(key)->a.key : key_data(key)->a.pubkey;
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
ULONG size = key->u.a.bitlen / 8;
UCHAR *dst;
if (!dh) return STATUS_INVALID_PARAMETER;
pDH_get0_pqg( dh, &p, &q, &g );
pDH_get0_key( dh, &pub_key, &priv_key );
if (!p || !g || !pub_key) return STATUS_INVALID_PARAMETER;
*ret_len = sizeof(*dh_blob) + size * 3;
if (buf && len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (len >= *ret_len && buf)
{
NTSTATUS status;
dh_blob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC;
dh_blob->cbKey = size;
dst = (UCHAR *)(dh_blob + 1);
if ((status = export_bn( dst, size, p, TRUE, NULL ))) return status;
dst += size;
if ((status = export_bn( dst, size, g, TRUE, NULL ))) return status;
dst += size;
if ((status = export_bn( dst, size, pub_key, TRUE, NULL ))) return status;
}
return STATUS_SUCCESS;
}
static NTSTATUS key_export_dh( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf;
DH *dh = key_data(key)->a.key;
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
ULONG size = key->u.a.bitlen / 8;
UCHAR *dst;
if (!dh) return STATUS_INVALID_PARAMETER;
pDH_get0_pqg( dh, &p, &q, &g );
pDH_get0_key( dh, &pub_key, &priv_key );
if (!p || !g || !priv_key) return STATUS_INVALID_PARAMETER;
*ret_len = sizeof(*dh_blob) + size * 4;
if (buf && len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (len >= *ret_len && buf)
{
NTSTATUS status;
dh_blob->dwMagic = BCRYPT_DH_PRIVATE_MAGIC;
dh_blob->cbKey = size;
dst = (UCHAR *)(dh_blob + 1);
if ((status = export_bn( dst, size, p, TRUE, NULL ))) return status;
dst += size;
if ((status = export_bn( dst, size, g, TRUE, NULL ))) return status;
dst += size;
if ((status = export_bn( dst, size, pub_key, TRUE, NULL ))) return status;
dst += size;
if ((status = export_bn( dst, size, priv_key, TRUE, NULL ))) return status;
}
return STATUS_SUCCESS;
}
static NTSTATUS key_export_dh_params( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BCRYPT_DH_PARAMETER_HEADER *dh_blob = (BCRYPT_DH_PARAMETER_HEADER *)buf;
DH *dh = key_data(key)->a.pubkey ? key_data(key)->a.pubkey : key_data(key)->a.key;
const BIGNUM *p, *q, *g;
ULONG size = key->u.a.bitlen / 8;
UCHAR *dst;
if (!dh) return STATUS_INVALID_PARAMETER;
pDH_get0_pqg( dh, &p, &q, &g );
if (!p || !g) return STATUS_INVALID_PARAMETER;
*ret_len = sizeof(*dh_blob) + size * 2;
if (buf && len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (len >= *ret_len && buf)
{
NTSTATUS status;
dh_blob->cbLength = sizeof(*dh_blob) + size * 2;
dh_blob->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC;
dh_blob->cbKeyLength = size;
dst = (UCHAR *)(dh_blob + 1);
if ((status = export_bn( dst, size, p, TRUE, NULL ))) return status;
dst += size;
if ((status = export_bn( dst, size, g, TRUE, NULL ))) return status;
}
return STATUS_SUCCESS;
}
static NTSTATUS key_asymmetric_export( void *args )
{
const struct key_asymmetric_export_params *params = args;
struct key *key = params->key;
ULONG flags = params->flags;
if (!(key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE;
switch (key->alg_id)
{
case ALG_ID_ECDH:
case ALG_ID_ECDH_P256:
case ALG_ID_ECDH_P384:
case ALG_ID_ECDH_P521:
case ALG_ID_ECDSA:
case ALG_ID_ECDSA_P256:
case ALG_ID_ECDSA_P384:
case ALG_ID_ECDSA_P521:
if (flags & KEY_EXPORT_FLAG_PUBLIC)
return key_export_ecc_public( key, params->buf, params->len, params->ret_len );
return key_export_ecc( key, params->buf, params->len, params->ret_len );
case ALG_ID_RSA:
case ALG_ID_RSA_SIGN:
if (flags & KEY_EXPORT_FLAG_PUBLIC)
return key_export_rsa_public( key, params->buf, params->len, params->ret_len );
return key_export_rsa( key, flags, params->buf, params->len, params->ret_len );
case ALG_ID_DSA:
if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2)
{
if (flags & KEY_EXPORT_FLAG_PUBLIC)
return key_export_dsa_capi_public( key, params->buf, params->len, params->ret_len );
return key_export_dsa_capi( key, params->buf, params->len, params->ret_len );
}
if (flags & KEY_EXPORT_FLAG_PUBLIC)
return key_export_dsa_public( key, params->buf, params->len, params->ret_len );
return STATUS_NOT_IMPLEMENTED;
case ALG_ID_DH:
if (flags & KEY_EXPORT_FLAG_DH_PARAMETERS)
return key_export_dh_params( key, params->buf, params->len, params->ret_len );
if (flags & KEY_EXPORT_FLAG_PUBLIC)
return key_export_dh_public( key, params->buf, params->len, params->ret_len );
return key_export_dh( key, params->buf, params->len, params->ret_len );
default:
return STATUS_NOT_SUPPORTED;
}
}
static NTSTATUS key_import_dh_public( struct key *key, UCHAR *buf, ULONG len )
{
BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf;
DH *dh = NULL;
BIGNUM *p = NULL, *g = NULL, *pub_key = NULL;
UCHAR *src;
ULONG size;
if (!buf || len < sizeof(*dh_blob)) return STATUS_INVALID_PARAMETER;
if (dh_blob->dwMagic != BCRYPT_DH_PUBLIC_MAGIC) return STATUS_NOT_SUPPORTED;
if (!dh_blob->cbKey) return STATUS_INVALID_PARAMETER;
size = dh_blob->cbKey;
if ((ULONG64)len < sizeof(*dh_blob) + 3ULL * size) return STATUS_INVALID_PARAMETER;
src = (UCHAR *)(dh_blob + 1);
p = pBN_bin2bn( src, size, NULL ); src += size;
g = pBN_bin2bn( src, size, NULL ); src += size;
pub_key = pBN_bin2bn( src, size, NULL );
dh = pDH_new();
if (!dh || !p || !g || !pub_key) goto fail;
if (!pDH_set0_pqg( dh, p, NULL, g )) goto fail;
p = g = NULL;
if (!pDH_set0_key( dh, pub_key, NULL )) goto fail;
pub_key = NULL;
key->u.a.bitlen = dh_blob->cbKey * 8;
if (key_data(key)->a.pubkey) pDH_free( key_data(key)->a.pubkey );
key_data(key)->a.pubkey = dh;
return STATUS_SUCCESS;
fail:
pERR_clear_error();
if (dh) pDH_free( dh );
if (p) pBN_free( p );
if (g) pBN_free( g );
if (pub_key) pBN_free( pub_key );
return STATUS_NO_MEMORY;
}
static NTSTATUS key_import_dh( struct key *key, UCHAR *buf, ULONG len )
{
BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf;
DH *dh = NULL;
BIGNUM *p = NULL, *g = NULL, *pub_key = NULL, *priv_key = NULL;
UCHAR *src;
ULONG size;
if (!buf || len < sizeof(*dh_blob)) return STATUS_INVALID_PARAMETER;
if (dh_blob->dwMagic != BCRYPT_DH_PRIVATE_MAGIC) return STATUS_NOT_SUPPORTED;
if (!dh_blob->cbKey) return STATUS_INVALID_PARAMETER;
size = dh_blob->cbKey;
if ((ULONG64)len < sizeof(*dh_blob) + 4ULL * size) return STATUS_INVALID_PARAMETER;
src = (UCHAR *)(dh_blob + 1);
p = pBN_bin2bn( src, size, NULL ); src += size;
g = pBN_bin2bn( src, size, NULL ); src += size;
pub_key = pBN_bin2bn( src, size, NULL ); src += size;
priv_key = pBN_bin2bn( src, size, NULL );
dh = pDH_new();
if (!dh || !p || !g || !pub_key || !priv_key) goto fail;
if (!pDH_set0_pqg( dh, p, NULL, g )) goto fail;
p = g = NULL;
if (!pDH_set0_key( dh, pub_key, priv_key )) goto fail;
pub_key = priv_key = NULL;
key->u.a.bitlen = dh_blob->cbKey * 8;
if (key_data(key)->a.key) pDH_free( key_data(key)->a.key );
key_data(key)->a.key = dh;
return STATUS_SUCCESS;
fail:
pERR_clear_error();
if (dh) pDH_free( dh );
if (p) pBN_free( p );
if (g) pBN_free( g );
if (pub_key) pBN_free( pub_key );
if (priv_key) ossl_bn_clear_free( priv_key );
return STATUS_NO_MEMORY;
}
static NTSTATUS key_import_dh_params( struct key *key, UCHAR *buf, ULONG len )
{
BCRYPT_DH_PARAMETER_HEADER *dh_header = (BCRYPT_DH_PARAMETER_HEADER *)buf;
DH *dh = NULL;
BIGNUM *p = NULL, *g = NULL;
UCHAR *src;
ULONG size;
if (!buf || len < sizeof(*dh_header)) return STATUS_INVALID_PARAMETER;
if (dh_header->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC) return STATUS_NOT_SUPPORTED;
if (!dh_header->cbKeyLength) return STATUS_INVALID_PARAMETER;
size = dh_header->cbKeyLength;
if ((ULONG64)len < sizeof(*dh_header) + 2ULL * size) return STATUS_INVALID_PARAMETER;
src = (UCHAR *)(dh_header + 1);
p = pBN_bin2bn( src, size, NULL ); src += size;
g = pBN_bin2bn( src, size, NULL );
dh = pDH_new();
if (!dh || !p || !g) goto fail;
if (!pDH_set0_pqg( dh, p, NULL, g )) goto fail;
p = g = NULL;
key->u.a.bitlen = dh_header->cbKeyLength * 8;
if (key_data(key)->a.pubkey) pDH_free( key_data(key)->a.pubkey );
key_data(key)->a.pubkey = dh;
return STATUS_SUCCESS;
fail:
pERR_clear_error();
if (dh) pDH_free( dh );
if (p) pBN_free( p );
if (g) pBN_free( g );
return STATUS_NO_MEMORY;
}
static NTSTATUS key_asymmetric_import( void *args )
{
const struct key_asymmetric_import_params *params = args;
struct key *key = params->key;
ULONG flags = params->flags;
if (!alg_family_available( key )) return STATUS_NOT_SUPPORTED;
switch (key->alg_id)
{
case ALG_ID_ECDH:
case ALG_ID_ECDH_P256:
case ALG_ID_ECDH_P384:
case ALG_ID_ECDH_P521:
case ALG_ID_ECDSA:
case ALG_ID_ECDSA_P256:
case ALG_ID_ECDSA_P384:
case ALG_ID_ECDSA_P521:
if (flags & KEY_IMPORT_FLAG_PUBLIC)
return key_import_ecc_public( key, params->buf, params->len );
return key_import_ecc( key, flags, params->buf, params->len );
case ALG_ID_RSA:
case ALG_ID_RSA_SIGN:
if (flags & KEY_IMPORT_FLAG_PUBLIC)
return key_import_rsa_public( key, params->buf, params->len );
return key_import_rsa( key, flags, params->buf, params->len );
case ALG_ID_DSA:
if (flags & KEY_IMPORT_FLAG_PUBLIC)
{
if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2)
return key_import_dsa_capi_public( key, params->buf, params->len );
return key_import_dsa_public( key, params->buf, params->len );
}
if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2)
return key_import_dsa_capi( key, params->buf, params->len );
FIXME( "DSA private key not supported\n" );
return STATUS_NOT_IMPLEMENTED;
case ALG_ID_DH:
if (flags & KEY_IMPORT_FLAG_DH_PARAMETERS)
return key_import_dh_params( key, params->buf, params->len );
if (flags & KEY_IMPORT_FLAG_PUBLIC)
return key_import_dh_public( key, params->buf, params->len );
return key_import_dh( key, params->buf, params->len );
default:
FIXME( "algorithm %u not yet supported\n", key->alg_id );
return STATUS_NOT_IMPLEMENTED;
}
}
/* GnuTLS matched these with wcscmp(), so the comparison is case-sensitive. */
static inline int match_alg( const WCHAR *str, const char *ascii )
{
if (!str || !ascii) return 0;
while (*str && *ascii)
{
if (*str != (WCHAR)*ascii) return 0;
str++;
ascii++;
}
return *str == 0 && *ascii == 0;
}
static int get_hash_nid( const WCHAR *alg )
{
if (!alg) return NID_undef;
if (match_alg( alg, "SHA1" )) return NID_sha1;
if (match_alg( alg, "SHA256" )) return NID_sha256;
if (match_alg( alg, "SHA384" )) return NID_sha384;
if (match_alg( alg, "SHA512" )) return NID_sha512;
if (match_alg( alg, "MD5" )) return NID_md5;
if (match_alg( alg, "MD4" )) return NID_md4; /* addition beyond gnutls.c */
if (match_alg( alg, "MD2" )) return NID_md2;
return NID_undef;
}
static const EVP_MD *get_evp_md( int nid )
{
switch (nid)
{
case NID_sha1: return pEVP_sha1();
case NID_sha256: return pEVP_sha256();
case NID_sha384: return pEVP_sha384();
case NID_sha512: return pEVP_sha512();
case NID_md5: return pEVP_md5();
case NID_md4: if (pEVP_md4) return pEVP_md4(); break;
case NID_md2: if (pEVP_md2) return pEVP_md2(); break;
default: break;
}
return NULL;
}
static NTSTATUS key_asymmetric_verify( void *args )
{
const struct key_asymmetric_verify_params *params = args;
struct key *key = params->key;
if (!params->signature) return STATUS_INVALID_PARAMETER;
if (params->hash_len > INT_MAX || params->signature_len > INT_MAX) return STATUS_INVALID_PARAMETER;
switch (key->alg_id)
{
case ALG_ID_ECDSA:
case ALG_ID_ECDSA_P256:
case ALG_ID_ECDSA_P384:
case ALG_ID_ECDSA_P521:
{
EC_KEY *ec = key_data(key)->a.key ? key_data(key)->a.key : key_data(key)->a.pubkey;
ECDSA_SIG *sig;
BIGNUM *r, *s;
ULONG size_ul = 0;
DWORD size;
int ret;
get_ecc_curve_nid( key, &size_ul );
size = size_ul ? size_ul : len_from_bitlen( key->u.a.bitlen );
if (params->flags) FIXME( "flags %#x not supported\n", params->flags );
if (!ec || !params->hash) return STATUS_INVALID_PARAMETER;
/* gnutls.c split whatever it was handed and let the verify fail. */
if (params->signature_len != size * 2) return STATUS_INVALID_SIGNATURE;
/* only the hash size must match, not the actual hash function */
switch (params->hash_len)
{
case 20:
case 32:
case 48:
case 64:
break;
default:
FIXME( "hash size %u not yet supported\n", params->hash_len );
return STATUS_INVALID_SIGNATURE;
}
r = pBN_bin2bn( params->signature, size, NULL );
s = pBN_bin2bn( params->signature + size, size, NULL );
if (!r || !s)
{
if (r) pBN_free( r );
if (s) pBN_free( s );
return STATUS_NO_MEMORY;
}
sig = pECDSA_SIG_new();
if (!sig)
{
pBN_free( r );
pBN_free( s );
return STATUS_NO_MEMORY;
}
if (!pECDSA_SIG_set0( sig, r, s ))
{
pBN_free( r );
pBN_free( s );
pECDSA_SIG_free( sig );
return STATUS_INTERNAL_ERROR;
}
ret = pECDSA_do_verify( params->hash, params->hash_len, sig, ec );
if (ret != 1) pERR_clear_error();
pECDSA_SIG_free( sig );
return ret == 1 ? STATUS_SUCCESS : STATUS_INVALID_SIGNATURE;
}
case ALG_ID_RSA:
case ALG_ID_RSA_SIGN:
{
RSA *rsa = key_data(key)->a.key ? key_data(key)->a.key : key_data(key)->a.pubkey;
if (!rsa) return STATUS_INVALID_PARAMETER;
/* Note: PKCS1 is checked before PSS, matching the GnuTLS backend. */
if (params->flags & BCRYPT_PAD_PKCS1)
{
BCRYPT_PKCS1_PADDING_INFO *info = params->padding;
if (!info) return STATUS_INVALID_PARAMETER;
if (info->pszAlgId)
{
int nid = get_hash_nid( info->pszAlgId );
if (nid == NID_undef) return STATUS_NOT_SUPPORTED;
if (!params->hash) return STATUS_INVALID_PARAMETER;
/* gnutls.c did not check hash_len against the digest size; RSA_verify()
* rejects a mismatch itself. Revisit if Windows differs. */
if (!pRSA_verify( nid, params->hash, params->hash_len, params->signature, params->signature_len, rsa ))
{
pERR_clear_error();
return STATUS_INVALID_SIGNATURE;
}
return STATUS_SUCCESS;
}
else
{
UCHAR stack_buf[512], *em = stack_buf;
ULONG key_bytes = params->signature_len;
const BIGNUM *n = NULL;
int ret;
if (!params->hash) return STATUS_INVALID_PARAMETER;
pRSA_get0_key( rsa, &n, NULL, NULL );
if (n) key_bytes = bn_num_bytes( n );
if (key_bytes < params->signature_len) key_bytes = params->signature_len;
if (key_bytes > sizeof(stack_buf))
{
if (!(em = malloc( key_bytes )))
return STATUS_NO_MEMORY;
}
ret = pRSA_public_decrypt( params->signature_len, params->signature, em, rsa, RSA_PKCS1_PADDING );
if (ret < 0 || (ULONG)ret != params->hash_len || pCRYPTO_memcmp( em, params->hash, ret ))
{
pERR_clear_error();
pOPENSSL_cleanse( em, key_bytes );
if (em != stack_buf) free( em );
return STATUS_INVALID_SIGNATURE;
}
pOPENSSL_cleanse( em, key_bytes );
if (em != stack_buf) free( em );
return STATUS_SUCCESS;
}
}
else if (params->flags & BCRYPT_PAD_PSS)
{
BCRYPT_PSS_PADDING_INFO *info = params->padding;
int nid;
const EVP_MD *md;
UCHAR *em;
int ret;
const BIGNUM *n = NULL;
ULONG key_bytes;
if (!info) return STATUS_INVALID_PARAMETER;
if (!info->pszAlgId) return STATUS_INVALID_SIGNATURE;
if (info->cbSalt > params->signature_len) return STATUS_INVALID_PARAMETER;
nid = get_hash_nid( info->pszAlgId );
if (nid == NID_undef) return STATUS_NOT_SUPPORTED;
md = get_evp_md( nid );
if (!md) return STATUS_INVALID_PARAMETER;
if (!params->hash) return STATUS_INVALID_PARAMETER;
if (params->hash_len != (ULONG)pEVP_MD_get_size( md )) return STATUS_INVALID_SIGNATURE;
pRSA_get0_key( rsa, &n, NULL, NULL );
if (!n) return STATUS_INVALID_PARAMETER;
key_bytes = bn_num_bytes( n );
if (params->signature_len != key_bytes) return STATUS_INVALID_SIGNATURE;
em = malloc( key_bytes );
if (!em) return STATUS_NO_MEMORY;
if (pRSA_public_decrypt( params->signature_len, params->signature, em, rsa, RSA_NO_PADDING ) != (int)key_bytes)
{
pERR_clear_error();
pOPENSSL_cleanse( em, key_bytes );
free( em );
return STATUS_INVALID_SIGNATURE;
}
ret = pRSA_verify_PKCS1_PSS_mgf1( rsa, params->hash, md, md, em, info->cbSalt );
if (!ret) pERR_clear_error();
pOPENSSL_cleanse( em, key_bytes );
free( em );
return ret ? STATUS_SUCCESS : STATUS_INVALID_SIGNATURE;
}
else return STATUS_INVALID_PARAMETER;
}
case ALG_ID_DSA:
{
DSA *dsa = key_data(key)->a.key ? key_data(key)->a.key : key_data(key)->a.pubkey;
DSA_SIG *sig;
BIGNUM *r, *s;
int ret;
if (params->flags) FIXME( "flags %#x not supported\n", params->flags );
if (!params->hash || params->hash_len != 20)
{
FIXME( "hash size %u not supported\n", params->hash_len );
return STATUS_INVALID_PARAMETER;
}
if (!dsa) return STATUS_INVALID_PARAMETER;
/* gnutls.c split whatever it was handed and let the verify fail. */
if (params->signature_len != 40) return STATUS_INVALID_SIGNATURE;
r = pBN_bin2bn( params->signature, 20, NULL );
s = pBN_bin2bn( params->signature + 20, 20, NULL );
if (!r || !s)
{
if (r) pBN_free( r );
if (s) pBN_free( s );
return STATUS_NO_MEMORY;
}
sig = pDSA_SIG_new();
if (!sig)
{
pBN_free( r );
pBN_free( s );
return STATUS_NO_MEMORY;
}
if (!pDSA_SIG_set0( sig, r, s ))
{
pBN_free( r );
pBN_free( s );
pDSA_SIG_free( sig );
return STATUS_INTERNAL_ERROR;
}
ret = pDSA_do_verify( params->hash, params->hash_len, sig, dsa );
if (ret != 1) pERR_clear_error();
pDSA_SIG_free( sig );
return ret == 1 ? STATUS_SUCCESS : STATUS_INVALID_SIGNATURE;
}
default:
FIXME( "algorithm %u not yet supported\n", key->alg_id );
return STATUS_NOT_IMPLEMENTED;
}
}
static NTSTATUS key_asymmetric_sign( void *args )
{
const struct key_asymmetric_sign_params *params = args;
struct key *key = params->key;
NTSTATUS status;
if (!params->ret_len) return STATUS_INVALID_PARAMETER;
if (params->input_len > INT_MAX || params->output_len > INT_MAX) return STATUS_INVALID_PARAMETER;
if (key->alg_id == ALG_ID_ECDSA || key->alg_id == ALG_ID_ECDSA_P256 ||
key->alg_id == ALG_ID_ECDSA_P384 || key->alg_id == ALG_ID_ECDSA_P521)
{
EC_KEY *ec = key_data(key)->a.key;
ECDSA_SIG *sig;
const BIGNUM *r = NULL, *s = NULL;
ULONG size_ul = 0;
DWORD size;
int nid;
get_ecc_curve_nid( key, &size_ul );
size = size_ul ? size_ul : len_from_bitlen( key->u.a.bitlen );
/* With ECDSA the digest algorithm follows from the hash length. */
switch (params->input_len)
{
case 20: nid = NID_sha1; break;
case 32: nid = NID_sha256; break;
case 48: nid = NID_sha384; break;
case 64: nid = NID_sha512; break;
default:
FIXME( "hash size %u not yet supported\n", params->input_len );
return STATUS_INVALID_PARAMETER;
}
if (params->flags == BCRYPT_PAD_PKCS1)
{
BCRYPT_PKCS1_PADDING_INFO *pad = params->padding;
/* gnutls.c accepted PKCS#1 here as long as the caller agreed on the digest. */
if (pad && pad->pszAlgId && get_hash_nid( pad->pszAlgId ) != nid)
{
WARN( "incorrect hashing algorithm %s\n", debugstr_w(pad->pszAlgId) );
return STATUS_INVALID_PARAMETER;
}
}
else if (params->flags) return STATUS_INVALID_PARAMETER;
*params->ret_len = size * 2;
if (!params->output)
return STATUS_SUCCESS;
if (!ec || !params->input) return STATUS_INVALID_PARAMETER;
if (params->output_len < *params->ret_len)
return STATUS_BUFFER_TOO_SMALL;
sig = pECDSA_do_sign( params->input, params->input_len, ec );
if (!sig)
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
pECDSA_SIG_get0( sig, &r, &s );
if ((status = export_bn( params->output, size, r, TRUE, NULL )) ||
(status = export_bn( params->output + size, size, s, TRUE, NULL )))
{
pECDSA_SIG_free( sig );
return status;
}
pECDSA_SIG_free( sig );
return STATUS_SUCCESS;
}
else if (key->alg_id == ALG_ID_RSA || key->alg_id == ALG_ID_RSA_SIGN)
{
RSA *rsa = key_data(key)->a.key;
const BIGNUM *n;
unsigned int sig_len = 0;
int nid = NID_undef;
const EVP_MD *md = NULL;
if (!rsa && !key_data(key)->a.pubkey) return STATUS_INVALID_HANDLE;
pRSA_get0_key( rsa ? rsa : key_data(key)->a.pubkey, &n, NULL, NULL );
*params->ret_len = bn_num_bytes( n );
/* Note: PKCS1 is checked before PSS, matching key_asymmetric_verify() and gnutls.c. */
if (params->flags & BCRYPT_PAD_PKCS1)
{
BCRYPT_PKCS1_PADDING_INFO *info = params->padding;
if (!info) return STATUS_INVALID_PARAMETER;
if (info->pszAlgId)
{
nid = get_hash_nid( info->pszAlgId );
if (nid == NID_undef) return STATUS_NOT_SUPPORTED;
}
}
else if (params->flags & BCRYPT_PAD_PSS)
{
BCRYPT_PSS_PADDING_INFO *info = params->padding;
if (!info || !info->pszAlgId) return STATUS_INVALID_PARAMETER;
if (info->cbSalt > *params->ret_len) return STATUS_INVALID_PARAMETER;
nid = get_hash_nid( info->pszAlgId );
if (nid == NID_undef) return STATUS_NOT_SUPPORTED;
if (!(md = get_evp_md( nid ))) return STATUS_INVALID_PARAMETER;
}
else if (!params->flags) return STATUS_INVALID_PARAMETER;
else return STATUS_NOT_IMPLEMENTED;
if (!params->output)
return STATUS_SUCCESS;
if (!rsa) return STATUS_INVALID_PARAMETER;
if (params->output_len < *params->ret_len)
return STATUS_BUFFER_TOO_SMALL;
if (params->flags & BCRYPT_PAD_PKCS1)
{
BCRYPT_PKCS1_PADDING_INFO *info = params->padding;
if (info->pszAlgId)
{
if (!params->input) return STATUS_INVALID_PARAMETER;
/* gnutls.c did not check input_len against the digest size here; left to
* OpenSSL, which rejects a mismatch itself. Revisit if Windows differs. */
if (!pRSA_sign( nid, params->input, params->input_len, params->output, &sig_len, rsa ))
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
*params->ret_len = sig_len;
}
else
{
int ret;
if (!params->input) return STATUS_INVALID_PARAMETER;
if (!pRSA_private_encrypt) return STATUS_NOT_SUPPORTED;
ret = pRSA_private_encrypt( params->input_len, params->input, params->output, rsa, RSA_PKCS1_PADDING );
if (ret < 0)
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
*params->ret_len = ret;
}
}
else if (params->flags & BCRYPT_PAD_PSS)
{
BCRYPT_PSS_PADDING_INFO *info = params->padding;
ULONG em_len = *params->ret_len;
UCHAR *em;
if (!params->input || params->input_len != (ULONG)pEVP_MD_get_size( md ))
return STATUS_INVALID_PARAMETER;
em = malloc( em_len );
if (!em) return STATUS_NO_MEMORY;
if (!pRSA_padding_add_PKCS1_PSS_mgf1( rsa, em, params->input, md, md, info->cbSalt ))
{
pERR_clear_error();
pOPENSSL_cleanse( em, em_len );
free( em );
return STATUS_INTERNAL_ERROR;
}
if (!pRSA_private_encrypt || pRSA_private_encrypt( em_len, em, params->output, rsa, RSA_NO_PADDING ) < 0)
{
pERR_clear_error();
pOPENSSL_cleanse( em, em_len );
free( em );
return STATUS_INTERNAL_ERROR;
}
pOPENSSL_cleanse( em, em_len );
free( em );
}
return STATUS_SUCCESS;
}
else if (key->alg_id == ALG_ID_DSA)
{
DSA *dsa = key_data(key)->a.key;
DSA_SIG *sig;
const BIGNUM *r = NULL, *s = NULL;
if (params->flags) FIXME( "flags %#x not supported\n", params->flags );
if (params->input_len != 20)
{
FIXME( "hash size %u not supported\n", params->input_len );
return STATUS_INVALID_PARAMETER;
}
*params->ret_len = 40;
if (!params->output)
return STATUS_SUCCESS;
if (!dsa || !params->input) return STATUS_INVALID_PARAMETER;
if (params->output_len < *params->ret_len)
return STATUS_BUFFER_TOO_SMALL;
sig = pDSA_do_sign( params->input, params->input_len, dsa );
if (!sig)
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
pDSA_SIG_get0( sig, &r, &s );
if ((status = export_bn( params->output, 20, r, TRUE, NULL )) ||
(status = export_bn( params->output + 20, 20, s, TRUE, NULL )))
{
pDSA_SIG_free( sig );
return status;
}
pDSA_SIG_free( sig );
return STATUS_SUCCESS;
}
else
{
FIXME( "algorithm %u not yet supported\n", key->alg_id );
return STATUS_NOT_IMPLEMENTED;
}
}
static NTSTATUS key_asymmetric_destroy( void *args )
{
struct key *key = args;
if (key_data(key)->a.key)
{
switch (key->alg_id)
{
case ALG_ID_RSA:
case ALG_ID_RSA_SIGN:
pRSA_free( key_data(key)->a.key );
break;
case ALG_ID_ECDH:
case ALG_ID_ECDH_P256:
case ALG_ID_ECDH_P384:
case ALG_ID_ECDH_P521:
case ALG_ID_ECDSA:
case ALG_ID_ECDSA_P256:
case ALG_ID_ECDSA_P384:
case ALG_ID_ECDSA_P521:
if (is_x25519_key( key )) pEVP_PKEY_free( key_data(key)->a.key );
else pEC_KEY_free( key_data(key)->a.key );
break;
case ALG_ID_DSA:
pDSA_free( key_data(key)->a.key );
break;
case ALG_ID_DH:
pDH_free( key_data(key)->a.key );
break;
default:
break;
}
key_data(key)->a.key = NULL;
}
if (key_data(key)->a.pubkey)
{
switch (key->alg_id)
{
case ALG_ID_RSA:
case ALG_ID_RSA_SIGN:
pRSA_free( key_data(key)->a.pubkey );
break;
case ALG_ID_ECDH:
case ALG_ID_ECDH_P256:
case ALG_ID_ECDH_P384:
case ALG_ID_ECDH_P521:
case ALG_ID_ECDSA:
case ALG_ID_ECDSA_P256:
case ALG_ID_ECDSA_P384:
case ALG_ID_ECDSA_P521:
if (is_x25519_key( key )) pEVP_PKEY_free( key_data(key)->a.pubkey );
else pEC_KEY_free( key_data(key)->a.pubkey );
break;
case ALG_ID_DSA:
pDSA_free( key_data(key)->a.pubkey );
break;
case ALG_ID_DH:
pDH_free( key_data(key)->a.pubkey );
break;
default:
break;
}
key_data(key)->a.pubkey = NULL;
}
return STATUS_SUCCESS;
}
static NTSTATUS key_asymmetric_duplicate( void *args )
{
const struct key_asymmetric_duplicate_params *params = args;
struct key *key_orig = params->key_orig;
struct key *key_copy = params->key_copy;
switch (key_orig->alg_id)
{
case ALG_ID_RSA:
case ALG_ID_RSA_SIGN:
if (key_data(key_orig)->a.key)
{
const BIGNUM *n, *e, *d;
pRSA_get0_key( key_data(key_orig)->a.key, &n, &e, &d );
if (d)
key_data(key_copy)->a.key = pRSAPrivateKey_dup( key_data(key_orig)->a.key );
else
key_data(key_copy)->a.key = pRSAPublicKey_dup( key_data(key_orig)->a.key );
if (!key_data(key_copy)->a.key)
{
pERR_clear_error();
return STATUS_NO_MEMORY;
}
}
if (key_data(key_orig)->a.pubkey)
{
key_data(key_copy)->a.pubkey = pRSAPublicKey_dup( key_data(key_orig)->a.pubkey );
if (!key_data(key_copy)->a.pubkey)
{
pERR_clear_error();
if (key_data(key_copy)->a.key)
{
pRSA_free( key_data(key_copy)->a.key );
key_data(key_copy)->a.key = NULL;
}
return STATUS_NO_MEMORY;
}
}
break;
case ALG_ID_ECDH:
case ALG_ID_ECDH_P256:
case ALG_ID_ECDH_P384:
case ALG_ID_ECDH_P521:
case ALG_ID_ECDSA:
case ALG_ID_ECDSA_P256:
case ALG_ID_ECDSA_P384:
case ALG_ID_ECDSA_P521:
if (is_x25519_key( key_orig ))
{
if (key_data(key_orig)->a.key)
{
UCHAR raw[32];
size_t raw_len = sizeof(raw);
if (pEVP_PKEY_get_raw_private_key( key_data(key_orig)->a.key, raw, &raw_len ) > 0)
key_data(key_copy)->a.key = pEVP_PKEY_new_raw_private_key( EVP_PKEY_X25519, NULL, raw, raw_len );
else if (pEVP_PKEY_get_raw_public_key( key_data(key_orig)->a.key, raw, &raw_len ) > 0)
key_data(key_copy)->a.key = pEVP_PKEY_new_raw_public_key( EVP_PKEY_X25519, NULL, raw, raw_len );
pOPENSSL_cleanse( raw, sizeof(raw) );
if (!key_data(key_copy)->a.key)
{
pERR_clear_error();
return STATUS_NO_MEMORY;
}
}
if (key_data(key_orig)->a.pubkey)
{
UCHAR raw[32];
size_t raw_len = sizeof(raw);
if (pEVP_PKEY_get_raw_public_key( key_data(key_orig)->a.pubkey, raw, &raw_len ) > 0)
key_data(key_copy)->a.pubkey = pEVP_PKEY_new_raw_public_key( EVP_PKEY_X25519, NULL, raw, raw_len );
pOPENSSL_cleanse( raw, sizeof(raw) );
if (!key_data(key_copy)->a.pubkey)
{
pERR_clear_error();
if (key_data(key_copy)->a.key)
{
pEVP_PKEY_free( key_data(key_copy)->a.key );
key_data(key_copy)->a.key = NULL;
}
return STATUS_NO_MEMORY;
}
}
break;
}
if (key_data(key_orig)->a.key)
{
key_data(key_copy)->a.key = pEC_KEY_dup( key_data(key_orig)->a.key );
if (!key_data(key_copy)->a.key)
{
pERR_clear_error();
return STATUS_NO_MEMORY;
}
}
if (key_data(key_orig)->a.pubkey)
{
key_data(key_copy)->a.pubkey = pEC_KEY_dup( key_data(key_orig)->a.pubkey );
if (!key_data(key_copy)->a.pubkey)
{
pERR_clear_error();
if (key_data(key_copy)->a.key)
{
pEC_KEY_free( key_data(key_copy)->a.key );
key_data(key_copy)->a.key = NULL;
}
return STATUS_NO_MEMORY;
}
}
break;
case ALG_ID_DSA:
if (key_data(key_orig)->a.key)
{
DSA *orig = key_data(key_orig)->a.key;
DSA *copy = pDSA_new();
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
BIGNUM *cp, *cq, *cg, *cpub, *cpriv;
if (!copy) return STATUS_NO_MEMORY;
pDSA_get0_pqg( orig, &p, &q, &g );
pDSA_get0_key( orig, &pub_key, &priv_key );
cp = p ? pBN_dup( p ) : NULL;
cq = q ? pBN_dup( q ) : NULL;
cg = g ? pBN_dup( g ) : NULL;
cpub = pub_key ? pBN_dup( pub_key ) : NULL;
cpriv = priv_key ? pBN_dup( priv_key ) : NULL;
if ((p && !cp) || (q && !cq) || (g && !cg) || !pDSA_set0_pqg( copy, cp, cq, cg ))
{
pERR_clear_error();
if (cp) pBN_free( cp );
if (cq) pBN_free( cq );
if (cg) pBN_free( cg );
if (cpub) pBN_free( cpub );
if (cpriv) ossl_bn_clear_free( cpriv );
pDSA_free( copy );
return STATUS_NO_MEMORY;
}
if ((pub_key || priv_key) &&
((pub_key && !cpub) || (priv_key && !cpriv) || !pDSA_set0_key( copy, cpub, cpriv )))
{
pERR_clear_error();
if (cpub) pBN_free( cpub );
if (cpriv) ossl_bn_clear_free( cpriv );
pDSA_free( copy );
return STATUS_NO_MEMORY;
}
key_data(key_copy)->a.key = copy;
}
if (key_data(key_orig)->a.pubkey)
{
DSA *orig = key_data(key_orig)->a.pubkey;
DSA *copy = pDSA_new();
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
BIGNUM *cp, *cq, *cg, *cpub, *cpriv;
if (!copy)
{
if (key_data(key_copy)->a.key) { pDSA_free( key_data(key_copy)->a.key ); key_data(key_copy)->a.key = NULL; }
return STATUS_NO_MEMORY;
}
pDSA_get0_pqg( orig, &p, &q, &g );
pDSA_get0_key( orig, &pub_key, &priv_key );
cp = p ? pBN_dup( p ) : NULL;
cq = q ? pBN_dup( q ) : NULL;
cg = g ? pBN_dup( g ) : NULL;
cpub = pub_key ? pBN_dup( pub_key ) : NULL;
cpriv = priv_key ? pBN_dup( priv_key ) : NULL;
if ((p && !cp) || (q && !cq) || (g && !cg) || !pDSA_set0_pqg( copy, cp, cq, cg ))
{
pERR_clear_error();
if (cp) pBN_free( cp );
if (cq) pBN_free( cq );
if (cg) pBN_free( cg );
if (cpub) pBN_free( cpub );
if (cpriv) ossl_bn_clear_free( cpriv );
if (key_data(key_copy)->a.key) { pDSA_free( key_data(key_copy)->a.key ); key_data(key_copy)->a.key = NULL; }
pDSA_free( copy );
return STATUS_NO_MEMORY;
}
if ((pub_key || priv_key) &&
((pub_key && !cpub) || (priv_key && !cpriv) || !pDSA_set0_key( copy, cpub, cpriv )))
{
pERR_clear_error();
if (cpub) pBN_free( cpub );
if (cpriv) ossl_bn_clear_free( cpriv );
if (key_data(key_copy)->a.key) { pDSA_free( key_data(key_copy)->a.key ); key_data(key_copy)->a.key = NULL; }
pDSA_free( copy );
return STATUS_NO_MEMORY;
}
key_data(key_copy)->a.pubkey = copy;
}
key_copy->u.a.dss_seed = key_orig->u.a.dss_seed;
break;
case ALG_ID_DH:
if (key_data(key_orig)->a.key)
{
DH *orig = key_data(key_orig)->a.key;
DH *copy = pDH_new();
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
BIGNUM *cp, *cq, *cg, *cpub, *cpriv;
if (!copy) return STATUS_NO_MEMORY;
pDH_get0_pqg( orig, &p, &q, &g );
pDH_get0_key( orig, &pub_key, &priv_key );
cp = p ? pBN_dup( p ) : NULL;
cq = q ? pBN_dup( q ) : NULL;
cg = g ? pBN_dup( g ) : NULL;
cpub = pub_key ? pBN_dup( pub_key ) : NULL;
cpriv = priv_key ? pBN_dup( priv_key ) : NULL;
if ((p && !cp) || (q && !cq) || (g && !cg) || !pDH_set0_pqg( copy, cp, cq, cg ))
{
pERR_clear_error();
if (cp) pBN_free( cp );
if (cq) pBN_free( cq );
if (cg) pBN_free( cg );
if (cpub) pBN_free( cpub );
if (cpriv) ossl_bn_clear_free( cpriv );
pDH_free( copy );
return STATUS_NO_MEMORY;
}
if ((pub_key || priv_key) &&
((pub_key && !cpub) || (priv_key && !cpriv) || !pDH_set0_key( copy, cpub, cpriv )))
{
pERR_clear_error();
if (cpub) pBN_free( cpub );
if (cpriv) ossl_bn_clear_free( cpriv );
pDH_free( copy );
return STATUS_NO_MEMORY;
}
key_data(key_copy)->a.key = copy;
}
if (key_data(key_orig)->a.pubkey)
{
DH *orig = key_data(key_orig)->a.pubkey;
DH *copy = pDH_new();
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
BIGNUM *cp, *cq, *cg, *cpub, *cpriv;
if (!copy)
{
if (key_data(key_copy)->a.key) { pDH_free( key_data(key_copy)->a.key ); key_data(key_copy)->a.key = NULL; }
return STATUS_NO_MEMORY;
}
pDH_get0_pqg( orig, &p, &q, &g );
pDH_get0_key( orig, &pub_key, &priv_key );
cp = p ? pBN_dup( p ) : NULL;
cq = q ? pBN_dup( q ) : NULL;
cg = g ? pBN_dup( g ) : NULL;
cpub = pub_key ? pBN_dup( pub_key ) : NULL;
cpriv = priv_key ? pBN_dup( priv_key ) : NULL;
if ((p && !cp) || (q && !cq) || (g && !cg) || !pDH_set0_pqg( copy, cp, cq, cg ))
{
pERR_clear_error();
if (cp) pBN_free( cp );
if (cq) pBN_free( cq );
if (cg) pBN_free( cg );
if (cpub) pBN_free( cpub );
if (cpriv) ossl_bn_clear_free( cpriv );
if (key_data(key_copy)->a.key) { pDH_free( key_data(key_copy)->a.key ); key_data(key_copy)->a.key = NULL; }
pDH_free( copy );
return STATUS_NO_MEMORY;
}
if ((pub_key || priv_key) &&
((pub_key && !cpub) || (priv_key && !cpriv) || !pDH_set0_key( copy, cpub, cpriv )))
{
pERR_clear_error();
if (cpub) pBN_free( cpub );
if (cpriv) ossl_bn_clear_free( cpriv );
if (key_data(key_copy)->a.key) { pDH_free( key_data(key_copy)->a.key ); key_data(key_copy)->a.key = NULL; }
pDH_free( copy );
return STATUS_NO_MEMORY;
}
key_data(key_copy)->a.pubkey = copy;
}
break;
default:
return STATUS_NOT_SUPPORTED;
}
return STATUS_SUCCESS;
}
static NTSTATUS key_asymmetric_decrypt( void *args )
{
const struct key_asymmetric_decrypt_params *params = args;
struct key *key = params->key;
RSA *rsa = key_data(key)->a.key;
int padding, ret;
UCHAR stack_buf[512];
UCHAR *tmp = stack_buf;
ULONG key_bytes;
const BIGNUM *n = NULL;
if (!params->ret_len) return STATUS_INVALID_PARAMETER;
if (params->input_len > INT_MAX || params->output_len > INT_MAX) return STATUS_INVALID_PARAMETER;
if (!rsa) return STATUS_INVALID_HANDLE;
pRSA_get0_key( rsa, &n, NULL, NULL );
key_bytes = n ? (ULONG)bn_num_bytes( n ) : params->input_len;
if (key_bytes < params->input_len) key_bytes = params->input_len;
if (!key_bytes) return STATUS_INVALID_PARAMETER;
if (key_bytes > sizeof(stack_buf))
{
if (!(tmp = malloc( key_bytes )))
return STATUS_NO_MEMORY;
}
if (params->flags & BCRYPT_PAD_OAEP)
{
BCRYPT_OAEP_PADDING_INFO *pad = params->padding;
EVP_PKEY *pkey = NULL;
EVP_PKEY_CTX *pctx = NULL;
const EVP_MD *md;
size_t out_len = key_bytes;
int nid;
if (!pad || !pad->pszAlgId)
{
WARN( "padding info not found\n" );
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_INVALID_PARAMETER;
}
if ((nid = get_hash_nid( pad->pszAlgId )) == NID_undef || !(md = get_evp_md( nid )))
{
FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) );
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_NOT_SUPPORTED;
}
if ((pad->cbLabel && !pad->pbLabel) || pad->cbLabel > INT_MAX || !params->input)
{
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_INVALID_PARAMETER;
}
if (!pEVP_PKEY_set1_RSA)
{
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_NOT_SUPPORTED;
}
pERR_clear_error();
if (!(pkey = pEVP_PKEY_new()) || !pEVP_PKEY_set1_RSA( pkey, rsa ) ||
!(pctx = pEVP_PKEY_CTX_new( pkey, NULL )) ||
pEVP_PKEY_decrypt_init( pctx ) <= 0 ||
pEVP_PKEY_CTX_set_rsa_padding( pctx, RSA_PKCS1_OAEP_PADDING ) <= 0 ||
pEVP_PKEY_CTX_set_rsa_oaep_md( pctx, md ) <= 0 ||
pEVP_PKEY_CTX_set_rsa_mgf1_md( pctx, md ) <= 0)
{
pERR_clear_error();
if (pctx) pEVP_PKEY_CTX_free( pctx );
if (pkey) pEVP_PKEY_free( pkey );
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_INTERNAL_ERROR;
}
if (pad->cbLabel > 0)
{
UCHAR *label_copy = pCRYPTO_malloc( pad->cbLabel, __FILE__, __LINE__ );
if (!label_copy)
{
pEVP_PKEY_CTX_free( pctx );
pEVP_PKEY_free( pkey );
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_NO_MEMORY;
}
memcpy( label_copy, pad->pbLabel, pad->cbLabel );
if (pEVP_PKEY_CTX_set0_rsa_oaep_label( pctx, label_copy, pad->cbLabel ) <= 0)
{
pCRYPTO_free( label_copy, __FILE__, __LINE__ );
pERR_clear_error();
pEVP_PKEY_CTX_free( pctx );
pEVP_PKEY_free( pkey );
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_INTERNAL_ERROR;
}
}
if (pEVP_PKEY_decrypt( pctx, tmp, &out_len, params->input, params->input_len ) <= 0)
{
pERR_clear_error();
pEVP_PKEY_CTX_free( pctx );
pEVP_PKEY_free( pkey );
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_INTERNAL_ERROR;
}
pEVP_PKEY_CTX_free( pctx );
pEVP_PKEY_free( pkey );
*params->ret_len = out_len;
if (!params->output)
{
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_SUCCESS;
}
if (params->output_len < *params->ret_len)
{
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_BUFFER_TOO_SMALL;
}
memcpy( params->output, tmp, out_len );
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_SUCCESS;
}
if (!params->input)
{
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_INVALID_PARAMETER;
}
if (params->flags & BCRYPT_PAD_NONE)
padding = RSA_NO_PADDING;
else
padding = RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING;
pERR_clear_error();
ret = pRSA_private_decrypt( params->input_len, params->input, tmp, rsa, padding );
if (ret < 0 && padding == RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING &&
ERR_GET_REASON(pERR_peek_last_error()) == RSA_R_UNKNOWN_PADDING_TYPE)
{
pERR_clear_error();
ret = pRSA_private_decrypt( params->input_len, params->input, tmp, rsa, RSA_PKCS1_PADDING );
}
if (ret < 0)
{
pERR_clear_error();
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_INTERNAL_ERROR;
}
*params->ret_len = ret;
if (!params->output)
{
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_SUCCESS;
}
if (params->output_len < *params->ret_len)
{
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_BUFFER_TOO_SMALL;
}
memcpy( params->output, tmp, ret );
pOPENSSL_cleanse( tmp, key_bytes );
if (tmp != stack_buf) free( tmp );
return STATUS_SUCCESS;
}
static NTSTATUS key_asymmetric_encrypt( void *args )
{
const struct key_asymmetric_encrypt_params *params = args;
struct key *key = params->key;
RSA *rsa = key_data(key)->a.key ? key_data(key)->a.key : key_data(key)->a.pubkey;
int padding, ret;
const BIGNUM *n;
ULONG key_bytes;
if (!params->ret_len) return STATUS_INVALID_PARAMETER;
if (params->input_len > INT_MAX || params->output_len > INT_MAX) return STATUS_INVALID_PARAMETER;
if (!rsa) return STATUS_INVALID_HANDLE;
pRSA_get0_key( rsa, &n, NULL, NULL );
key_bytes = bn_num_bytes( n );
*params->ret_len = key_bytes;
if (!params->output)
return STATUS_SUCCESS;
if (params->output_len < *params->ret_len)
return STATUS_BUFFER_TOO_SMALL;
if (params->input_len && !params->input)
return STATUS_INVALID_PARAMETER;
if (params->flags & BCRYPT_PAD_OAEP)
{
BCRYPT_OAEP_PADDING_INFO *pad = params->padding;
const EVP_MD *md;
UCHAR *em;
const unsigned char *label = NULL;
int label_len = 0;
int nid;
/* Note: unlike the GnuTLS backend this does not require pbLabel to be
* non-NULL. Rejecting a NULL label would break label-less OAEP, which
* Windows accepts; gnutls.c looks simply wrong there. */
if (!pad || !pad->pszAlgId)
{
WARN( "padding info not found\n" );
return STATUS_INVALID_PARAMETER;
}
if (pad->cbLabel && !pad->pbLabel) return STATUS_INVALID_PARAMETER;
if ((nid = get_hash_nid( pad->pszAlgId )) == NID_undef || !(md = get_evp_md( nid )))
{
FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) );
return STATUS_NOT_SUPPORTED;
}
if (pad->cbLabel > INT_MAX) return STATUS_INVALID_PARAMETER;
label = pad->pbLabel;
label_len = pad->cbLabel;
if (!(em = malloc( key_bytes )))
return STATUS_NO_MEMORY;
ret = pRSA_padding_add_PKCS1_OAEP_mgf1( em, key_bytes, params->input, params->input_len,
label, label_len, md, md );
if (ret <= 0)
{
pERR_clear_error();
pOPENSSL_cleanse( em, key_bytes );
free( em );
return STATUS_INTERNAL_ERROR;
}
ret = pRSA_public_encrypt( key_bytes, em, params->output, rsa, RSA_NO_PADDING );
pOPENSSL_cleanse( em, key_bytes );
free( em );
if (ret < 0)
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
*params->ret_len = ret;
return STATUS_SUCCESS;
}
if (params->flags & BCRYPT_PAD_PKCS1)
padding = RSA_PKCS1_PADDING;
else if (params->flags & BCRYPT_PAD_NONE)
padding = RSA_NO_PADDING;
else
padding = RSA_PKCS1_PADDING;
ret = pRSA_public_encrypt( params->input_len, params->input, params->output, rsa, padding );
if (ret < 0)
{
pERR_clear_error();
return STATUS_INTERNAL_ERROR;
}
*params->ret_len = ret;
return STATUS_SUCCESS;
}
static NTSTATUS key_asymmetric_derive_key( void *args )
{
const struct key_asymmetric_derive_key_params *params = args;
struct key *privkey = params->privkey;
struct key *pubkey = params->pubkey;
if (!privkey || !pubkey) return STATUS_INVALID_PARAMETER;
if (privkey->alg_id != pubkey->alg_id) return STATUS_INVALID_PARAMETER;
if (is_x25519_key( privkey ))
{
EVP_PKEY *priv_pkey = key_data(privkey)->a.key;
EVP_PKEY *pub_pkey = key_data(pubkey)->a.key ? key_data(pubkey)->a.key : key_data(pubkey)->a.pubkey;
EVP_PKEY_CTX *ctx;
size_t secret_len = 32;
if (!priv_pkey || !pub_pkey) return STATUS_INVALID_PARAMETER;
*params->ret_len = 32;
if (!params->output) return STATUS_SUCCESS;
if (params->output_len < 32) return STATUS_BUFFER_TOO_SMALL;
if (!(ctx = pEVP_PKEY_CTX_new( priv_pkey, NULL ))) return STATUS_NO_MEMORY;
if (pEVP_PKEY_derive_init( ctx ) <= 0 ||
pEVP_PKEY_derive_set_peer( ctx, pub_pkey ) <= 0 ||
pEVP_PKEY_derive( ctx, params->output, &secret_len ) <= 0)
{
pERR_clear_error();
pEVP_PKEY_CTX_free( ctx );
return STATUS_INTERNAL_ERROR;
}
pEVP_PKEY_CTX_free( ctx );
*params->ret_len = secret_len;
return STATUS_SUCCESS;
}
if (privkey->alg_id == ALG_ID_ECDH || privkey->alg_id == ALG_ID_ECDH_P256 ||
privkey->alg_id == ALG_ID_ECDH_P384 || privkey->alg_id == ALG_ID_ECDH_P521)
{
EC_KEY *priv_ec = key_data(privkey)->a.key;
EC_KEY *pub_ec = key_data(pubkey)->a.key ? key_data(pubkey)->a.key : key_data(pubkey)->a.pubkey;
const EC_POINT *point;
ULONG expected_len = 0;
UCHAR stack_buf[66];
int ret;
get_ecc_curve_nid( privkey, &expected_len );
if (!expected_len) expected_len = len_from_bitlen( privkey->u.a.bitlen );
if (!priv_ec || !pub_ec) return STATUS_INVALID_PARAMETER;
point = pEC_KEY_get0_public_key( pub_ec );
if (!point) return STATUS_INVALID_PARAMETER;
*params->ret_len = expected_len;
if (!params->output)
return STATUS_SUCCESS;
if (params->output_len < expected_len)
return STATUS_BUFFER_TOO_SMALL;
if (!pECDH_compute_key) return STATUS_NOT_SUPPORTED;
ret = pECDH_compute_key( stack_buf, sizeof(stack_buf), point, priv_ec, NULL );
if (ret <= 0)
{
pERR_clear_error();
pOPENSSL_cleanse( stack_buf, sizeof(stack_buf) );
return STATUS_INTERNAL_ERROR;
}
if ((ULONG)ret < expected_len)
{
memset( params->output, 0, expected_len - ret );
memcpy( params->output + (expected_len - ret), stack_buf, ret );
}
else
{
memcpy( params->output, stack_buf, expected_len );
}
pOPENSSL_cleanse( stack_buf, sizeof(stack_buf) );
return STATUS_SUCCESS;
}
else if (privkey->alg_id == ALG_ID_DH)
{
DH *priv_dh = key_data(privkey)->a.key;
DH *pub_dh = key_data(pubkey)->a.key ? key_data(pubkey)->a.key : key_data(pubkey)->a.pubkey;
const BIGNUM *pub_bn = NULL, *p1 = NULL, *q1 = NULL, *g1 = NULL, *p2 = NULL, *q2 = NULL, *g2 = NULL;
ULONG dh_size, buf_size;
UCHAR stack_buf[512];
UCHAR *tmp = stack_buf;
int check_codes = 0;
int ret;
if (!priv_dh || !pub_dh) return STATUS_INVALID_PARAMETER;
pDH_get0_pqg( priv_dh, &p1, &q1, &g1 );
pDH_get0_pqg( pub_dh, &p2, &q2, &g2 );
if (!p1 || !p2 || !g1 || !g2 || pBN_cmp( p1, p2 ) != 0 || pBN_cmp( g1, g2 ) != 0)
return STATUS_INVALID_PARAMETER;
if (q1 && q2 && pBN_cmp( q1, q2 ) != 0)
return STATUS_INVALID_PARAMETER;
pDH_get0_key( pub_dh, &pub_bn, NULL );
if (!pub_bn) return STATUS_INVALID_PARAMETER;
if (pDH_check_pub_key && (!pDH_check_pub_key( priv_dh, pub_bn, &check_codes ) || check_codes != 0))
return STATUS_INVALID_PARAMETER;
*params->ret_len = privkey->u.a.bitlen / 8;
if (!params->output)
return STATUS_SUCCESS;
if (params->output_len < *params->ret_len)
return STATUS_BUFFER_TOO_SMALL;
dh_size = bn_num_bytes( p1 );
buf_size = max( *params->ret_len, dh_size );
if (!buf_size) return STATUS_INVALID_PARAMETER;
if (buf_size > sizeof(stack_buf))
{
if (!(tmp = malloc( buf_size )))
return STATUS_NO_MEMORY;
}
ret = pDH_compute_key( tmp, pub_bn, priv_dh );
if (ret <= 0)
{
pERR_clear_error();
pOPENSSL_cleanse( tmp, buf_size );
if (tmp != stack_buf) free( tmp );
return STATUS_INTERNAL_ERROR;
}
if ((ULONG)ret < *params->ret_len)
{
memset( params->output, 0, *params->ret_len - ret );
memcpy( params->output + (*params->ret_len - ret), tmp, ret );
}
else
{
memcpy( params->output, tmp, *params->ret_len );
}
pOPENSSL_cleanse( tmp, buf_size );
if (tmp != stack_buf)
{
free( tmp );
}
return STATUS_SUCCESS;
}
return STATUS_NOT_SUPPORTED;
}
const unixlib_entry_t __wine_unix_call_funcs[] =
{
process_attach,
process_detach,
key_symmetric_vector_reset,
key_symmetric_set_auth_data,
key_symmetric_encrypt,
key_symmetric_decrypt,
key_symmetric_get_tag,
key_symmetric_destroy,
key_asymmetric_generate,
key_asymmetric_decrypt,
key_asymmetric_encrypt,
key_asymmetric_duplicate,
key_asymmetric_sign,
key_asymmetric_verify,
key_asymmetric_destroy,
key_asymmetric_export,
key_asymmetric_import,
key_asymmetric_derive_key,
};
C_ASSERT( ARRAYSIZE(__wine_unix_call_funcs) == unix_funcs_count );
#ifdef _WIN64
typedef ULONG PTR32;
struct key_symmetric32
{
enum chain_mode mode;
ULONG block_size;
PTR32 vector;
ULONG vector_len;
PTR32 secret;
ULONG secret_len;
ULONG __cs[6];
};
struct key_asymmetric32
{
ULONG bitlen; /* ignored for ECC keys */
enum ecc_curve_id curve_id;
ULONG flags;
DSSSEED dss_seed;
};
struct key32
{
struct object hdr;
enum alg_id alg_id;
UINT64 private[PRIVATE_DATA_SIZE]; /* private data for backend */
union
{
struct key_symmetric32 s;
struct key_asymmetric32 a;
} u;
};
union padding
{
BCRYPT_OAEP_PADDING_INFO oaep;
BCRYPT_PKCS1_PADDING_INFO pkcs1;
BCRYPT_PSS_PADDING_INFO pss;
};
union padding32
{
struct
{
PTR32 pszAlgId;
PTR32 pbLabel;
ULONG cbLabel;
} oaep;
struct
{
PTR32 pszAlgId;
} pkcs1;
struct
{
PTR32 pszAlgId;
ULONG cbSalt;
} pss;
};
static union padding *get_padding( union padding32 *padding32, union padding *padding, ULONG flags)
{
if (!padding32) return NULL;
switch (flags)
{
case BCRYPT_PAD_OAEP:
padding->oaep.pszAlgId = ULongToPtr( padding32->oaep.pszAlgId );
padding->oaep.pbLabel = ULongToPtr( padding32->oaep.pbLabel );
padding->oaep.cbLabel = padding32->oaep.cbLabel;
return padding;
case BCRYPT_PAD_PKCS1:
padding->pkcs1.pszAlgId = ULongToPtr( padding32->pkcs1.pszAlgId );
return padding;
case BCRYPT_PAD_PSS:
padding->pss.pszAlgId = ULongToPtr( padding32->pss.pszAlgId );
padding->pss.cbSalt = padding32->pss.cbSalt;
return padding;
default:
break;
}
return NULL;
}
static struct key *get_symmetric_key( struct key32 *key32, struct key *key )
{
key->hdr = key32->hdr;
key->alg_id = key32->alg_id;
memcpy( key->private, key32->private, sizeof(key->private) );
key->u.s.mode = key32->u.s.mode;
key->u.s.block_size = key32->u.s.block_size;
key->u.s.vector = ULongToPtr(key32->u.s.vector);
key->u.s.vector_len = key32->u.s.vector_len;
key->u.s.secret = ULongToPtr(key32->u.s.secret);
key->u.s.secret_len = key32->u.s.secret_len;
return key;
}
static struct key *get_asymmetric_key( struct key32 *key32, struct key *key )
{
key->hdr = key32->hdr;
key->alg_id = key32->alg_id;
memcpy( key->private, key32->private, sizeof(key->private) );
key->u.a.bitlen = key32->u.a.bitlen;
key->u.a.curve_id = key32->u.a.curve_id;
key->u.a.flags = key32->u.a.flags;
key->u.a.dss_seed = key32->u.a.dss_seed;
return key;
}
static void put_symmetric_key32( struct key *key, struct key32 *key32 )
{
memcpy( key32->private, key->private, sizeof(key32->private) );
}
static void put_asymmetric_key32( struct key *key, struct key32 *key32 )
{
memcpy( key32->private, key->private, sizeof(key32->private) );
key32->u.a.bitlen = key->u.a.bitlen;
key32->u.a.curve_id = key->u.a.curve_id;
key32->u.a.flags = key->u.a.flags;
key32->u.a.dss_seed = key->u.a.dss_seed;
}
static NTSTATUS wow64_key_symmetric_vector_reset( void *args )
{
NTSTATUS ret;
struct key key;
struct key32 *key32 = args;
ret = key_symmetric_vector_reset( get_symmetric_key( key32, &key ));
put_symmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_symmetric_set_auth_data( void *args )
{
struct
{
PTR32 key;
PTR32 auth_data;
ULONG len;
} const *params32 = args;
NTSTATUS ret;
struct key key;
struct key32 *key32 = ULongToPtr( params32->key );
struct key_symmetric_set_auth_data_params params =
{
get_symmetric_key( key32, &key ),
ULongToPtr(params32->auth_data),
params32->len
};
ret = key_symmetric_set_auth_data( &params );
put_symmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_symmetric_encrypt( void *args )
{
struct
{
PTR32 key;
PTR32 input;
ULONG input_len;
PTR32 output;
ULONG output_len;
} const *params32 = args;
NTSTATUS ret;
struct key key;
struct key32 *key32 = ULongToPtr( params32->key );
struct key_symmetric_encrypt_params params =
{
get_symmetric_key( key32, &key ),
ULongToPtr(params32->input),
params32->input_len,
ULongToPtr(params32->output),
params32->output_len
};
ret = key_symmetric_encrypt( &params );
put_symmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_symmetric_decrypt( void *args )
{
struct
{
PTR32 key;
PTR32 input;
ULONG input_len;
PTR32 output;
ULONG output_len;
} const *params32 = args;
NTSTATUS ret;
struct key key;
struct key32 *key32 = ULongToPtr( params32->key );
struct key_symmetric_decrypt_params params =
{
get_symmetric_key( key32, &key ),
ULongToPtr(params32->input),
params32->input_len,
ULongToPtr(params32->output),
params32->output_len
};
ret = key_symmetric_decrypt( &params );
put_symmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_symmetric_get_tag( void *args )
{
struct
{
PTR32 key;
PTR32 tag;
ULONG len;
} const *params32 = args;
NTSTATUS ret;
struct key key;
struct key32 *key32 = ULongToPtr( params32->key );
struct key_symmetric_get_tag_params params =
{
get_symmetric_key( key32, &key ),
ULongToPtr(params32->tag),
params32->len
};
ret = key_symmetric_get_tag( &params );
put_symmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_symmetric_destroy( void *args )
{
struct key32 *key32 = args;
struct key key;
NTSTATUS ret;
ret = key_symmetric_destroy( get_symmetric_key( key32, &key ));
put_symmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_asymmetric_generate( void *args )
{
struct key32 *key32 = args;
struct key key;
NTSTATUS ret;
ret = key_asymmetric_generate( get_asymmetric_key( key32, &key ));
put_asymmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_asymmetric_decrypt( void *args )
{
struct
{
PTR32 key;
PTR32 input;
ULONG input_len;
PTR32 padding;
PTR32 output;
ULONG output_len;
PTR32 ret_len;
ULONG flags;
} const *params32 = args;
NTSTATUS ret;
struct key key;
union padding padding;
struct key32 *key32 = ULongToPtr( params32->key );
struct key_asymmetric_decrypt_params params =
{
get_asymmetric_key( key32, &key ),
ULongToPtr(params32->input),
params32->input_len,
get_padding( ULongToPtr(params32->padding), &padding, params32->flags ),
ULongToPtr(params32->output),
params32->output_len,
ULongToPtr(params32->ret_len),
params32->flags
};
ret = key_asymmetric_decrypt( &params );
put_asymmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_asymmetric_encrypt( void *args )
{
struct
{
PTR32 key;
PTR32 input;
ULONG input_len;
PTR32 padding;
PTR32 output;
ULONG output_len;
PTR32 ret_len;
ULONG flags;
} const *params32 = args;
NTSTATUS ret;
struct key key;
union padding padding;
struct key32 *key32 = ULongToPtr( params32->key );
struct key_asymmetric_encrypt_params params =
{
get_asymmetric_key( key32, &key ),
ULongToPtr(params32->input),
params32->input_len,
get_padding( ULongToPtr(params32->padding), &padding, params32->flags ),
ULongToPtr(params32->output),
params32->output_len,
ULongToPtr(params32->ret_len),
params32->flags
};
ret = key_asymmetric_encrypt( &params );
put_asymmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_asymmetric_duplicate( void *args )
{
struct
{
PTR32 key_orig;
PTR32 key_copy;
} const *params32 = args;
NTSTATUS ret;
struct key key_orig, key_copy;
struct key32 *key_orig32 = ULongToPtr( params32->key_orig );
struct key32 *key_copy32 = ULongToPtr( params32->key_copy );
struct key_asymmetric_duplicate_params params =
{
get_asymmetric_key( key_orig32, &key_orig ),
get_asymmetric_key( key_copy32, &key_copy )
};
ret = key_asymmetric_duplicate( &params );
put_asymmetric_key32( &key_copy, key_copy32 );
return ret;
}
static NTSTATUS wow64_key_asymmetric_sign( void *args )
{
struct
{
PTR32 key;
PTR32 padding;
PTR32 input;
ULONG input_len;
PTR32 output;
ULONG output_len;
PTR32 ret_len;
ULONG flags;
} const *params32 = args;
NTSTATUS ret;
struct key key;
union padding padding;
struct key32 *key32 = ULongToPtr( params32->key );
struct key_asymmetric_sign_params params =
{
get_asymmetric_key( key32, &key ),
get_padding(ULongToPtr( params32->padding ), &padding, params32->flags),
ULongToPtr(params32->input),
params32->input_len,
ULongToPtr(params32->output),
params32->output_len,
ULongToPtr(params32->ret_len),
params32->flags
};
ret = key_asymmetric_sign( &params );
put_asymmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_asymmetric_verify( void *args )
{
struct
{
PTR32 key;
PTR32 padding;
PTR32 hash;
ULONG hash_len;
PTR32 signature;
ULONG signature_len;
ULONG flags;
} const *params32 = args;
NTSTATUS ret;
struct key key;
union padding padding;
struct key32 *key32 = ULongToPtr( params32->key );
struct key_asymmetric_verify_params params =
{
get_asymmetric_key( key32, &key ),
get_padding(ULongToPtr( params32->padding ), &padding, params32->flags),
ULongToPtr(params32->hash),
params32->hash_len,
ULongToPtr(params32->signature),
params32->signature_len,
params32->flags
};
ret = key_asymmetric_verify( &params );
put_asymmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_asymmetric_destroy( void *args )
{
struct key32 *key32 = args;
struct key key;
NTSTATUS ret;
ret = key_asymmetric_destroy( get_asymmetric_key( key32, &key ));
put_asymmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_asymmetric_export( void *args )
{
struct
{
PTR32 key;
ULONG flags;
PTR32 buf;
ULONG len;
PTR32 ret_len;
} const *params32 = args;
NTSTATUS ret;
struct key key;
struct key32 *key32 = ULongToPtr( params32->key );
struct key_asymmetric_export_params params =
{
get_asymmetric_key( key32, &key ),
params32->flags,
ULongToPtr(params32->buf),
params32->len,
ULongToPtr(params32->ret_len),
};
ret = key_asymmetric_export( &params );
put_asymmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_asymmetric_import( void *args )
{
struct
{
PTR32 key;
ULONG flags;
PTR32 buf;
ULONG len;
} const *params32 = args;
NTSTATUS ret;
struct key key;
struct key32 *key32 = ULongToPtr( params32->key );
struct key_asymmetric_import_params params =
{
get_asymmetric_key( key32, &key ),
params32->flags,
ULongToPtr(params32->buf),
params32->len
};
ret = key_asymmetric_import( &params );
put_asymmetric_key32( &key, key32 );
return ret;
}
static NTSTATUS wow64_key_asymmetric_derive_key( void *args )
{
struct
{
PTR32 privkey;
PTR32 pubkey;
PTR32 output;
ULONG output_len;
PTR32 ret_len;
} const *params32 = args;
NTSTATUS ret;
struct key privkey, pubkey;
struct key32 *privkey32 = ULongToPtr( params32->privkey );
struct key32 *pubkey32 = ULongToPtr( params32->pubkey );
struct key_asymmetric_derive_key_params params =
{
get_asymmetric_key( privkey32, &privkey ),
get_asymmetric_key( pubkey32, &pubkey ),
ULongToPtr(params32->output),
params32->output_len,
ULongToPtr(params32->ret_len),
};
ret = key_asymmetric_derive_key( &params );
put_asymmetric_key32( &privkey, privkey32 );
put_asymmetric_key32( &pubkey, pubkey32 );
return ret;
}
const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
{
process_attach,
process_detach,
wow64_key_symmetric_vector_reset,
wow64_key_symmetric_set_auth_data,
wow64_key_symmetric_encrypt,
wow64_key_symmetric_decrypt,
wow64_key_symmetric_get_tag,
wow64_key_symmetric_destroy,
wow64_key_asymmetric_generate,
wow64_key_asymmetric_decrypt,
wow64_key_asymmetric_encrypt,
wow64_key_asymmetric_duplicate,
wow64_key_asymmetric_sign,
wow64_key_asymmetric_verify,
wow64_key_asymmetric_destroy,
wow64_key_asymmetric_export,
wow64_key_asymmetric_import,
wow64_key_asymmetric_derive_key,
};
C_ASSERT( ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_funcs_count );
#endif /* _WIN64 */
#endif /* HAVE_OPENSSL */