| /* | |
| * 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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( ¶ms ); | |
| 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 */ |