diff --git a/dll/3rdparty/mbedtls/CMakeLists.txt b/dll/3rdparty/mbedtls/CMakeLists.txt index a3cc7ab97a8..2d8d847d57c 100644 --- a/dll/3rdparty/mbedtls/CMakeLists.txt +++ b/dll/3rdparty/mbedtls/CMakeLists.txt @@ -27,6 +27,7 @@ list(APPEND SOURCE #debug.c des.c dhm.c + dsa.c ecdh.c ecdsa.c ecp.c @@ -38,8 +39,8 @@ list(APPEND SOURCE #havege.c #hkdf.c hmac_drbg.c - #md2.c - #md4.c + md2.c + md4.c md5.c md.c md_wrap.c diff --git a/dll/3rdparty/mbedtls/cipher_wrap.c b/dll/3rdparty/mbedtls/cipher_wrap.c index 5973ca6ba25..dc841e7c6be 100644 --- a/dll/3rdparty/mbedtls/cipher_wrap.c +++ b/dll/3rdparty/mbedtls/cipher_wrap.c @@ -173,6 +173,15 @@ static int aes_crypt_cfb128_wrap( void *ctx, mbedtls_operation_t operation, return mbedtls_aes_crypt_cfb128( (mbedtls_aes_context *) ctx, operation, length, iv_off, iv, input, output ); } +#ifdef __REACTOS__ +static int aes_crypt_cfb8_wrap( void *ctx, mbedtls_operation_t operation, + size_t length, size_t *iv_off, unsigned char *iv, + const unsigned char *input, unsigned char *output ) +{ + return mbedtls_aes_crypt_cfb8( (mbedtls_aes_context *) ctx, operation, length, iv, + input, output ); +} +#endif #endif /* MBEDTLS_CIPHER_MODE_CFB */ #if defined(MBEDTLS_CIPHER_MODE_OFB) @@ -278,6 +287,35 @@ static const mbedtls_cipher_base_t aes_info = { aes_ctx_free }; +#ifdef __REACTOS__ +static const mbedtls_cipher_base_t cfb8_aes_info = { + MBEDTLS_CIPHER_ID_AES, + aes_crypt_ecb_wrap, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + aes_crypt_cbc_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + aes_crypt_cfb8_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + aes_crypt_ofb_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + aes_crypt_ctr_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + aes_setkey_enc_wrap, + aes_setkey_dec_wrap, + aes_ctx_alloc, + aes_ctx_free +}; +#endif + static const mbedtls_cipher_info_t aes_128_ecb_info = { MBEDTLS_CIPHER_AES_128_ECB, MBEDTLS_MODE_ECB, @@ -347,6 +385,41 @@ static const mbedtls_cipher_info_t aes_256_cbc_info = { #endif /* MBEDTLS_CIPHER_MODE_CBC */ #if defined(MBEDTLS_CIPHER_MODE_CFB) +#ifdef __REACTOS__ +static const mbedtls_cipher_info_t aes_128_cfb8_info = { + MBEDTLS_CIPHER_AES_128_CFB8, + MBEDTLS_MODE_CFB, + 128, + "AES-128-CFB8", + 16, + 0, + 16, + &cfb8_aes_info +}; + +static const mbedtls_cipher_info_t aes_192_cfb8_info = { + MBEDTLS_CIPHER_AES_192_CFB8, + MBEDTLS_MODE_CFB, + 192, + "AES-192-CFB8", + 16, + 0, + 16, + &cfb8_aes_info +}; + +static const mbedtls_cipher_info_t aes_256_cfb8_info = { + MBEDTLS_CIPHER_AES_256_CFB8, + MBEDTLS_MODE_CFB, + 256, + "AES-256-CFB8", + 16, + 0, + 16, + &cfb8_aes_info +}; +#endif + static const mbedtls_cipher_info_t aes_128_cfb128_info = { MBEDTLS_CIPHER_AES_128_CFB128, MBEDTLS_MODE_CFB, @@ -2156,6 +2229,11 @@ const mbedtls_cipher_definition_t mbedtls_cipher_definitions[] = { MBEDTLS_CIPHER_AES_256_CBC, &aes_256_cbc_info }, #endif #if defined(MBEDTLS_CIPHER_MODE_CFB) +#ifdef __REACTOS__ + { MBEDTLS_CIPHER_AES_128_CFB8, &aes_128_cfb8_info }, + { MBEDTLS_CIPHER_AES_192_CFB8, &aes_192_cfb8_info }, + { MBEDTLS_CIPHER_AES_256_CFB8, &aes_256_cfb8_info }, +#endif { MBEDTLS_CIPHER_AES_128_CFB128, &aes_128_cfb128_info }, { MBEDTLS_CIPHER_AES_192_CFB128, &aes_192_cfb128_info }, { MBEDTLS_CIPHER_AES_256_CFB128, &aes_256_cfb128_info }, diff --git a/dll/3rdparty/mbedtls/dhm.c b/dll/3rdparty/mbedtls/dhm.c index 535b698ce64..1caac2e6f74 100644 --- a/dll/3rdparty/mbedtls/dhm.c +++ b/dll/3rdparty/mbedtls/dhm.c @@ -452,9 +452,10 @@ int mbedtls_dhm_calc_secret( mbedtls_dhm_context *ctx, if( output_size < ctx->len ) return( MBEDTLS_ERR_DHM_BAD_INPUT_DATA ); +#ifndef __REACTOS__ if( ( ret = dhm_check_range( &ctx->GY, &ctx->P ) ) != 0 ) return( ret ); - +#endif mbedtls_mpi_init( &GYb ); /* Blind peer's value */ diff --git a/dll/3rdparty/mbedtls/dsa.c b/dll/3rdparty/mbedtls/dsa.c new file mode 100644 index 00000000000..12f88028d8c --- /dev/null +++ b/dll/3rdparty/mbedtls/dsa.c @@ -0,0 +1,786 @@ +/* + * Digital Signature Algorithm (DSA) + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_DSA_C) + +#include "mbedtls/dsa.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/platform_util.h" + +#include + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include +#include +#define mbedtls_printf printf +#define mbedtls_calloc calloc +#define mbedtls_free free +#endif + +#if !defined(MBEDTLS_DSA_ALT) + +#define DSA_VALIDATE_RET( cond ) \ + MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_DSA_BAD_INPUT_DATA ) +#define DSA_VALIDATE( cond ) \ + MBEDTLS_INTERNAL_VALIDATE( cond ) + +/* + * Initialize context + */ +void mbedtls_dsa_init( mbedtls_dsa_context *ctx ) +{ + DSA_VALIDATE( ctx != NULL ); + memset( ctx, 0, sizeof( mbedtls_dsa_context ) ); + + mbedtls_mpi_init( &ctx->P ); + mbedtls_mpi_init( &ctx->Q ); + mbedtls_mpi_init( &ctx->G ); + mbedtls_mpi_init( &ctx->Y ); + mbedtls_mpi_init( &ctx->X ); +} + +/* + * Free context + */ +void mbedtls_dsa_free( mbedtls_dsa_context *ctx ) +{ + if( ctx == NULL ) + return; + + mbedtls_mpi_free( &ctx->P ); + mbedtls_mpi_free( &ctx->Q ); + mbedtls_mpi_free( &ctx->G ); + mbedtls_mpi_free( &ctx->Y ); + mbedtls_mpi_free( &ctx->X ); + + mbedtls_platform_zeroize( ctx, sizeof( mbedtls_dsa_context ) ); +} + +/* + * Set group parameters + */ +int mbedtls_dsa_set_group( mbedtls_dsa_context *ctx, const mbedtls_mpi *P, const mbedtls_mpi *Q, const mbedtls_mpi *G ) +{ + int ret; + DSA_VALIDATE_RET( ctx != NULL ); + DSA_VALIDATE_RET( P != NULL ); + DSA_VALIDATE_RET( Q != NULL ); + DSA_VALIDATE_RET( G != NULL ); + + if( ( ret = mbedtls_mpi_copy( &ctx->P, P ) ) != 0 ) + { + return( MBEDTLS_ERR_DSA_BAD_INPUT_DATA + ret ); + } + + if( ( ret = mbedtls_mpi_copy( &ctx->Q, Q ) ) != 0 ) + { + return( MBEDTLS_ERR_DSA_BAD_INPUT_DATA + ret ); + } + + if( ( ret = mbedtls_mpi_copy( &ctx->G, G ) ) != 0 ) + { + return( MBEDTLS_ERR_DSA_BAD_INPUT_DATA + ret ); + } + + if (!ctx->len) ctx->len = mbedtls_mpi_size( &ctx->P ); + + return( 0 ); +} + +/** + * Compute a power of two + * @param a The integer to store the power in + * @param n The power of two you want to store (a = 2^n) + * @return 0 on success + */ +int mpi_2expt(mbedtls_mpi *a, int n) +{ + int ret; + + DSA_VALIDATE_RET( a != NULL ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_lset(a, 0) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit(a, n, 1) ); + +cleanup: + return ret; +} + +/* + * Generate keypair + */ +int mbedtls_dsa_genkey( mbedtls_dsa_context *ctx, size_t group_size, size_t modulus_size, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + int ret; + unsigned long L, N, n, outbytes, seedbytes, counter, j, i; + int mr_tests_q, mr_tests_p, found_p, found_q; + mbedtls_mpi t2L1, t2N1, t2seedlen, U, U1, W, W1, X, q, q1, p, p1, e, h, h1, g, c, t2q, seedinc, seedinc1; + unsigned char *wbuf, *sbuf, digest[MBEDTLS_DSA_MAX_GROUP]; + const mbedtls_md_info_t * md_info; + + DSA_VALIDATE_RET( ctx != NULL ); + DSA_VALIDATE_RET( f_rng != NULL ); + + /* check size */ + DSA_VALIDATE_RET( group_size <= MBEDTLS_DSA_MAX_GROUP ); + DSA_VALIDATE_RET( group_size >= 1 ); + DSA_VALIDATE_RET( group_size < modulus_size ); + DSA_VALIDATE_RET( modulus_size <= MBEDTLS_DSA_MAX_MODULUS ); + +#if !defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_SHA256_C) + return MBEDTLS_ERR_DSA_BAD_INPUT_DATA; +#endif + +#if defined(MBEDTLS_SHA512_C) + md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); +#else + md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); +#endif + + seedbytes = group_size; + L = modulus_size * 8; + N = group_size * 8; + if (N > mbedtls_md_get_size(md_info) * 8) return MBEDTLS_ERR_DSA_BAD_INPUT_DATA; /* group_size too big */ + + outbytes = mbedtls_md_get_size(md_info); + n = ((L + outbytes*8 - 1) / (outbytes*8)) - 1; + + if ((wbuf = mbedtls_calloc(1, (n+1)*outbytes)) == NULL) { ret = MBEDTLS_ERR_MPI_ALLOC_FAILED; goto cleanup3; } + if ((sbuf = mbedtls_calloc(1, seedbytes)) == NULL) { ret = MBEDTLS_ERR_MPI_ALLOC_FAILED; goto cleanup2; } + + mbedtls_mpi_init( &t2L1 ); + mbedtls_mpi_init( &t2N1 ); + mbedtls_mpi_init( &t2seedlen ); + mbedtls_mpi_init( &U ); + mbedtls_mpi_init( &U1 ); + mbedtls_mpi_init( &W ); + mbedtls_mpi_init( &W1 ); + mbedtls_mpi_init( &X ); + mbedtls_mpi_init( &q ); + mbedtls_mpi_init( &q1 ); + mbedtls_mpi_init( &p ); + mbedtls_mpi_init( &p1 ); + mbedtls_mpi_init( &e ); + mbedtls_mpi_init( &h ); + mbedtls_mpi_init( &h1 ); + mbedtls_mpi_init( &g ); + mbedtls_mpi_init( &c ); + mbedtls_mpi_init( &t2q ); + mbedtls_mpi_init( &seedinc ); + mbedtls_mpi_init( &seedinc1 ); + + /* M-R tests (without Lucas test) according FIPS-186-4 - Appendix C.3 - table C.1 */ + if (L <= 1024) { mr_tests_p = 40; } + else if (L <= 2048) { mr_tests_p = 56; } + else { mr_tests_p = 64; } + + if (N <= 160) { mr_tests_q = 40; } + else if (N <= 224) { mr_tests_q = 56; } + else { mr_tests_q = 64; } + + /* t2L1 = 2^(L-1) */ + MBEDTLS_MPI_CHK( mpi_2expt(&t2L1, L-1) ); + /* t2N1 = 2^(N-1) */ + MBEDTLS_MPI_CHK( mpi_2expt(&t2N1, N-1) ); + /* t2seedlen = 2^seedlen */ + MBEDTLS_MPI_CHK( mpi_2expt(&t2seedlen, seedbytes*8) ); + + /* FIPS-186-4 A.1.1.2 Generation of the Probable Primes p and q Using an Approved Hash Function + * + * L = The desired length of the prime p (in bits e.g. L = 1024) + * N = The desired length of the prime q (in bits e.g. N = 160) + * seedlen = The desired bit length of the domain parameter seed; seedlen shallbe equal to or greater than N + * outlen = The bit length of Hash function + * + * 1. Check that the (L, N) + * 2. If (seedlen = 2^(L-1)) { + * Test whether or not p is prime as specified in Appendix C.3. + * If p is determined to be prime, then return VALID and the values of p, qand (optionally) the values of domain_parameter_seed and counter + * } + * offset = offset + n + 1 Comment: Increment offset + * } + */ + for(found_p=0; !found_p;) { + /* q */ + for(found_q=0; !found_q;) { + f_rng( p_rng, sbuf, seedbytes ); + i = outbytes; + MBEDTLS_MPI_CHK( mbedtls_md(md_info, sbuf, seedbytes, digest) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary(&U, digest, outbytes) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi(&U1, &U, &t2N1) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi(&q, &t2N1, &U1) ); + if (mbedtls_mpi_get_bit(&q, 0) == 0) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy(&q, &q1) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_add_int(&q, &q1, 1) ); + } + if( mbedtls_mpi_is_prime_ext(&q, mr_tests_q, f_rng, p_rng) == 0) found_q = 1; + } + /* p */ + MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary(&seedinc, sbuf, seedbytes) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi(&t2q, &q, &q) ); + for(counter=0; counter < 4*L && !found_p; counter++) { + for(j=0; j<=n; j++) { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy(&seedinc1, &seedinc) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_add_int(&seedinc, &seedinc1, 1) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi(&seedinc1, &seedinc, &t2seedlen) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy(&seedinc, &seedinc1) ); + /* seedinc = (seedinc+1) % 2^seed_bitlen */ + i = mbedtls_mpi_size(&seedinc); + if (i > seedbytes) { ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; goto cleanup; } + mbedtls_platform_zeroize(sbuf, seedbytes); + MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary(&seedinc, sbuf + seedbytes-i, i) ); + i = outbytes; + MBEDTLS_MPI_CHK( mbedtls_md(md_info, sbuf, seedbytes, wbuf+(n-j)*outbytes) ); + } + MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary(&W, wbuf, (n+1)*outbytes) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi(&W1, &W, &t2L1) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi(&X, &W1, &t2L1) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi(&c, &X, &t2q) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int(&p1, &c, 1) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi(&p, &X, &p1) ); + if (mbedtls_mpi_cmp_mpi(&p, &t2L1) >= 0) { + /* p >= 2^(L-1) */ + if( mbedtls_mpi_is_prime_ext(&p, mr_tests_p, f_rng, p_rng) == 0 ) found_p = 1; + } + } + } + + /* FIPS-186-4 A.2.1 Unverifiable Generation of the Generator g + * 1. e = (p - 1)/q + * 2. h = any integer satisfying: 1 < h < (p - 1) + * h could be obtained from a random number generator or from a counter that changes after each use + * 3. g = h^e mod p + * 4. if (g == 1), then go to step 2. + * + */ + + /* e = (p - 1)/q */ + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int(&p1, &p, 1) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_div_mpi(&e, &c, &p1, &q) ); + + i = mbedtls_mpi_size(&p); + do { + /* h is random and 1 < h < (p-1) */ + do { + MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random(&h, i, f_rng, p_rng) ); + } while (mbedtls_mpi_cmp_mpi(&h, &p) >= 0 || mbedtls_mpi_cmp_int(&h, 2) <= 0); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int(&h1, &h, 1) ); + /* g = h^e mod p */ + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod(&g, &h1, &e, &p, NULL) ); + } while (mbedtls_mpi_cmp_int(&g, 1) == 0); + + MBEDTLS_MPI_CHK( mbedtls_mpi_copy(&ctx->P, &p) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy(&ctx->Q, &q) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_copy(&ctx->G, &g) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int(&q1, &q, 1) ); + + /* private key x should be from range: 1 <= x <= q-1 (see FIPS 186-4 B.1.2) */ + do { + MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &ctx->X, mbedtls_mpi_size( &q1 ), f_rng, p_rng ) ); + } while ( mbedtls_mpi_cmp_int( &ctx->X, 1 ) < 0 || mbedtls_mpi_cmp_mpi( &ctx->X, &q1 ) > 0 ); + + /* compute y = g^x mod p */ + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &ctx->Y, &ctx->G, &ctx->X, &ctx->P, NULL ) ); + +cleanup: + mbedtls_mpi_free( &t2L1 ); + mbedtls_mpi_free( &t2N1 ); + mbedtls_mpi_free( &t2seedlen ); + mbedtls_mpi_free( &U ); + mbedtls_mpi_free( &U1 ); + mbedtls_mpi_free( &W ); + mbedtls_mpi_free( &W1 ); + mbedtls_mpi_free( &X ); + mbedtls_mpi_free( &q ); + mbedtls_mpi_free( &q1 ); + mbedtls_mpi_free( &p ); + mbedtls_mpi_free( &p1 ); + mbedtls_mpi_free( &e ); + mbedtls_mpi_free( &h ); + mbedtls_mpi_free( &h1 ); + mbedtls_mpi_free( &g ); + mbedtls_mpi_free( &c ); + mbedtls_mpi_free( &t2q ); + mbedtls_mpi_free( &seedinc ); + mbedtls_mpi_free( &seedinc1 ); + mbedtls_free(sbuf); +cleanup2: + mbedtls_free(wbuf); +cleanup3: + return( ret ); +} + +/* + * Check group parameters + */ +int mbedtls_dsa_check_pqg( const mbedtls_mpi *P, const mbedtls_mpi *Q, const mbedtls_mpi *G ) +{ + int ret = 0; + mbedtls_mpi P1, T; + + DSA_VALIDATE_RET( P != NULL ); + DSA_VALIDATE_RET( Q != NULL ); + DSA_VALIDATE_RET( G != NULL ); + + mbedtls_mpi_init( &P1 ); + mbedtls_mpi_init( &T ); + + if( mbedtls_mpi_cmp_int( P, 0 ) == 0 ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + goto cleanup; + } + + if( mbedtls_mpi_cmp_int( Q, 0 ) == 0 ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + goto cleanup; + } + + if( mbedtls_mpi_cmp_int( G, 0 ) == 0 ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + goto cleanup; + } + + /* Check if P-1 is a multiple of Q */ + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &P1, P, 1 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &T, &P1, Q ) ); + if( mbedtls_mpi_cmp_int( &T, 0 ) != 0 ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + goto cleanup; + } + + /* Check if 1 < G < P */ + if( mbedtls_mpi_cmp_int( G, 1 ) <= 0 || mbedtls_mpi_cmp_mpi( G, P ) >= 0 ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + goto cleanup; + } + + /* Check if G^Q mod P = 1 */ + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &T, G, Q, P, NULL ) ); + if( mbedtls_mpi_cmp_int( &T, 1 ) != 0 ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + goto cleanup; + } + +cleanup: + mbedtls_mpi_free( &P1 ); + mbedtls_mpi_free( &T ); + + return( ret ); +} + +/* + * Check public key + */ +int mbedtls_dsa_check_pubkey( const mbedtls_dsa_context *ctx ) +{ + int ret = 0; + mbedtls_mpi T; + + DSA_VALIDATE_RET( ctx != NULL ); + + /* Y must be in [2, P-1] */ + if( mbedtls_mpi_cmp_int( &ctx->Y, 2 ) < 0 || + mbedtls_mpi_cmp_mpi( &ctx->Y, &ctx->P ) >= 0 ) + { + return( MBEDTLS_ERR_DSA_BAD_INPUT_DATA ); + } + + mbedtls_mpi_init( &T ); + + /* Y^Q mod P = 1 */ + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &T, &ctx->Y, &ctx->Q, &ctx->P, NULL ) ); + if( mbedtls_mpi_cmp_int( &T, 1 ) != 0 ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + } + +cleanup: + mbedtls_mpi_free( &T ); + return( ret ); +} + +/* + * Check private key + */ +int mbedtls_dsa_check_privkey( const mbedtls_dsa_context *ctx ) +{ + int ret = 0; + mbedtls_mpi T; + + DSA_VALIDATE_RET( ctx != NULL ); + + /* X must be in ]0, Q[ */ + if( mbedtls_mpi_cmp_int( &ctx->X, 0 ) <= 0 || + mbedtls_mpi_cmp_mpi( &ctx->X, &ctx->Q ) >= 0 ) + { + return( MBEDTLS_ERR_DSA_BAD_INPUT_DATA ); + } + + mbedtls_mpi_init( &T ); + + /* Y = G^X mod P */ + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &T, &ctx->G, &ctx->X, &ctx->P, NULL ) ); + if( mbedtls_mpi_cmp_mpi( &T, &ctx->Y ) != 0 ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + } + +cleanup: + mbedtls_mpi_free( &T ); + return( ret ); +} + +/* + * Derive public key from private key + */ +int mbedtls_dsa_pubkey_from_privkey( mbedtls_dsa_context *ctx ) +{ + int ret = 0; + + DSA_VALIDATE_RET( ctx != NULL ); + + /* X must be in ]0, Q[ */ + if( mbedtls_mpi_cmp_int( &ctx->X, 0 ) <= 0 || + mbedtls_mpi_cmp_mpi( &ctx->X, &ctx->Q ) >= 0 ) + { + return( MBEDTLS_ERR_DSA_BAD_INPUT_DATA ); + } + + /* Y = G^X mod P */ + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &ctx->Y, &ctx->G, &ctx->X, &ctx->P, NULL ) ); + +cleanup: + return( ret ); +} + +/* + * Convert a signature (given by context) to ASN.1 + */ +static int dsa_signature_to_asn1( const mbedtls_mpi *r, const mbedtls_mpi *s, + unsigned char *sig, size_t *slen ) +{ + int ret; + unsigned char buf[MBEDTLS_DSA_ASN_SIGNATURE_MAX_LEN]; + unsigned char *p = buf + sizeof( buf ); + size_t len = 0; + + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &p, buf, s ) ); + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &p, buf, r ) ); + + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &p, buf, len ) ); + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &p, buf, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ); + + memcpy( sig, p, len ); + *slen = len; + + return( 0 ); +} + +/* + * Compute and write signature + */ +int mbedtls_dsa_sign( mbedtls_dsa_context *ctx, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + mbedtls_mpi k, r, rp, kq, gcd, s, xr, xrh, xrhkq, h; + size_t hashlen, qlen; + + DSA_VALIDATE_RET( ctx != NULL ); + DSA_VALIDATE_RET( hash != NULL ); + DSA_VALIDATE_RET( sig != NULL ); + DSA_VALIDATE_RET( slen != NULL ); + DSA_VALIDATE_RET( f_rng != NULL ); + + mbedtls_mpi_init( &k ); + mbedtls_mpi_init( &r ); + mbedtls_mpi_init( &rp ); + mbedtls_mpi_init( &kq ); + mbedtls_mpi_init( &gcd ); + mbedtls_mpi_init( &s ); + mbedtls_mpi_init( &xr ); + mbedtls_mpi_init( &xrh ); + mbedtls_mpi_init( &xrhkq ); + mbedtls_mpi_init( &h ); + + /* FIPS 186-4 4.7: use leftmost min(bitlen(q), bitlen(hash)) bits of 'hash' */ + qlen = mbedtls_mpi_size( &ctx->Q ); + hashlen = hlen; + if (hashlen > qlen) hashlen = qlen; + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &h, hash, hashlen ) ); + +retry: + do + { + /* gen random k */ + if( mbedtls_mpi_fill_random( &k, qlen, f_rng, p_rng ) != 0 ) goto cleanup; + + /* k should be from range: 1 <= k <= q-1 (see FIPS 186-4 B.2.2) */ + if( mbedtls_mpi_cmp_int( &k, 0 ) <= 0 || mbedtls_mpi_cmp_mpi( &k, &ctx->Q ) >= 0 ) goto retry; + + /* test gcd */ + if( mbedtls_mpi_gcd( &gcd, &k, &ctx->Q ) != 0 ) goto cleanup; + + } while( mbedtls_mpi_cmp_int( &gcd, 1 ) != 0 ); + + /* now find 1/k mod q */ + MBEDTLS_MPI_CHK( mbedtls_mpi_inv_mod( &kq, &k, &ctx->Q ) ); + + /* now find r = g^k mod p mod q */ + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &rp, &ctx->G, &k, &ctx->P, NULL ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &r, &rp, &ctx->Q ) ); + + /* now find s = (hash + xr)/k mod q */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &xr, &ctx->X, &r ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &xrh, &xr, &h ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &xrhkq, &xrh, &kq ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &s, &xrhkq, &ctx->Q ) ); + + MBEDTLS_MPI_CHK( dsa_signature_to_asn1( &r, &s, sig, slen ) ); + +cleanup: + mbedtls_mpi_free( &k ); + mbedtls_mpi_free( &r ); + mbedtls_mpi_init( &rp ); + mbedtls_mpi_init( &kq ); + mbedtls_mpi_init( &gcd ); + mbedtls_mpi_free( &s ); + mbedtls_mpi_init( &xr ); + mbedtls_mpi_init( &xrh ); + mbedtls_mpi_init( &xrhkq ); + mbedtls_mpi_free( &h ); + + return( ret ); +} + +/* + * Read and check signature + */ +int mbedtls_dsa_verify( mbedtls_dsa_context *ctx, + const unsigned char *hash, size_t hlen, + const unsigned char *sig, size_t slen ) +{ + int ret; + unsigned char *p = (unsigned char *) sig; + const unsigned char *end = sig + slen; + size_t len, hashlen, qlen; + mbedtls_mpi r, s, h, w, hw, rw, u1, u2, gu1, yu2, u12, u12p, v, _rr; + + DSA_VALIDATE_RET( ctx != NULL ); + DSA_VALIDATE_RET( hash != NULL ); + DSA_VALIDATE_RET( sig != NULL ); + + mbedtls_mpi_init( &r ); + mbedtls_mpi_init( &s ); + mbedtls_mpi_init( &h ); + mbedtls_mpi_init( &w ); + mbedtls_mpi_init( &hw ); + mbedtls_mpi_init( &rw ); + mbedtls_mpi_init( &u1 ); + mbedtls_mpi_init( &u2 ); + mbedtls_mpi_init( &gu1 ); + mbedtls_mpi_init( &yu2 ); + mbedtls_mpi_init( &u12 ); + mbedtls_mpi_init( &u12p ); + mbedtls_mpi_init( &v ); + mbedtls_mpi_init( &_rr ); + + if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + goto cleanup; + } + + if( p + len != end ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + goto cleanup; + } + + if( ( ret = mbedtls_asn1_get_mpi( &p, end, &r ) ) != 0 || + ( ret = mbedtls_asn1_get_mpi( &p, end, &s ) ) != 0 ) + { + ret = MBEDTLS_ERR_DSA_BAD_INPUT_DATA; + goto cleanup; + } + + if( mbedtls_mpi_cmp_int( &r, 0 ) <= 0 || mbedtls_mpi_cmp_mpi( &r, &ctx->Q ) >= 0 || + mbedtls_mpi_cmp_int( &s, 0 ) <= 0 || mbedtls_mpi_cmp_mpi( &s, &ctx->Q ) >= 0 ) + { + ret = MBEDTLS_ERR_DSA_VERIFY_FAILED; + goto cleanup; + } + + /* FIPS 186-4 4.7: use leftmost min(bitlen(q), bitlen(hash)) bits of 'hash' */ + qlen = mbedtls_mpi_size( &ctx->Q ); + hashlen = hlen; + if (hashlen > qlen) hashlen = qlen; + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &h, hash, hashlen ) ); + + /* w = 1/s mod q */ + MBEDTLS_MPI_CHK( mbedtls_mpi_inv_mod( &w, &s, &ctx->Q ) ); + + /* u1 = h*w mod q */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &hw, &h, &w ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &u1, &hw, &ctx->Q ) ); + + /* u2 = r*w mod q */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &rw, &r, &w ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &u2, &rw, &ctx->Q ) ); + + /* v = g^u1 * y^u2 mod p mod q */ + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &gu1, &ctx->G, &u1, &ctx->P, &_rr ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &yu2, &ctx->Y, &u2, &ctx->P, &_rr ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &u12, &gu1, &yu2 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &u12p, &u12, &ctx->P ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &v, &u12p, &ctx->Q ) ); + + /* if r = v then it is verified */ + if( mbedtls_mpi_cmp_mpi( &v, &r ) != 0 ) + { + ret = MBEDTLS_ERR_DSA_VERIFY_FAILED; + goto cleanup; + } + +cleanup: + mbedtls_mpi_free( &r ); + mbedtls_mpi_free( &s ); + mbedtls_mpi_free( &h ); + mbedtls_mpi_free( &w ); + mbedtls_mpi_free( &u1 ); + mbedtls_mpi_free( &u2 ); + mbedtls_mpi_init( &gu1 ); + mbedtls_mpi_init( &yu2 ); + mbedtls_mpi_init( &u12 ); + mbedtls_mpi_init( &u12p ); + mbedtls_mpi_free( &v ); + mbedtls_mpi_free( &_rr ); + + return( ret ); +} + +#endif /* MBEDTLS_DSA_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +static const char *dsa_test_p_hex = "F7E75FDC469067FFDC4E847C51F452DF27303F51D8F2E3E444924563740179E1"; +static const char *dsa_test_q_hex = "87E85B349454331564214218435B420E53368DB1"; +static const char *dsa_test_g_hex = "F7E75FDC469067FFDC4E847C51F452DF27303F51D8F2E3E444924563740179E1"; +static const char *dsa_test_x_hex = "20B4822143298349213489213498213498213948"; +static const char *dsa_test_y_hex = "154285095685091850198501985091850918509185091850918509185091850918509185091850918509185091850918509185091850918509185091850918509185"; +static const char *dsa_test_hash_hex = "A9993E364706816ABA3E25717850C26C9CD0D89D"; + +/* + * Checkup routine + */ +int mbedtls_dsa_self_test( int verbose ) +{ + int ret; + mbedtls_dsa_context dsa; + unsigned char sig[MBEDTLS_DSA_ASN_SIGNATURE_MAX_LEN]; + size_t slen; + unsigned char hash[20]; + + mbedtls_dsa_init( &dsa ); + + if( verbose != 0 ) + mbedtls_printf( " DSA key generation: " ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &dsa.P, 16, dsa_test_p_hex ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &dsa.Q, 16, dsa_test_q_hex ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &dsa.G, 16, dsa_test_g_hex ) ); + dsa.len = mbedtls_mpi_size( &dsa.P ); + + if( ( ret = mbedtls_dsa_genkey( &dsa, NULL, NULL ) ) != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + mbedtls_printf( "passed\n DSA signature: " ); + + memcpy( hash, dsa_test_hash_hex, 20 ); + + if( ( ret = mbedtls_dsa_write_signature( &dsa, MBEDTLS_MD_SHA1, hash, 20, sig, &slen, NULL, NULL ) ) != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + mbedtls_printf( "passed\n DSA verification: " ); + + if( ( ret = mbedtls_dsa_read_signature( &dsa, hash, 20, sig, slen ) ) != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + mbedtls_printf( "passed\n\n" ); + +exit: + mbedtls_dsa_free( &dsa ); + + return( ret ); +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_DSA_C */ diff --git a/dll/3rdparty/mbedtls/mbedtls.spec b/dll/3rdparty/mbedtls/mbedtls.spec index c6db76673f6..37a78c2df78 100644 --- a/dll/3rdparty/mbedtls/mbedtls.spec +++ b/dll/3rdparty/mbedtls/mbedtls.spec @@ -1,6 +1,7 @@ @ cdecl mbedtls_ctr_drbg_free(ptr) @ cdecl mbedtls_ctr_drbg_init(ptr) @ cdecl mbedtls_ctr_drbg_random(ptr str long) +@ cdecl mbedtls_ctr_drbg_seed(ptr ptr ptr ptr long) @ cdecl mbedtls_entropy_free(ptr) @ cdecl mbedtls_entropy_func(ptr str long) @ cdecl mbedtls_entropy_init(ptr) @@ -27,33 +28,92 @@ @ cdecl mbedtls_ssl_config_defaults(ptr long long long) @ cdecl mbedtls_ssl_conf_dbg(ptr ptr ptr) @ cdecl mbedtls_ssl_setup(ptr ptr) -@ cdecl mbedtls_cipher_info_from_type(long) -@ cdecl mbedtls_md_info_from_type(long) -@ cdecl mbedtls_pk_get_bitlen(ptr) -@ cdecl mbedtls_ctr_drbg_seed(ptr ptr ptr str long) @ cdecl mbedtls_md_init(ptr) +@ cdecl mbedtls_md_info_from_type(long) +@ cdecl mbedtls_md_get_size(ptr) @ cdecl mbedtls_md_setup(ptr ptr long) @ cdecl mbedtls_md_update(ptr ptr long) -@ cdecl mbedtls_md_hmac_starts(ptr) +@ cdecl mbedtls_md_starts(ptr) +@ cdecl mbedtls_md_finish(ptr ptr) +@ cdecl mbedtls_md_hmac_update(ptr ptr long) +@ cdecl mbedtls_md_hmac_starts(ptr ptr long) @ cdecl mbedtls_md_hmac_finish(ptr ptr) @ cdecl mbedtls_md_free(ptr) -@ cdecl mbedtls_md5_init(ptr) -@ cdecl mbedtls_md5_starts(ptr) -@ cdecl mbedtls_md5_update(ptr ptr long) -@ cdecl mbedtls_md5_finish(ptr ptr) -@ cdecl mbedtls_md5_free(ptr) -@ cdecl mbedtls_sha1_init(ptr) -@ cdecl mbedtls_sha1_starts(ptr) -@ cdecl mbedtls_sha1_update(ptr ptr long) -@ cdecl mbedtls_sha1_finish(ptr ptr) -@ cdecl mbedtls_sha1_free(ptr) -@ cdecl mbedtls_sha256_init(ptr) -@ cdecl mbedtls_sha256_starts(ptr long) -@ cdecl mbedtls_sha256_update(ptr ptr long) -@ cdecl mbedtls_sha256_finish(ptr ptr) -@ cdecl mbedtls_sha256_free(ptr) -@ cdecl mbedtls_sha512_init(ptr) -@ cdecl mbedtls_sha512_starts(ptr long) -@ cdecl mbedtls_sha512_update(ptr ptr long) -@ cdecl mbedtls_sha512_finish(ptr ptr) -@ cdecl mbedtls_sha512_free(ptr) \ No newline at end of file +@ cdecl mbedtls_cipher_init(ptr) +@ cdecl mbedtls_cipher_info_from_type(long) +@ cdecl mbedtls_cipher_setup(ptr ptr) +@ cdecl mbedtls_cipher_setkey(ptr ptr long long) +@ cdecl mbedtls_cipher_set_iv(ptr ptr long) +@ cdecl mbedtls_cipher_set_padding_mode(ptr long) +@ cdecl mbedtls_cipher_update(ptr ptr long ptr long) +@ cdecl mbedtls_cipher_update_ad(ptr ptr long) +@ cdecl mbedtls_cipher_check_tag(ptr ptr long) +@ cdecl mbedtls_cipher_write_tag(ptr ptr long) +@ cdecl mbedtls_cipher_finish(ptr ptr long) +@ cdecl mbedtls_cipher_free(ptr) +@ cdecl mbedtls_pk_init(ptr) +@ cdecl mbedtls_pk_get_bitlen(ptr) +@ cdecl mbedtls_pk_get_type(ptr str long) +@ cdecl mbedtls_pk_setup(ptr ptr) +@ cdecl mbedtls_pk_info_from_type(long) +@ cdecl mbedtls_pk_parse_key(ptr ptr long ptr long) +@ cdecl mbedtls_pk_parse_public_key(ptr ptr long) +@ cdecl mbedtls_pk_verify_ext(ptr ptr long ptr long ptr long) +@ cdecl mbedtls_pk_sign(ptr long ptr long ptr ptr ptr ptr) +@ cdecl mbedtls_pk_encrypt(ptr ptr long ptr ptr long ptr ptr) +@ cdecl mbedtls_pk_decrypt(ptr ptr long ptr ptr long ptr ptr) +@ cdecl mbedtls_pk_free(ptr) +@ cdecl mbedtls_ecdh_init(ptr) +@ cdecl mbedtls_ecdh_compute_shared(ptr ptr ptr ptr ptr ptr) +@ cdecl mbedtls_ecdh_free(ptr) +@ cdecl mbedtls_rsa_gen_key(ptr ptr ptr long long) +@ cdecl mbedtls_rsa_import_raw(ptr ptr long ptr long ptr long ptr long ptr long) +@ cdecl mbedtls_rsa_export_raw(ptr ptr long ptr long ptr long ptr long ptr long) +@ cdecl mbedtls_rsa_export_crt(ptr ptr long ptr long ptr long) +@ cdecl mbedtls_rsa_export(ptr ptr ptr ptr ptr ptr) +@ cdecl mbedtls_rsa_set_padding(ptr long long) +@ cdecl mbedtls_rsa_complete(ptr) +@ cdecl mbedtls_rsa_check_pubkey(ptr) +@ cdecl mbedtls_rsa_check_privkey(ptr) +@ cdecl mbedtls_rsa_check_pub_priv(ptr ptr) +@ cdecl mbedtls_ecp_gen_key(long ptr ptr ptr) +@ cdecl mbedtls_ecp_group_load(ptr long) +@ cdecl mbedtls_ecp_point_read_binary(ptr ptr ptr long) +@ cdecl mbedtls_ecp_point_write_binary(ptr ptr ptr long) +@ cdecl mbedtls_ecp_check_pubkey(ptr ptr) +@ cdecl mbedtls_ecp_copy(ptr ptr) +@ cdecl mbedtls_mpi_init(ptr) +@ cdecl mbedtls_mpi_size(ptr) +@ cdecl mbedtls_mpi_lset(ptr long) +@ cdecl mbedtls_mpi_copy(ptr ptr) +@ cdecl mbedtls_mpi_cmp_mpi(ptr ptr) +@ cdecl mbedtls_mpi_cmp_int(ptr long) +@ cdecl mbedtls_mpi_read_binary(ptr ptr long) +@ cdecl mbedtls_mpi_write_binary(ptr ptr long) +@ cdecl mbedtls_mpi_read_string(ptr long str) +@ cdecl mbedtls_mpi_gen_prime(ptr long long ptr ptr) +@ cdecl mbedtls_mpi_sub_int(ptr ptr long) +@ cdecl mbedtls_mpi_div_int(ptr ptr ptr long) +@ cdecl mbedtls_mpi_is_prime_ext(ptr long ptr ptr) +@ cdecl mbedtls_mpi_free(ptr) +@ cdecl mbedtls_asn1_get_len(ptr ptr ptr) +@ cdecl mbedtls_asn1_get_mpi(ptr ptr ptr) +@ cdecl mbedtls_asn1_get_tag(ptr ptr ptr) +@ cdecl mbedtls_asn1_write_len(ptr ptr long) +@ cdecl mbedtls_asn1_write_mpi(ptr ptr ptr) +@ cdecl mbedtls_asn1_write_tag(ptr ptr long) +@ cdecl mbedtls_asn1_write_raw_buffer(ptr ptr ptr long) +@ cdecl mbedtls_dhm_init(ptr) +@ cdecl mbedtls_dhm_make_params(ptr long ptr ptr ptr ptr) +@ cdecl mbedtls_dhm_calc_secret(ptr ptr long ptr ptr ptr) +@ cdecl mbedtls_dhm_free(ptr) +@ cdecl mbedtls_dsa_init(ptr) +@ cdecl mbedtls_dsa_set_group(ptr ptr ptr ptr) +@ cdecl mbedtls_dsa_genkey(ptr long long ptr ptr) +@ cdecl mbedtls_dsa_check_pqg(ptr ptr ptr) +@ cdecl mbedtls_dsa_check_pubkey(ptr) +@ cdecl mbedtls_dsa_check_privkey(ptr) +@ cdecl mbedtls_dsa_pubkey_from_privkey(ptr) +@ cdecl mbedtls_dsa_verify(ptr ptr long ptr long) +@ cdecl mbedtls_dsa_sign(ptr ptr long ptr ptr ptr ptr) +@ cdecl mbedtls_dsa_free(ptr) diff --git a/dll/win32/bcrypt/CMakeLists.txt b/dll/win32/bcrypt/CMakeLists.txt index 53b00916330..8b58add730d 100644 --- a/dll/win32/bcrypt/CMakeLists.txt +++ b/dll/win32/bcrypt/CMakeLists.txt @@ -5,11 +5,18 @@ spec2def(bcrypt.dll bcrypt.spec ADD_IMPORTLIB) list(APPEND SOURCE bcrypt_main.c + gnutls.c + mbedtls.c + commoncrypto.c version.rc - ${CMAKE_CURRENT_BINARY_DIR}/bcrypt_stubs.c - ${CMAKE_CURRENT_BINARY_DIR}/bcrypt.def) + ${CMAKE_CURRENT_BINARY_DIR}/bcrypt_stubs.c) + +add_library(bcrypt MODULE + ${SOURCE} + version.rc + ${CMAKE_CURRENT_BINARY_DIR}/bcrypt.def) +add_pch(bcrypt precomp.h SOURCE) -add_library(bcrypt MODULE ${SOURCE}) set_module_type(bcrypt win32dll) target_link_libraries(bcrypt wine) add_importlibs(bcrypt mbedtls advapi32 msvcrt kernel32 ntdll) diff --git a/dll/win32/bcrypt/bcrypt.spec b/dll/win32/bcrypt/bcrypt.spec index b7bde3a0df9..4f481b0bd30 100644 --- a/dll/win32/bcrypt/bcrypt.spec +++ b/dll/win32/bcrypt/bcrypt.spec @@ -1,56 +1,59 @@ -@ stub BCryptAddContextFunction -@ stub BCryptAddContextFunctionProvider +@ stdcall BCryptAddContextFunction(long wstr long wstr long) +@ stdcall BCryptAddContextFunctionProvider(long wstr long wstr wstr long) @ stdcall BCryptCloseAlgorithmProvider(ptr long) @ stub BCryptConfigureContext @ stub BCryptConfigureContextFunction @ stub BCryptCreateContext @ stdcall BCryptCreateHash(ptr ptr ptr long ptr long long) -@ stub BCryptDecrypt +@ stdcall BCryptDecrypt(ptr ptr long ptr ptr long ptr long ptr long) @ stub BCryptDeleteContext -@ stub BCryptDeriveKey +@ stdcall BCryptDeriveKey(ptr wstr ptr ptr long ptr long) +@ stdcall BCryptDeriveKeyCapi(ptr ptr ptr long long) +@ stdcall BCryptDeriveKeyPBKDF2(ptr ptr long ptr long int64 ptr long long) @ stdcall BCryptDestroyHash(ptr) -@ stdcall -stub BCryptDestroyKey(ptr) -@ stub BCryptDestroySecret -@ stub BCryptDuplicateHash -@ stub BCryptDuplicateKey -@ stub BCryptEncrypt +@ stdcall BCryptDestroyKey(ptr) +@ stdcall BCryptDestroySecret(ptr) +@ stdcall BCryptDuplicateHash(ptr ptr ptr long long) +@ stdcall BCryptDuplicateKey(ptr ptr ptr long long) +@ stdcall BCryptEncrypt(ptr ptr long ptr ptr long ptr long ptr long) @ stdcall BCryptEnumAlgorithms(long ptr ptr long) @ stub BCryptEnumContextFunctionProviders -@ stub BCryptEnumContextFunctions +@ stdcall BCryptEnumContextFunctions(long wstr long ptr ptr) @ stub BCryptEnumContexts @ stub BCryptEnumProviders @ stub BCryptEnumRegisteredProviders -@ stub BCryptExportKey -@ stub BCryptFinalizeKeyPair +@ stdcall BCryptExportKey(ptr ptr wstr ptr long ptr long) +@ stdcall BCryptFinalizeKeyPair(ptr long) @ stdcall BCryptFinishHash(ptr ptr long long) -@ stub BCryptFreeBuffer +@ stdcall BCryptFreeBuffer(ptr) @ stdcall BCryptGenRandom(ptr ptr long long) -@ stub BCryptGenerateKeyPair -@ stub BCryptGenerateSymmetricKey +@ stdcall BCryptGenerateKeyPair(ptr ptr long long) +@ stdcall BCryptGenerateSymmetricKey(ptr ptr ptr long ptr long long) @ stdcall BCryptGetFipsAlgorithmMode(ptr) @ stdcall BCryptGetProperty(ptr wstr ptr long ptr long) @ stdcall BCryptHash(ptr ptr long ptr long ptr long) @ stdcall BCryptHashData(ptr ptr long long) -@ stub BCryptImportKey -@ stdcall -stub BCryptImportKeyPair(ptr ptr wstr ptr ptr long long) +@ stdcall BCryptImportKey(ptr ptr wstr ptr ptr long ptr long long) +@ stdcall BCryptImportKeyPair(ptr ptr wstr ptr ptr long long) +@ stdcall BCryptKeyDerivation(ptr ptr ptr long ptr long) @ stdcall BCryptOpenAlgorithmProvider(ptr wstr wstr long) @ stub BCryptQueryContextConfiguration @ stub BCryptQueryContextFunctionConfiguration @ stub BCryptQueryContextFunctionProperty @ stub BCryptQueryProviderRegistration @ stub BCryptRegisterConfigChangeNotify -@ stub BCryptRegisterProvider -@ stub BCryptRemoveContextFunction -@ stub BCryptRemoveContextFunctionProvider +@ stdcall BCryptRegisterProvider(wstr long ptr) +@ stdcall BCryptRemoveContextFunction(long wstr long wstr) +@ stdcall BCryptRemoveContextFunctionProvider(long wstr long wstr wstr) @ stub BCryptResolveProviders -@ stub BCryptSecretAgreement +@ stdcall BCryptSecretAgreement(ptr ptr ptr long) @ stub BCryptSetAuditingInterface @ stub BCryptSetContextFunctionProperty -@ stub BCryptSetProperty -@ stub BCryptSignHash +@ stdcall BCryptSetProperty(ptr wstr ptr long long) +@ stdcall BCryptSignHash(ptr ptr ptr long ptr long ptr long) @ stub BCryptUnregisterConfigChangeNotify -@ stub BCryptUnregisterProvider -@ stdcall -stub BCryptVerifySignature(ptr ptr ptr long ptr long long) +@ stdcall BCryptUnregisterProvider(wstr) +@ stdcall BCryptVerifySignature(ptr ptr ptr long ptr long long) @ stub GetAsymmetricEncryptionInterface @ stub GetCipherInterface @ stub GetHashInterface diff --git a/dll/win32/bcrypt/bcrypt_internal.h b/dll/win32/bcrypt/bcrypt_internal.h new file mode 100644 index 00000000000..d2d2660f8ee --- /dev/null +++ b/dll/win32/bcrypt/bcrypt_internal.h @@ -0,0 +1,312 @@ +#ifndef __BCRYPT_INTERNAL_H +#define __BCRYPT_INTERNAL_H + +#include "precomp.h" +#ifdef SONAME_LIBMBEDTLS +#include +#endif + +#define MAGIC_DSS1 ('D' | ('S' << 8) | ('S' << 16) | ('1' << 24)) +#define MAGIC_DSS2 ('D' | ('S' << 8) | ('S' << 16) | ('2' << 24)) + +#define MAGIC_ALG (('A' << 24) | ('L' << 16) | ('G' << 8) | '0') +#define MAGIC_HASH (('H' << 24) | ('A' << 16) | ('S' << 8) | 'H') +#define MAGIC_KEY (('K' << 24) | ('E' << 16) | ('Y' << 8) | '0') +#define MAGIC_SECRET (('S' << 24) | ('C' << 16) | ('R' << 8) | 'T') +struct object +{ + ULONG magic; +}; + +enum alg_id +{ + /* cipher */ + ALG_ID_3DES, + ALG_ID_AES, + ALG_ID_RC4, + + /* hash */ + ALG_ID_SHA256, + ALG_ID_SHA384, + ALG_ID_SHA512, + ALG_ID_SHA1, + ALG_ID_MD5, + ALG_ID_MD4, + ALG_ID_MD2, + + /* asymmetric encryption */ + ALG_ID_RSA, + + /* secret agreement */ + ALG_ID_DH, + ALG_ID_ECDH_P256, + ALG_ID_ECDH_P384, + + /* signature */ + ALG_ID_RSA_SIGN, + ALG_ID_ECDSA_P256, + ALG_ID_ECDSA_P384, + ALG_ID_DSA, + + /* rng */ + ALG_ID_RNG, + + /* key derivation */ + ALG_ID_PBKDF2, +}; + +enum chain_mode +{ + CHAIN_MODE_CBC, + CHAIN_MODE_ECB, + CHAIN_MODE_CFB, + CHAIN_MODE_CCM, + CHAIN_MODE_GCM, +}; + +struct algorithm +{ + struct object hdr; + enum alg_id id; + enum chain_mode mode; + ULONG flags; +}; + +#define BLOCK_LENGTH_RC4 1 +#define BLOCK_LENGTH_3DES 8 +#define BLOCK_LENGTH_AES 16 + +struct key_symmetric +{ + enum chain_mode mode; + ULONG block_size; + UCHAR *vector; + ULONG vector_len; + UCHAR *secret; + ULONG secret_len; + CRITICAL_SECTION cs; +}; + +#define KEY_FLAG_LEGACY_DSA_V2 0x00000001 +#define KEY_FLAG_FINALIZED 0x00000002 + +struct key_asymmetric +{ + ULONG bitlen; /* ignored for ECC keys */ + ULONG flags; + DSSSEED dss_seed; +}; + +#define PRIVATE_DATA_SIZE 64 + +struct key +{ + struct object hdr; + enum alg_id alg_id; + UINT64 private[PRIVATE_DATA_SIZE]; /* private data for backend */ + union + { + struct key_symmetric s; + struct key_asymmetric a; + } u; +}; + +struct secret +{ + struct object hdr; + struct key *privkey; + struct key *pubkey; +}; + +struct key_symmetric_set_auth_data_params +{ + struct key *key; + UCHAR *auth_data; + ULONG len; +#ifdef SONAME_LIBMBEDTLS + BOOL encrypt; +#endif +}; + +struct key_symmetric_encrypt_params +{ + struct key *key; + const UCHAR *input; + unsigned input_len; + UCHAR *output; + ULONG output_len; +}; + +struct key_symmetric_decrypt_params +{ + struct key *key; + const UCHAR *input; + unsigned input_len; + UCHAR *output; + ULONG output_len; +}; + +struct key_symmetric_get_tag_params +{ + struct key *key; + UCHAR *tag; + ULONG len; +}; + +struct key_asymmetric_decrypt_params +{ + struct key *key; + UCHAR *input; + ULONG input_len; + void *padding; + UCHAR *output; + ULONG output_len; + ULONG *ret_len; + ULONG flags; +}; + +struct key_asymmetric_encrypt_params +{ + struct key *key; + UCHAR *input; + ULONG input_len; + void *padding; + UCHAR *output; + ULONG output_len; + ULONG *ret_len; + ULONG flags; +}; + +struct key_asymmetric_duplicate_params +{ + struct key *key_orig; + struct key *key_copy; +}; + +struct key_asymmetric_sign_params +{ + struct key *key; + void *padding; + UCHAR *input; + ULONG input_len; + UCHAR *output; + ULONG output_len; + ULONG *ret_len; + ULONG flags; +}; + +struct key_asymmetric_verify_params +{ + struct key *key; + void *padding; + UCHAR *hash; + ULONG hash_len; + UCHAR *signature; + ULONG signature_len; + ULONG flags; +}; + +#define KEY_EXPORT_FLAG_PUBLIC 0x00000001 +#define KEY_EXPORT_FLAG_RSA_FULL 0x00000002 +#define KEY_EXPORT_FLAG_DH_PARAMETERS 0x00000004 + +struct key_asymmetric_export_params +{ + struct key *key; + ULONG flags; + UCHAR *buf; + ULONG len; + ULONG *ret_len; +}; + +#define KEY_IMPORT_FLAG_PUBLIC 0x00000001 +#define KEY_IMPORT_FLAG_DH_PARAMETERS 0x00000002 + +struct key_asymmetric_import_params +{ + struct key *key; + ULONG flags; + UCHAR *buf; + ULONG len; +}; + +struct key_asymmetric_derive_key_params +{ + struct key *privkey; + struct key *pubkey; + UCHAR *output; + ULONG output_len; + ULONG *ret_len; +}; + + +#define HASH_FLAG_HMAC 0x01 +#define HASH_FLAG_REUSABLE 0x02 + +#if defined(HAVE_GNUTLS_HASH) +struct hash +{ + struct object hdr; + enum alg_id alg_id; + ULONG flags; + UCHAR *secret; + ULONG secret_len; + union + { + gnutls_hash_hd_t hash_handle; + gnutls_hmac_hd_t hmac_handle; + } u; +}; +#elif defined(SONAME_LIBMBEDTLS) +struct hash +{ + struct object hdr; + enum alg_id alg_id; + ULONG flags; + UCHAR *secret; + ULONG secret_len; + mbedtls_md_context_t hash_ctx; +}; +#else +struct hash +{ + struct object hdr; + enum alg_id alg_id; + ULONG flags; + UCHAR *secret; + ULONG secret_len; +}; +#endif + +extern NTSTATUS hash_init( struct hash *hash ); +extern NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ); +extern NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ); +extern NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ); +extern NTSTATUS hash_finish( struct hash *hash, UCHAR *output ); +extern NTSTATUS hmac_finish( struct hash *hash, UCHAR *output ); +extern NTSTATUS hash_get_size( struct hash *hash, ULONG *output ); + +extern NTSTATUS process_detach( void *args ); +extern NTSTATUS process_attach( void *args ); +extern NTSTATUS key_symmetric_set_auth_data( void *args ); +extern NTSTATUS key_symmetric_vector_reset( void *args ); +extern NTSTATUS key_symmetric_encrypt_internal( void *args ); +extern NTSTATUS key_symmetric_decrypt_internal( void *args ); +extern NTSTATUS key_symmetric_get_tag( void *args ); +extern NTSTATUS key_symmetric_destroy( void *args ); +extern NTSTATUS key_asymmetric_generate( void *args ); +extern NTSTATUS key_asymmetric_export( void *args ); +extern NTSTATUS key_asymmetric_import( void *args ); +extern NTSTATUS key_asymmetric_verify( void *args ); +extern NTSTATUS key_asymmetric_sign( void *args ); +extern NTSTATUS key_asymmetric_destroy( void *args ); +extern NTSTATUS key_asymmetric_duplicate( void *args ); +extern NTSTATUS key_asymmetric_decrypt( void *args ); +extern NTSTATUS key_asymmetric_encrypt( void *args ); +extern NTSTATUS key_asymmetric_derive_key( void *args ); + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE ARRAYSIZE +#endif + +#endif /* __BCRYPT_INTERNAL_H */ diff --git a/dll/win32/bcrypt/bcrypt_main.c b/dll/win32/bcrypt/bcrypt_main.c index 56234006bcd..7b5e3801713 100644 --- a/dll/win32/bcrypt/bcrypt_main.c +++ b/dll/win32/bcrypt/bcrypt_main.c @@ -17,6 +17,7 @@ * */ +#ifndef __REACTOS__ #include #include @@ -38,268 +39,288 @@ #include #include #endif +#else +#include "precomp.h" +#endif WINE_DEFAULT_DEBUG_CHANNEL(bcrypt); static HINSTANCE instance; -#if defined(HAVE_GNUTLS_HASH) && !defined(HAVE_COMMONCRYPTO_COMMONDIGEST_H) -WINE_DECLARE_DEBUG_CHANNEL(winediag); - -static void *libgnutls_handle; -#define MAKE_FUNCPTR(f) static typeof(f) * p##f -MAKE_FUNCPTR(gnutls_global_deinit); -MAKE_FUNCPTR(gnutls_global_init); -MAKE_FUNCPTR(gnutls_global_set_log_function); -MAKE_FUNCPTR(gnutls_global_set_log_level); -MAKE_FUNCPTR(gnutls_hash); -MAKE_FUNCPTR(gnutls_hash_deinit); -MAKE_FUNCPTR(gnutls_hash_init); -MAKE_FUNCPTR(gnutls_hmac); -MAKE_FUNCPTR(gnutls_hmac_deinit); -MAKE_FUNCPTR(gnutls_hmac_init); -MAKE_FUNCPTR(gnutls_perror); -#undef MAKE_FUNCPTR - -static void gnutls_log( int level, const char *msg ) +NTSTATUS WINAPI BCryptAddContextFunction( ULONG table, const WCHAR *ctx, ULONG iface, const WCHAR *func, ULONG pos ) { - TRACE( "<%d> %s", level, msg ); + FIXME( "%#lx, %s, %#lx, %s, %lu: stub\n", table, debugstr_w(ctx), iface, debugstr_w(func), pos ); + return STATUS_SUCCESS; } -static BOOL gnutls_initialize(void) +NTSTATUS WINAPI BCryptAddContextFunctionProvider( ULONG table, const WCHAR *ctx, ULONG iface, const WCHAR *func, + const WCHAR *provider, ULONG pos ) { - int ret; - - if (!(libgnutls_handle = wine_dlopen( SONAME_LIBGNUTLS, RTLD_NOW, NULL, 0 ))) - { - ERR_(winediag)( "failed to load libgnutls, no support for crypto hashes\n" ); - return FALSE; - } - -#define LOAD_FUNCPTR(f) \ - if (!(p##f = wine_dlsym( libgnutls_handle, #f, NULL, 0 ))) \ - { \ - ERR( "failed to load %s\n", #f ); \ - goto fail; \ - } - - LOAD_FUNCPTR(gnutls_global_deinit) - LOAD_FUNCPTR(gnutls_global_init) - LOAD_FUNCPTR(gnutls_global_set_log_function) - LOAD_FUNCPTR(gnutls_global_set_log_level) - LOAD_FUNCPTR(gnutls_hash); - LOAD_FUNCPTR(gnutls_hash_deinit); - LOAD_FUNCPTR(gnutls_hash_init); - LOAD_FUNCPTR(gnutls_hmac); - LOAD_FUNCPTR(gnutls_hmac_deinit); - LOAD_FUNCPTR(gnutls_hmac_init); - LOAD_FUNCPTR(gnutls_perror) -#undef LOAD_FUNCPTR - - if ((ret = pgnutls_global_init()) != GNUTLS_E_SUCCESS) - { - pgnutls_perror( ret ); - goto fail; - } - - if (TRACE_ON( bcrypt )) - { - pgnutls_global_set_log_level( 4 ); - pgnutls_global_set_log_function( gnutls_log ); - } - - return TRUE; - -fail: - wine_dlclose( libgnutls_handle, NULL, 0 ); - libgnutls_handle = NULL; - return FALSE; + FIXME( "%#lx, %s, %#lx, %s, %s, %lu: stub\n", table, debugstr_w(ctx), iface, debugstr_w(func), + debugstr_w(provider), pos ); + return STATUS_SUCCESS; } -static void gnutls_uninitialize(void) +NTSTATUS WINAPI BCryptRemoveContextFunction( ULONG table, const WCHAR *ctx, ULONG iface, const WCHAR *func ) { - pgnutls_global_deinit(); - wine_dlclose( libgnutls_handle, NULL, 0 ); - libgnutls_handle = NULL; -} -#elif defined(SONAME_LIBMBEDTLS) && !defined(HAVE_COMMONCRYPTO_COMMONDIGEST_H) && !defined(__REACTOS__) -WINE_DECLARE_DEBUG_CHANNEL(winediag); - -void *libmbedtls_handle; - -#define MAKE_FUNCPTR(f) static typeof(f) * p##f -MAKE_FUNCPTR(mbedtls_md_init); -MAKE_FUNCPTR(mbedtls_md_setup); -MAKE_FUNCPTR(mbedtls_md_update); -MAKE_FUNCPTR(mbedtls_md_hmac_starts); -MAKE_FUNCPTR(mbedtls_md_hmac_finish); -MAKE_FUNCPTR(mbedtls_md_free); -MAKE_FUNCPTR(mbedtls_md5_init); -MAKE_FUNCPTR(mbedtls_md5_starts); -MAKE_FUNCPTR(mbedtls_md5_update); -MAKE_FUNCPTR(mbedtls_md5_finish); -MAKE_FUNCPTR(mbedtls_md5_free); -MAKE_FUNCPTR(mbedtls_sha1_init); -MAKE_FUNCPTR(mbedtls_sha1_starts); -MAKE_FUNCPTR(mbedtls_sha1_update); -MAKE_FUNCPTR(mbedtls_sha1_finish); -MAKE_FUNCPTR(mbedtls_sha1_free); -MAKE_FUNCPTR(mbedtls_sha256_init); -MAKE_FUNCPTR(mbedtls_sha256_starts); -MAKE_FUNCPTR(mbedtls_sha256_update); -MAKE_FUNCPTR(mbedtls_sha256_finish); -MAKE_FUNCPTR(mbedtls_sha256_free); -MAKE_FUNCPTR(mbedtls_sha512_init); -MAKE_FUNCPTR(mbedtls_sha512_starts); -MAKE_FUNCPTR(mbedtls_sha512_update); -MAKE_FUNCPTR(mbedtls_sha512_finish); -MAKE_FUNCPTR(mbedtls_sha512_free); -#undef MAKE_FUNCPTR - -#define mbedtls_md_init pmbedtls_md_init -#define mbedtls_md_setup pmbedtls_md_setup -#define mbedtls_md_update pmbedtls_md_update -#define mbedtls_md_hmac_starts pmbedtls_md_hmac_starts -#define mbedtls_md_hmac_finish pmbedtls_md_hmac_finish -#define mbedtls_md_free pmbedtls_md_free -#define mbedtls_md5_init pmbedtls_md5_init -#define mbedtls_md5_starts pmbedtls_md5_starts -#define mbedtls_md5_update pmbedtls_md5_update -#define mbedtls_md5_finish pmbedtls_md5_finish -#define mbedtls_md5_free pmbedtls_md5_free -#define mbedtls_sha1_init pmbedtls_sha1_init -#define mbedtls_sha1_starts pmbedtls_sha1_starts -#define mbedtls_sha1_update pmbedtls_sha1_update -#define mbedtls_sha1_finish pmbedtls_sha1_finish -#define mbedtls_sha1_free pmbedtls_sha1_free -#define mbedtls_sha256_init pmbedtls_sha256_init -#define mbedtls_sha256_starts pmbedtls_sha256_starts -#define mbedtls_sha256_update pmbedtls_sha256_update -#define mbedtls_sha256_finish pmbedtls_sha256_finish -#define mbedtls_sha256_free pmbedtls_sha256_free -#define mbedtls_sha512_init pmbedtls_sha512_init -#define mbedtls_sha512_starts pmbedtls_sha512_starts -#define mbedtls_sha512_update pmbedtls_sha512_update -#define mbedtls_sha512_finish pmbedtls_sha512_finish -#define mbedtls_sha512_free pmbedtls_sha512_free - -static BOOL mbedtls_initialize(void) -{ - if (!(libmbedtls_handle = wine_dlopen( SONAME_LIBMBEDTLS, RTLD_NOW, NULL, 0 ))) - { - ERR_(winediag)( "failed to load libmbedtls, no support for crypto hashes\n" ); - return FALSE; - } - -#define LOAD_FUNCPTR(f) \ - if (!(p##f = wine_dlsym( libmbedtls_handle, #f, NULL, 0 ))) \ - { \ - ERR( "failed to load %s\n", #f ); \ - goto fail; \ - } - - LOAD_FUNCPTR(mbedtls_md_init) - LOAD_FUNCPTR(mbedtls_md_setup) - LOAD_FUNCPTR(mbedtls_md_update) - LOAD_FUNCPTR(mbedtls_md_hmac_starts) - LOAD_FUNCPTR(mbedtls_md_hmac_finish) - LOAD_FUNCPTR(mbedtls_md_free); - LOAD_FUNCPTR(mbedtls_md5_init) - LOAD_FUNCPTR(mbedtls_md5_starts) - LOAD_FUNCPTR(mbedtls_md5_update) - LOAD_FUNCPTR(mbedtls_md5_finish) - LOAD_FUNCPTR(mbedtls_md5_free); - LOAD_FUNCPTR(mbedtls_sha1_init) - LOAD_FUNCPTR(mbedtls_sha1_starts) - LOAD_FUNCPTR(mbedtls_sha1_update) - LOAD_FUNCPTR(mbedtls_sha1_finish) - LOAD_FUNCPTR(mbedtls_sha1_free); - LOAD_FUNCPTR(mbedtls_sha256_init) - LOAD_FUNCPTR(mbedtls_sha256_starts) - LOAD_FUNCPTR(mbedtls_sha256_update) - LOAD_FUNCPTR(mbedtls_sha256_finish) - LOAD_FUNCPTR(mbedtls_sha256_free); - LOAD_FUNCPTR(mbedtls_sha512_init) - LOAD_FUNCPTR(mbedtls_sha512_starts) - LOAD_FUNCPTR(mbedtls_sha512_update) - LOAD_FUNCPTR(mbedtls_sha512_finish) - LOAD_FUNCPTR(mbedtls_sha512_free); -#undef LOAD_FUNCPTR - - return TRUE; - -fail: - wine_dlclose( libmbedtls_handle, NULL, 0 ); - libmbedtls_handle = NULL; - return FALSE; -} - -static void mbedtls_uninitialize(void) -{ - wine_dlclose( libmbedtls_handle, NULL, 0 ); - libmbedtls_handle = NULL; -} -#endif /* SONAME_LIBMBEDTLS && !HAVE_COMMONCRYPTO_COMMONDIGEST_H && !__REACTOS__ */ - -NTSTATUS WINAPI BCryptEnumAlgorithms(ULONG dwAlgOperations, ULONG *pAlgCount, - BCRYPT_ALGORITHM_IDENTIFIER **ppAlgList, ULONG dwFlags) -{ - FIXME("%08x, %p, %p, %08x - stub\n", dwAlgOperations, pAlgCount, ppAlgList, dwFlags); - - *ppAlgList=NULL; - *pAlgCount=0; - + FIXME( "%#lx, %s, %#lx, %s: stub\n", table, debugstr_w(ctx), iface, debugstr_w(func) ); return STATUS_NOT_IMPLEMENTED; } -#define MAGIC_ALG (('A' << 24) | ('L' << 16) | ('G' << 8) | '0') -#define MAGIC_HASH (('H' << 24) | ('A' << 16) | ('S' << 8) | 'H') -struct object +NTSTATUS WINAPI BCryptRemoveContextFunctionProvider( ULONG table, const WCHAR *ctx, ULONG iface, const WCHAR *func, + const WCHAR *provider ) { - ULONG magic; -}; + FIXME( "%#lx, %s, %#lx, %s, %s: stub\n", table, debugstr_w(ctx), iface, debugstr_w(func), debugstr_w(provider) ); + return STATUS_NOT_IMPLEMENTED; +} -enum alg_id +NTSTATUS WINAPI BCryptEnumContextFunctions( ULONG table, const WCHAR *ctx, ULONG iface, ULONG *buflen, + CRYPT_CONTEXT_FUNCTIONS **buffer ) { - ALG_ID_MD5, - ALG_ID_RNG, - ALG_ID_SHA1, - ALG_ID_SHA256, - ALG_ID_SHA384, - ALG_ID_SHA512, - ALG_ID_ECDSA_P256, - ALG_ID_ECDSA_P384, -}; + FIXME( "%#lx, %s, %#lx, %p, %p\n", table, debugstr_w(ctx), iface, buflen, buffer ); + return STATUS_NOT_IMPLEMENTED; +} -static const struct { - ULONG hash_length; - const WCHAR *alg_name; -} alg_props[] = { - /* ALG_ID_MD5 */ { 16, BCRYPT_MD5_ALGORITHM }, - /* ALG_ID_RNG */ { 0, BCRYPT_RNG_ALGORITHM }, - /* ALG_ID_SHA1 */ { 20, BCRYPT_SHA1_ALGORITHM }, - /* ALG_ID_SHA256 */ { 32, BCRYPT_SHA256_ALGORITHM }, - /* ALG_ID_SHA384 */ { 48, BCRYPT_SHA384_ALGORITHM }, - /* ALG_ID_SHA512 */ { 64, BCRYPT_SHA512_ALGORITHM }, - /* ALG_ID_ECDSA_P256 */ { 0, BCRYPT_ECDSA_P256_ALGORITHM }, - /* ALG_ID_ECDSA_P384 */ { 0, BCRYPT_ECDSA_P384_ALGORITHM }, -}; - -struct algorithm +void WINAPI BCryptFreeBuffer( void *buffer ) { - struct object hdr; - enum alg_id id; - BOOL hmac; + free( buffer ); +} + +NTSTATUS WINAPI BCryptRegisterProvider( const WCHAR *provider, ULONG flags, CRYPT_PROVIDER_REG *reg ) +{ + FIXME( "%s, %#lx, %p: stub\n", debugstr_w(provider), flags, reg ); + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI BCryptUnregisterProvider( const WCHAR *provider ) +{ + FIXME( "%s: stub\n", debugstr_w(provider) ); + return STATUS_NOT_IMPLEMENTED; +} + +#define MAX_HASH_OUTPUT_BYTES 64 +#define MAX_HASH_BLOCK_BITS 1024 + +/* ordered by class, keep in sync with enum alg_id */ +static const struct +{ + const WCHAR *name; + ULONG class; + ULONG object_length; + ULONG hash_length; + ULONG block_bits; +} +builtin_algorithms[] = +{ + { BCRYPT_3DES_ALGORITHM, BCRYPT_CIPHER_INTERFACE, 522, 0, 0 }, + { BCRYPT_AES_ALGORITHM, BCRYPT_CIPHER_INTERFACE, 654, 0, 0 }, + { BCRYPT_RC4_ALGORITHM, BCRYPT_CIPHER_INTERFACE, 654, 0, 0 }, + { BCRYPT_SHA256_ALGORITHM, BCRYPT_HASH_INTERFACE, 286, 32, 512 }, + { BCRYPT_SHA384_ALGORITHM, BCRYPT_HASH_INTERFACE, 382, 48, 1024 }, + { BCRYPT_SHA512_ALGORITHM, BCRYPT_HASH_INTERFACE, 382, 64, 1024 }, + { BCRYPT_SHA1_ALGORITHM, BCRYPT_HASH_INTERFACE, 278, 20, 512 }, + { BCRYPT_MD5_ALGORITHM, BCRYPT_HASH_INTERFACE, 274, 16, 512 }, + { BCRYPT_MD4_ALGORITHM, BCRYPT_HASH_INTERFACE, 270, 16, 512 }, + { BCRYPT_MD2_ALGORITHM, BCRYPT_HASH_INTERFACE, 270, 16, 128 }, + { BCRYPT_RSA_ALGORITHM, BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE, 0, 0, 0 }, + { BCRYPT_DH_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, + { BCRYPT_ECDH_P256_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, + { BCRYPT_ECDH_P384_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, + { BCRYPT_RSA_SIGN_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, + { BCRYPT_ECDSA_P256_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, + { BCRYPT_ECDSA_P384_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, + { BCRYPT_DSA_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, + { BCRYPT_RNG_ALGORITHM, BCRYPT_RNG_INTERFACE, 0, 0, 0 }, + { BCRYPT_PBKDF2_ALGORITHM, BCRYPT_KEY_DERIVATION_INTERFACE, 618, 0, 0 }, }; -NTSTATUS WINAPI BCryptGenRandom(BCRYPT_ALG_HANDLE handle, UCHAR *buffer, ULONG count, ULONG flags) +static inline BOOL is_symmetric_key( const struct key *key ) +{ + return builtin_algorithms[key->alg_id].class == BCRYPT_CIPHER_INTERFACE + || builtin_algorithms[key->alg_id].class == BCRYPT_KEY_DERIVATION_INTERFACE; +} + +static inline BOOL is_asymmetric_encryption_key( struct key *key ) +{ + return builtin_algorithms[key->alg_id].class == BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE; +} + +static inline BOOL is_agreement_key( struct key *key ) +{ + return builtin_algorithms[key->alg_id].class == BCRYPT_SECRET_AGREEMENT_INTERFACE; +} + +static inline BOOL is_signature_key( struct key *key ) +{ + return builtin_algorithms[key->alg_id].class == BCRYPT_SIGNATURE_INTERFACE || key->alg_id == ALG_ID_RSA; +} + +static BOOL match_operation_type( ULONG type, ULONG class ) +{ + if (!type) return TRUE; + switch (class) + { + case BCRYPT_CIPHER_INTERFACE: return type & BCRYPT_CIPHER_OPERATION; + case BCRYPT_HASH_INTERFACE: return type & BCRYPT_HASH_OPERATION; + case BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE: return type & BCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION; + case BCRYPT_SECRET_AGREEMENT_INTERFACE: return type & BCRYPT_SECRET_AGREEMENT_OPERATION; + case BCRYPT_SIGNATURE_INTERFACE: return type & BCRYPT_SIGNATURE_OPERATION; + case BCRYPT_RNG_INTERFACE: return type & BCRYPT_RNG_OPERATION; + case BCRYPT_KEY_DERIVATION_INTERFACE: return type & BCRYPT_KEY_DERIVATION_OPERATION; + default: break; + } + return FALSE; +} + +NTSTATUS WINAPI BCryptEnumAlgorithms( ULONG type, ULONG *ret_count, BCRYPT_ALGORITHM_IDENTIFIER **ret_list, ULONG flags ) +{ + static const ULONG supported = BCRYPT_CIPHER_OPERATION |\ + BCRYPT_HASH_OPERATION |\ + BCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION |\ + BCRYPT_SECRET_AGREEMENT_OPERATION |\ + BCRYPT_SIGNATURE_OPERATION |\ + BCRYPT_RNG_OPERATION |\ + BCRYPT_KEY_DERIVATION_OPERATION; + BCRYPT_ALGORITHM_IDENTIFIER *list; + ULONG i, j, count = 0; + + TRACE( "%#lx, %p, %p, %#lx\n", type, ret_count, ret_list, flags ); + + if (!ret_count || !ret_list || (type & ~supported)) return STATUS_INVALID_PARAMETER; + + for (i = 0; i < ARRAY_SIZE( builtin_algorithms ); i++) + { + if (match_operation_type( type, builtin_algorithms[i].class )) count++; + } + + if (!(list = malloc( count * sizeof(*list) ))) return STATUS_NO_MEMORY; + + for (i = 0, j = 0; i < ARRAY_SIZE( builtin_algorithms ); i++) + { + if (!match_operation_type( type, builtin_algorithms[i].class )) continue; + list[j].pszName = (WCHAR *)builtin_algorithms[i].name; + list[j].dwClass = builtin_algorithms[i].class; + list[j].dwFlags = 0; + j++; + } + + *ret_count = count; + *ret_list = list; + return STATUS_SUCCESS; +} + +static const struct algorithm pseudo_algorithms[] = +{ + {{ MAGIC_ALG }, ALG_ID_MD2 }, + {{ MAGIC_ALG }, ALG_ID_MD4 }, + {{ MAGIC_ALG }, ALG_ID_MD5 }, + {{ MAGIC_ALG }, ALG_ID_SHA1 }, + {{ MAGIC_ALG }, ALG_ID_SHA256 }, + {{ MAGIC_ALG }, ALG_ID_SHA384 }, + {{ MAGIC_ALG }, ALG_ID_SHA512 }, + {{ MAGIC_ALG }, ALG_ID_RC4 }, + {{ MAGIC_ALG }, ALG_ID_RNG }, + {{ MAGIC_ALG }, ALG_ID_MD5, 0, BCRYPT_ALG_HANDLE_HMAC_FLAG }, + {{ MAGIC_ALG }, ALG_ID_SHA1, 0, BCRYPT_ALG_HANDLE_HMAC_FLAG }, + {{ MAGIC_ALG }, ALG_ID_SHA256, 0, BCRYPT_ALG_HANDLE_HMAC_FLAG }, + {{ MAGIC_ALG }, ALG_ID_SHA384, 0, BCRYPT_ALG_HANDLE_HMAC_FLAG }, + {{ MAGIC_ALG }, ALG_ID_SHA512, 0, BCRYPT_ALG_HANDLE_HMAC_FLAG }, + {{ MAGIC_ALG }, ALG_ID_RSA }, + {{ 0 }}, /* ECDSA */ + {{ 0 }}, /* AES_CMAC */ + {{ 0 }}, /* AES_GMAC */ + {{ MAGIC_ALG }, ALG_ID_MD2, 0, BCRYPT_ALG_HANDLE_HMAC_FLAG }, + {{ MAGIC_ALG }, ALG_ID_MD4, 0, BCRYPT_ALG_HANDLE_HMAC_FLAG }, + {{ MAGIC_ALG }, ALG_ID_3DES, CHAIN_MODE_CBC }, + {{ MAGIC_ALG }, ALG_ID_3DES, CHAIN_MODE_ECB }, + {{ MAGIC_ALG }, ALG_ID_3DES, CHAIN_MODE_CFB }, + {{ 0 }}, /* 3DES_112_CBC */ + {{ 0 }}, /* 3DES_112_ECB */ + {{ 0 }}, /* 3DES_112_CFB */ + {{ MAGIC_ALG }, ALG_ID_AES, CHAIN_MODE_CBC }, + {{ MAGIC_ALG }, ALG_ID_AES, CHAIN_MODE_ECB }, + {{ MAGIC_ALG }, ALG_ID_AES, CHAIN_MODE_CFB }, + {{ MAGIC_ALG }, ALG_ID_AES, CHAIN_MODE_CCM }, + {{ MAGIC_ALG }, ALG_ID_AES, CHAIN_MODE_GCM }, + {{ 0 }}, /* DES_CBC */ + {{ 0 }}, /* DES_ECB */ + {{ 0 }}, /* DES_CFB */ + {{ 0 }}, /* DESX_CBC */ + {{ 0 }}, /* DESX_ECB */ + {{ 0 }}, /* DESX_CFB */ + {{ 0 }}, /* RC2_CBC */ + {{ 0 }}, /* RC2_ECB */ + {{ 0 }}, /* RC2_CFB */ + {{ MAGIC_ALG }, ALG_ID_DH }, + {{ 0 }}, /* ECDH */ + {{ MAGIC_ALG }, ALG_ID_ECDH_P256 }, + {{ MAGIC_ALG }, ALG_ID_ECDH_P384 }, + {{ 0 }}, /* ECDH_P512 */ + {{ MAGIC_ALG }, ALG_ID_DSA }, + {{ MAGIC_ALG }, ALG_ID_ECDSA_P256 }, + {{ MAGIC_ALG }, ALG_ID_ECDSA_P384 }, + {{ 0 }}, /* ECDSA_P512 */ + {{ MAGIC_ALG }, ALG_ID_RSA_SIGN }, +}; + +/* Algorithm pseudo-handles are denoted by having the lowest bit set. + * An aligned algorithm pointer will never have this bit set. + */ +static inline BOOL is_alg_pseudo_handle( BCRYPT_ALG_HANDLE handle ) +{ + return (((ULONG_PTR)handle & 1) == 1); +} + +static struct object *get_object( BCRYPT_HANDLE handle, ULONG magic ) +{ + ULONG idx; + + if (!handle) return NULL; + + if (!is_alg_pseudo_handle( handle )) + { + struct object *obj = handle; + if (magic && obj->magic != magic) return NULL; + return obj; + } + + idx = (ULONG_PTR)handle >> 4; + if (idx >= ARRAY_SIZE(pseudo_algorithms) || !pseudo_algorithms[idx].hdr.magic) + { + FIXME( "pseudo-handle %p not supported\n", handle ); + return NULL; + } + return (struct object *)&pseudo_algorithms[idx]; +} + +static inline struct algorithm *get_alg_object( BCRYPT_ALG_HANDLE handle ) +{ + return (struct algorithm *)get_object( handle, MAGIC_ALG ); +} + +static inline struct hash *get_hash_object( BCRYPT_HASH_HANDLE handle ) +{ + return (struct hash *)get_object( handle, MAGIC_HASH ); +} + +static inline struct key *get_key_object( BCRYPT_KEY_HANDLE handle ) +{ + return (struct key *)get_object( handle, MAGIC_KEY ); +} + +static inline struct secret *get_secret_object( BCRYPT_SECRET_HANDLE handle ) +{ + return (struct secret *)get_object( handle, MAGIC_SECRET ); +} + +NTSTATUS WINAPI BCryptGenRandom( BCRYPT_ALG_HANDLE handle, UCHAR *buffer, ULONG count, ULONG flags ) { const DWORD supported_flags = BCRYPT_USE_SYSTEM_PREFERRED_RNG; - struct algorithm *algorithm = handle; + struct algorithm *alg = get_alg_object( handle ); - TRACE("%p, %p, %u, %08x - semi-stub\n", handle, buffer, count, flags); + TRACE("%p, %p, %lu, %#lx - semi-stub\n", handle, buffer, count, flags); - if (!algorithm) + if (!handle) { /* It's valid to call without an algorithm if BCRYPT_USE_SYSTEM_PREFERRED_RNG * is set. In this case the preferred system RNG is used. @@ -307,84 +328,104 @@ NTSTATUS WINAPI BCryptGenRandom(BCRYPT_ALG_HANDLE handle, UCHAR *buffer, ULONG c if (!(flags & BCRYPT_USE_SYSTEM_PREFERRED_RNG)) return STATUS_INVALID_HANDLE; } - else if (algorithm->hdr.magic != MAGIC_ALG || algorithm->id != ALG_ID_RNG) + else if (is_alg_pseudo_handle( handle ) && handle != BCRYPT_RNG_ALG_HANDLE) + { + FIXME( "pseudo-handle %p not supported\n", handle ); + return STATUS_NOT_IMPLEMENTED; + } + else if (!alg || alg->id != ALG_ID_RNG) return STATUS_INVALID_HANDLE; if (!buffer) return STATUS_INVALID_PARAMETER; if (flags & ~supported_flags) - FIXME("unsupported flags %08x\n", flags & ~supported_flags); + FIXME("unsupported flags %#lx\n", flags & ~supported_flags); - if (algorithm) + if (alg) FIXME("ignoring selected algorithm\n"); /* When zero bytes are requested the function returns success too. */ if (!count) return STATUS_SUCCESS; - if (algorithm || (flags & BCRYPT_USE_SYSTEM_PREFERRED_RNG)) + if (alg || (flags & BCRYPT_USE_SYSTEM_PREFERRED_RNG)) { - if (RtlGenRandom(buffer, count)) - return STATUS_SUCCESS; + if (RtlGenRandom(buffer, count)) return STATUS_SUCCESS; } FIXME("called with unsupported parameters, returning error\n"); return STATUS_NOT_IMPLEMENTED; } -NTSTATUS WINAPI BCryptOpenAlgorithmProvider( BCRYPT_ALG_HANDLE *handle, LPCWSTR id, LPCWSTR implementation, DWORD flags ) +static struct algorithm *create_algorithm( enum alg_id id, enum chain_mode mode, DWORD flags ) { + struct algorithm *ret; + if (!(ret = calloc( 1, sizeof(*ret) ))) return NULL; + ret->hdr.magic = MAGIC_ALG; + ret->id = id; + ret->mode = mode; + ret->flags = flags; + return ret; +} + +NTSTATUS WINAPI BCryptOpenAlgorithmProvider( BCRYPT_ALG_HANDLE *handle, const WCHAR *id, const WCHAR *implementation, + DWORD flags ) +{ + const DWORD supported_flags = BCRYPT_ALG_HANDLE_HMAC_FLAG | BCRYPT_HASH_REUSABLE_FLAG; struct algorithm *alg; enum alg_id alg_id; + ULONG i; - const DWORD supported_flags = BCRYPT_ALG_HANDLE_HMAC_FLAG; - - TRACE( "%p, %s, %s, %08x\n", handle, wine_dbgstr_w(id), wine_dbgstr_w(implementation), flags ); + TRACE( "%p, %s, %s, %#lx\n", handle, wine_dbgstr_w(id), wine_dbgstr_w(implementation), flags ); if (!handle || !id) return STATUS_INVALID_PARAMETER; if (flags & ~supported_flags) { - FIXME( "unsupported flags %08x\n", flags & ~supported_flags); + FIXME( "unsupported flags %#lx\n", flags & ~supported_flags ); return STATUS_NOT_IMPLEMENTED; } - if (!strcmpW( id, BCRYPT_SHA1_ALGORITHM )) alg_id = ALG_ID_SHA1; - else if (!strcmpW( id, BCRYPT_MD5_ALGORITHM )) alg_id = ALG_ID_MD5; - else if (!strcmpW( id, BCRYPT_RNG_ALGORITHM )) alg_id = ALG_ID_RNG; - else if (!strcmpW( id, BCRYPT_SHA256_ALGORITHM )) alg_id = ALG_ID_SHA256; - else if (!strcmpW( id, BCRYPT_SHA384_ALGORITHM )) alg_id = ALG_ID_SHA384; - else if (!strcmpW( id, BCRYPT_SHA512_ALGORITHM )) alg_id = ALG_ID_SHA512; - else if (!strcmpW( id, BCRYPT_ECDSA_P256_ALGORITHM )) alg_id = ALG_ID_ECDSA_P256; - else if (!strcmpW( id, BCRYPT_ECDSA_P384_ALGORITHM )) alg_id = ALG_ID_ECDSA_P384; - else + for (i = 0; i < ARRAY_SIZE( builtin_algorithms ); i++) + { + if (!wcscmp( id, builtin_algorithms[i].name)) + { + alg_id = i; + break; + } + } + if (i == ARRAY_SIZE( builtin_algorithms )) { FIXME( "algorithm %s not supported\n", debugstr_w(id) ); return STATUS_NOT_IMPLEMENTED; } - if (implementation && strcmpW( implementation, MS_PRIMITIVE_PROVIDER )) + + if (implementation && wcscmp( implementation, MS_PRIMITIVE_PROVIDER )) { FIXME( "implementation %s not supported\n", debugstr_w(implementation) ); return STATUS_NOT_IMPLEMENTED; } - if (!(alg = HeapAlloc( GetProcessHeap(), 0, sizeof(*alg) ))) return STATUS_NO_MEMORY; - alg->hdr.magic = MAGIC_ALG; - alg->id = alg_id; - alg->hmac = flags & BCRYPT_ALG_HANDLE_HMAC_FLAG; - + if (!(alg = create_algorithm( alg_id, 0, flags ))) return STATUS_NO_MEMORY; *handle = alg; + TRACE( "returning handle %p\n", *handle ); return STATUS_SUCCESS; } +static void destroy_object( struct object *obj ) +{ + SecureZeroMemory( &obj->magic, sizeof(obj->magic) ); + free( obj ); +} + NTSTATUS WINAPI BCryptCloseAlgorithmProvider( BCRYPT_ALG_HANDLE handle, DWORD flags ) { struct algorithm *alg = handle; - TRACE( "%p, %08x\n", handle, flags ); + TRACE( "%p, %#lx\n", handle, flags ); - if (!alg || alg->hdr.magic != MAGIC_ALG) return STATUS_INVALID_HANDLE; - HeapFree( GetProcessHeap(), 0, alg ); + if (!handle || is_alg_pseudo_handle( handle ) || alg->hdr.magic != MAGIC_ALG) return STATUS_INVALID_HANDLE; + destroy_object( &alg->hdr ); return STATUS_SUCCESS; } @@ -399,587 +440,425 @@ NTSTATUS WINAPI BCryptGetFipsAlgorithmMode(BOOLEAN *enabled) return STATUS_SUCCESS; } -#ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H -struct hash -{ - struct object hdr; - enum alg_id alg_id; - BOOL hmac; - union - { - CC_MD5_CTX md5_ctx; - CC_SHA1_CTX sha1_ctx; - CC_SHA256_CTX sha256_ctx; - CC_SHA512_CTX sha512_ctx; - CCHmacContext hmac_ctx; - } u; -}; - -static NTSTATUS hash_init( struct hash *hash ) -{ - switch (hash->alg_id) - { - case ALG_ID_MD5: - CC_MD5_Init( &hash->u.md5_ctx ); - break; - - case ALG_ID_SHA1: - CC_SHA1_Init( &hash->u.sha1_ctx ); - break; - - case ALG_ID_SHA256: - CC_SHA256_Init( &hash->u.sha256_ctx ); - break; - - case ALG_ID_SHA384: - CC_SHA384_Init( &hash->u.sha512_ctx ); - break; - - case ALG_ID_SHA512: - CC_SHA512_Init( &hash->u.sha512_ctx ); - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); - return STATUS_NOT_IMPLEMENTED; - } - return STATUS_SUCCESS; -} - -static NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) -{ - CCHmacAlgorithm cc_algorithm; - switch (hash->alg_id) - { - case ALG_ID_MD5: - cc_algorithm = kCCHmacAlgMD5; - break; - - case ALG_ID_SHA1: - cc_algorithm = kCCHmacAlgSHA1; - break; - - case ALG_ID_SHA256: - cc_algorithm = kCCHmacAlgSHA256; - break; - - case ALG_ID_SHA384: - cc_algorithm = kCCHmacAlgSHA384; - break; - - case ALG_ID_SHA512: - cc_algorithm = kCCHmacAlgSHA512; - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); - return STATUS_NOT_IMPLEMENTED; - } - - CCHmacInit( &hash->u.hmac_ctx, cc_algorithm, key, key_size ); - return STATUS_SUCCESS; -} - - -static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) -{ - switch (hash->alg_id) - { - case ALG_ID_MD5: - CC_MD5_Update( &hash->u.md5_ctx, input, size ); - break; - - case ALG_ID_SHA1: - CC_SHA1_Update( &hash->u.sha1_ctx, input, size ); - break; - - case ALG_ID_SHA256: - CC_SHA256_Update( &hash->u.sha256_ctx, input, size ); - break; - - case ALG_ID_SHA384: - CC_SHA384_Update( &hash->u.sha512_ctx, input, size ); - break; - - case ALG_ID_SHA512: - CC_SHA512_Update( &hash->u.sha512_ctx, input, size ); - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); - return STATUS_NOT_IMPLEMENTED; - } - return STATUS_SUCCESS; -} - -static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) -{ - CCHmacUpdate( &hash->u.hmac_ctx, input, size ); - return STATUS_SUCCESS; -} - -static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) -{ - switch (hash->alg_id) - { - case ALG_ID_MD5: - CC_MD5_Final( output, &hash->u.md5_ctx ); - break; - - case ALG_ID_SHA1: - CC_SHA1_Final( output, &hash->u.sha1_ctx ); - break; - - case ALG_ID_SHA256: - CC_SHA256_Final( output, &hash->u.sha256_ctx ); - break; - - case ALG_ID_SHA384: - CC_SHA384_Final( output, &hash->u.sha512_ctx ); - break; - - case ALG_ID_SHA512: - CC_SHA512_Final( output, &hash->u.sha512_ctx ); - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); - break; - } - return STATUS_SUCCESS; -} - -static NTSTATUS hmac_finish( struct hash *hash, UCHAR *output, ULONG size ) -{ - CCHmacFinal( &hash->u.hmac_ctx, output ); - - return STATUS_SUCCESS; -} -#elif defined(HAVE_GNUTLS_HASH) -struct hash -{ - struct object hdr; - enum alg_id alg_id; - BOOL hmac; - union - { - gnutls_hash_hd_t hash_handle; - gnutls_hmac_hd_t hmac_handle; - } u; -}; - -static NTSTATUS hash_init( struct hash *hash ) -{ - gnutls_digest_algorithm_t alg; - - if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; - - switch (hash->alg_id) - { - case ALG_ID_MD5: - alg = GNUTLS_DIG_MD5; - break; - case ALG_ID_SHA1: - alg = GNUTLS_DIG_SHA1; - break; - - case ALG_ID_SHA256: - alg = GNUTLS_DIG_SHA256; - break; - - case ALG_ID_SHA384: - alg = GNUTLS_DIG_SHA384; - break; - - case ALG_ID_SHA512: - alg = GNUTLS_DIG_SHA512; - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); - return STATUS_NOT_IMPLEMENTED; - } - - if (pgnutls_hash_init( &hash->u.hash_handle, alg )) return STATUS_INTERNAL_ERROR; - return STATUS_SUCCESS; -} - -static NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) -{ - gnutls_mac_algorithm_t alg; - - if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; - - switch (hash->alg_id) - { - case ALG_ID_MD5: - alg = GNUTLS_MAC_MD5; - break; - case ALG_ID_SHA1: - alg = GNUTLS_MAC_SHA1; - break; - - case ALG_ID_SHA256: - alg = GNUTLS_MAC_SHA256; - break; - - case ALG_ID_SHA384: - alg = GNUTLS_MAC_SHA384; - break; - - case ALG_ID_SHA512: - alg = GNUTLS_MAC_SHA512; - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); - return STATUS_NOT_IMPLEMENTED; - } - - if (pgnutls_hmac_init( &hash->u.hmac_handle, alg, key, key_size )) return STATUS_INTERNAL_ERROR; - return STATUS_SUCCESS; -} - -static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) -{ - if (pgnutls_hash( hash->u.hash_handle, input, size )) return STATUS_INTERNAL_ERROR; - return STATUS_SUCCESS; -} - -static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) -{ - if (pgnutls_hmac( hash->u.hmac_handle, input, size )) return STATUS_INTERNAL_ERROR; - return STATUS_SUCCESS; -} - -static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) -{ - pgnutls_hash_deinit( hash->u.hash_handle, output ); - return STATUS_SUCCESS; -} - -static NTSTATUS hmac_finish( struct hash *hash, UCHAR *output, ULONG size ) -{ - pgnutls_hmac_deinit( hash->u.hmac_handle, output ); - return STATUS_SUCCESS; -} -#elif defined(SONAME_LIBMBEDTLS) -struct hash -{ - struct object hdr; - BOOL hmac; - enum alg_id alg_id; - union - { - mbedtls_md5_context md5_ctx; - mbedtls_sha1_context sha1_ctx; - mbedtls_sha256_context sha256_ctx; - mbedtls_sha512_context sha512_ctx; - mbedtls_md_context_t hmac_ctx; - } u; -}; - -static NTSTATUS hash_init( struct hash *hash ) -{ -#ifndef __REACTOS__ - if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; -#endif - switch (hash->alg_id) - { - case ALG_ID_MD5: - mbedtls_md5_init(&hash->u.md5_ctx); - mbedtls_md5_starts(&hash->u.md5_ctx); - break; - - case ALG_ID_SHA1: - mbedtls_sha1_init(&hash->u.sha1_ctx); - mbedtls_sha1_starts(&hash->u.sha1_ctx); - break; - - case ALG_ID_SHA256: - mbedtls_sha256_init(&hash->u.sha256_ctx); - mbedtls_sha256_starts(&hash->u.sha256_ctx, FALSE); - break; - - case ALG_ID_SHA384: - case ALG_ID_SHA512: - mbedtls_sha512_init(&hash->u.sha512_ctx); - mbedtls_sha512_starts(&hash->u.sha512_ctx, hash->alg_id==ALG_ID_SHA384); - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); - return STATUS_NOT_IMPLEMENTED; - } - - return STATUS_SUCCESS; -} - -static NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) -{ - const mbedtls_md_info_t *md_info; - mbedtls_md_type_t md_type; - int ret; -#ifndef __REACTOS__ - if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; -#endif - mbedtls_md_init(&hash->u.hmac_ctx); - switch (hash->alg_id) - { - case ALG_ID_MD5: - md_type = MBEDTLS_MD_MD5; - break; - - case ALG_ID_SHA1: - md_type = MBEDTLS_MD_SHA1; - break; - - case ALG_ID_SHA256: - md_type = MBEDTLS_MD_SHA256; - break; - - case ALG_ID_SHA384: - md_type = MBEDTLS_MD_SHA384; - break; - - case ALG_ID_SHA512: - md_type = MBEDTLS_MD_SHA512; - break; - - default: - ERR("unhandled id %u\n", hash->alg_id); - return STATUS_NOT_IMPLEMENTED; - } - if ((md_info = mbedtls_md_info_from_type(md_type)) == NULL) - { - mbedtls_md_free(&hash->u.hmac_ctx); - return STATUS_INTERNAL_ERROR; - } - - if ((ret = mbedtls_md_setup(&hash->u.hmac_ctx, md_info, 1)) != 0) - { - mbedtls_md_free(&hash->u.hmac_ctx); - return STATUS_INTERNAL_ERROR; - } - - mbedtls_md_hmac_starts(&hash->u.hmac_ctx, key, key_size); - - return STATUS_SUCCESS; -} - -static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) -{ -#ifndef __REACTOS__ - if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; -#endif - switch (hash->alg_id) - { - case ALG_ID_MD5: - mbedtls_md5_update(&hash->u.md5_ctx, input, size); - break; - - case ALG_ID_SHA1: - mbedtls_sha1_update(&hash->u.sha1_ctx, input, size); - break; - - case ALG_ID_SHA256: - mbedtls_sha256_update(&hash->u.sha256_ctx, input, size); - break; - - case ALG_ID_SHA384: - case ALG_ID_SHA512: - mbedtls_sha512_update(&hash->u.sha512_ctx, input, size); - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); - return STATUS_NOT_IMPLEMENTED; - } - - return STATUS_SUCCESS; -} - -static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) -{ -#ifndef __REACTOS__ - if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; -#endif - mbedtls_md_update(&hash->u.hmac_ctx, input, size); - - return STATUS_SUCCESS; -} - -static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) -{ -#ifndef __REACTOS__ - if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; -#endif - switch (hash->alg_id) - { - case ALG_ID_MD5: - mbedtls_md5_finish(&hash->u.md5_ctx, output); - mbedtls_md5_free(&hash->u.md5_ctx); - break; - - case ALG_ID_SHA1: - mbedtls_sha1_finish(&hash->u.sha1_ctx, output); - mbedtls_sha1_free(&hash->u.sha1_ctx); - break; - - case ALG_ID_SHA256: - mbedtls_sha256_finish(&hash->u.sha256_ctx, output); - mbedtls_sha256_free(&hash->u.sha256_ctx); - break; - - case ALG_ID_SHA384: - case ALG_ID_SHA512: - mbedtls_sha512_finish(&hash->u.sha512_ctx, output); - mbedtls_sha512_free(&hash->u.sha512_ctx); - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); - return STATUS_NOT_IMPLEMENTED; - } - - return STATUS_SUCCESS; -} - -static NTSTATUS hmac_finish( struct hash *hash, UCHAR *output, ULONG size ) -{ -#ifndef __REACTOS__ - if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; -#endif - mbedtls_md_hmac_finish(&hash->u.hmac_ctx, output); - mbedtls_md_free(&hash->u.hmac_ctx); - - return STATUS_SUCCESS; -} -#endif - -#define OBJECT_LENGTH_MD5 274 -#define OBJECT_LENGTH_SHA1 278 -#define OBJECT_LENGTH_SHA256 286 -#define OBJECT_LENGTH_SHA384 382 -#define OBJECT_LENGTH_SHA512 382 - static NTSTATUS generic_alg_property( enum alg_id id, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) { - if (!strcmpW( prop, BCRYPT_HASH_LENGTH )) + if (!wcscmp( prop, BCRYPT_OBJECT_LENGTH )) { + if (!builtin_algorithms[id].object_length) + return STATUS_NOT_SUPPORTED; + *ret_size = sizeof(ULONG); + if (size < sizeof(ULONG)) + return STATUS_BUFFER_TOO_SMALL; + if (buf) + *(ULONG *)buf = builtin_algorithms[id].object_length; + return STATUS_SUCCESS; + } + + if (!wcscmp( prop, BCRYPT_HASH_LENGTH )) + { + if (!builtin_algorithms[id].hash_length) + return STATUS_NOT_SUPPORTED; *ret_size = sizeof(ULONG); if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL; if(buf) - *(ULONG*)buf = alg_props[id].hash_length; + *(ULONG*)buf = builtin_algorithms[id].hash_length; return STATUS_SUCCESS; } - if (!strcmpW( prop, BCRYPT_ALGORITHM_NAME )) + if (!wcscmp( prop, BCRYPT_ALGORITHM_NAME )) { - *ret_size = (strlenW(alg_props[id].alg_name)+1)*sizeof(WCHAR); + *ret_size = (lstrlenW(builtin_algorithms[id].name) + 1) * sizeof(WCHAR); if (size < *ret_size) return STATUS_BUFFER_TOO_SMALL; if(buf) - memcpy(buf, alg_props[id].alg_name, *ret_size); + memcpy(buf, builtin_algorithms[id].name, *ret_size); return STATUS_SUCCESS; } return STATUS_NOT_IMPLEMENTED; } -static NTSTATUS get_alg_property( enum alg_id id, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +static NTSTATUS get_3des_property( enum chain_mode mode, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +{ + if (!wcscmp( prop, BCRYPT_BLOCK_LENGTH )) + { + *ret_size = sizeof(ULONG); + if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL; + if (buf) *(ULONG *)buf = BLOCK_LENGTH_3DES; + return STATUS_SUCCESS; + } + if (!wcscmp( prop, BCRYPT_CHAINING_MODE )) + { + const WCHAR *str; + switch (mode) + { + case CHAIN_MODE_CBC: str = BCRYPT_CHAIN_MODE_CBC; break; + default: return STATUS_NOT_IMPLEMENTED; + } + + *ret_size = 64; + if (size < *ret_size) return STATUS_BUFFER_TOO_SMALL; + memcpy( buf, str, (lstrlenW(str) + 1) * sizeof(WCHAR) ); + return STATUS_SUCCESS; + } + if (!wcscmp( prop, BCRYPT_KEY_LENGTHS )) + { + BCRYPT_KEY_LENGTHS_STRUCT *key_lengths = (void *)buf; + *ret_size = sizeof(*key_lengths); + if (key_lengths && size < *ret_size) return STATUS_BUFFER_TOO_SMALL; + if (key_lengths) + { + key_lengths->dwMinLength = 192; + key_lengths->dwMaxLength = 192; + key_lengths->dwIncrement = 0; + } + return STATUS_SUCCESS; + } + FIXME( "unsupported property %s\n", debugstr_w(prop) ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS get_aes_property( enum chain_mode mode, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +{ + if (!wcscmp( prop, BCRYPT_BLOCK_LENGTH )) + { + *ret_size = sizeof(ULONG); + if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL; + if (buf) *(ULONG *)buf = BLOCK_LENGTH_AES; + return STATUS_SUCCESS; + } + if (!wcscmp( prop, BCRYPT_CHAINING_MODE )) + { + const WCHAR *str; + switch (mode) + { + case CHAIN_MODE_ECB: str = BCRYPT_CHAIN_MODE_ECB; break; + case CHAIN_MODE_CBC: str = BCRYPT_CHAIN_MODE_CBC; break; + case CHAIN_MODE_GCM: str = BCRYPT_CHAIN_MODE_GCM; break; + case CHAIN_MODE_CFB: str = BCRYPT_CHAIN_MODE_CFB; break; + default: return STATUS_NOT_IMPLEMENTED; + } + + *ret_size = 64; + if (size < *ret_size) return STATUS_BUFFER_TOO_SMALL; + memcpy( buf, str, (lstrlenW(str) + 1) * sizeof(WCHAR) ); + return STATUS_SUCCESS; + } + if (!wcscmp( prop, BCRYPT_KEY_LENGTHS )) + { + BCRYPT_KEY_LENGTHS_STRUCT *key_lengths = (void *)buf; + *ret_size = sizeof(*key_lengths); + if (key_lengths && size < *ret_size) return STATUS_BUFFER_TOO_SMALL; + if (key_lengths) + { + key_lengths->dwMinLength = 128; + key_lengths->dwMaxLength = 256; + key_lengths->dwIncrement = 64; + } + return STATUS_SUCCESS; + } + if (!wcscmp( prop, BCRYPT_AUTH_TAG_LENGTH )) + { + BCRYPT_AUTH_TAG_LENGTHS_STRUCT *tag_length = (void *)buf; + if (mode != CHAIN_MODE_GCM) return STATUS_NOT_SUPPORTED; + *ret_size = sizeof(*tag_length); + if (tag_length && size < *ret_size) return STATUS_BUFFER_TOO_SMALL; + if (tag_length) + { + tag_length->dwMinLength = 12; + tag_length->dwMaxLength = 16; + tag_length->dwIncrement = 1; + } + return STATUS_SUCCESS; + } + + FIXME( "unsupported property %s\n", debugstr_w(prop) ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS get_rc4_property( enum chain_mode mode, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +{ + if (!wcscmp( prop, BCRYPT_BLOCK_LENGTH )) + { + *ret_size = sizeof(ULONG); + if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL; + if (buf) *(ULONG *)buf = BLOCK_LENGTH_RC4; + return STATUS_SUCCESS; + } + + FIXME( "unsupported property %s\n", debugstr_w(prop) ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS get_rsa_property( enum chain_mode mode, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +{ + if (!wcscmp( prop, BCRYPT_PADDING_SCHEMES )) + { + *ret_size = sizeof(ULONG); + if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL; + if (buf) *(ULONG *)buf = BCRYPT_SUPPORTED_PAD_PKCS1_SIG | BCRYPT_SUPPORTED_PAD_OAEP; + return STATUS_SUCCESS; + } + + FIXME( "unsupported property %s\n", debugstr_w(prop) ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS get_dsa_property( enum chain_mode mode, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +{ + if (!wcscmp( prop, BCRYPT_PADDING_SCHEMES )) return STATUS_NOT_SUPPORTED; + FIXME( "unsupported property %s\n", debugstr_w(prop) ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS get_pbkdf2_property( enum chain_mode mode, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +{ + if (!wcscmp( prop, BCRYPT_BLOCK_LENGTH )) return STATUS_NOT_SUPPORTED; + if (!wcscmp( prop, BCRYPT_KEY_LENGTHS )) + { + BCRYPT_KEY_LENGTHS_STRUCT *key_lengths = (void *)buf; + *ret_size = sizeof(*key_lengths); + if (key_lengths && size < *ret_size) return STATUS_BUFFER_TOO_SMALL; + if (key_lengths) + { + key_lengths->dwMinLength = 0; + key_lengths->dwMaxLength = 16384; + key_lengths->dwIncrement = 8; + } + return STATUS_SUCCESS; + } + FIXME( "unsupported property %s\n", debugstr_w(prop) ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS get_alg_property( const struct algorithm *alg, const WCHAR *prop, UCHAR *buf, ULONG size, + ULONG *ret_size ) { NTSTATUS status; - ULONG value; - status = generic_alg_property( id, prop, buf, size, ret_size ); + status = generic_alg_property( alg->id, prop, buf, size, ret_size ); if (status != STATUS_NOT_IMPLEMENTED) return status; - switch (id) + switch (alg->id) { - case ALG_ID_MD5: - if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) + case ALG_ID_3DES: + return get_3des_property( alg->mode, prop, buf, size, ret_size ); + + case ALG_ID_AES: + return get_aes_property( alg->mode, prop, buf, size, ret_size ); + + case ALG_ID_RC4: + return get_rc4_property( alg->mode, prop, buf, size, ret_size ); + + case ALG_ID_RSA: + return get_rsa_property( alg->mode, prop, buf, size, ret_size ); + + case ALG_ID_DSA: + return get_dsa_property( alg->mode, prop, buf, size, ret_size ); + + case ALG_ID_PBKDF2: + return get_pbkdf2_property( alg->mode, prop, buf, size, ret_size ); + + default: + break; + } + + FIXME( "unsupported property %s algorithm %u\n", debugstr_w(prop), alg->id ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS set_alg_property( struct algorithm *alg, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags ) +{ + switch (alg->id) + { + case ALG_ID_3DES: + if (!wcscmp( prop, BCRYPT_CHAINING_MODE )) { - value = OBJECT_LENGTH_MD5; - break; + if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_CBC )) + { + alg->mode = CHAIN_MODE_CBC; + return STATUS_SUCCESS; + } + else + { + FIXME( "unsupported mode %s\n", debugstr_w((WCHAR *)value) ); + return STATUS_NOT_SUPPORTED; + } } - FIXME( "unsupported md5 algorithm property %s\n", debugstr_w(prop) ); + FIXME( "unsupported 3des algorithm property %s\n", debugstr_w(prop) ); return STATUS_NOT_IMPLEMENTED; - case ALG_ID_RNG: - if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) return STATUS_NOT_SUPPORTED; - FIXME( "unsupported rng algorithm property %s\n", debugstr_w(prop) ); + case ALG_ID_AES: + if (!wcscmp( prop, BCRYPT_CHAINING_MODE )) + { + if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_ECB )) + { + alg->mode = CHAIN_MODE_ECB; + return STATUS_SUCCESS; + } + else if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_CBC )) + { + alg->mode = CHAIN_MODE_CBC; + return STATUS_SUCCESS; + } + else if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_GCM )) + { + alg->mode = CHAIN_MODE_GCM; + return STATUS_SUCCESS; + } + else if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_CFB )) + { + alg->mode = CHAIN_MODE_CFB; + return STATUS_SUCCESS; + } + else + { + FIXME( "unsupported mode %s\n", debugstr_w((WCHAR *)value) ); + return STATUS_NOT_IMPLEMENTED; + } + } + FIXME( "unsupported aes algorithm property %s\n", debugstr_w(prop) ); return STATUS_NOT_IMPLEMENTED; - case ALG_ID_SHA1: - if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) + case ALG_ID_RC4: + if (!wcscmp( prop, BCRYPT_CHAINING_MODE )) { - value = OBJECT_LENGTH_SHA1; - break; - } - FIXME( "unsupported sha1 algorithm property %s\n", debugstr_w(prop) ); - return STATUS_NOT_IMPLEMENTED; + if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_NA )) return STATUS_SUCCESS; - case ALG_ID_SHA256: - if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) - { - value = OBJECT_LENGTH_SHA256; - break; + FIXME( "unsupported mode %s\n", debugstr_w((WCHAR *)value) ); + return STATUS_NOT_IMPLEMENTED; } - FIXME( "unsupported sha256 algorithm property %s\n", debugstr_w(prop) ); - return STATUS_NOT_IMPLEMENTED; - - case ALG_ID_SHA384: - if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) - { - value = OBJECT_LENGTH_SHA384; - break; - } - FIXME( "unsupported sha384 algorithm property %s\n", debugstr_w(prop) ); - return STATUS_NOT_IMPLEMENTED; - - case ALG_ID_SHA512: - if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) - { - value = OBJECT_LENGTH_SHA512; - break; - } - FIXME( "unsupported sha512 algorithm property %s\n", debugstr_w(prop) ); + FIXME( "unsupported rc4 algorithm property %s\n", debugstr_w(prop) ); return STATUS_NOT_IMPLEMENTED; default: - FIXME( "unsupported algorithm %u\n", id ); + FIXME( "unsupported algorithm %u\n", alg->id ); return STATUS_NOT_IMPLEMENTED; } - - if (size < sizeof(ULONG)) - { - *ret_size = sizeof(ULONG); - return STATUS_BUFFER_TOO_SMALL; - } - if (buf) *(ULONG *)buf = value; - *ret_size = sizeof(ULONG); - - return STATUS_SUCCESS; } -static NTSTATUS get_hash_property( enum alg_id id, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags ) +{ + if (!wcscmp( prop, BCRYPT_CHAINING_MODE )) + { + if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_ECB )) + { + key->u.s.mode = CHAIN_MODE_ECB; + return STATUS_SUCCESS; + } + else if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_CBC )) + { + key->u.s.mode = CHAIN_MODE_CBC; + return STATUS_SUCCESS; + } + else if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_GCM )) + { + key->u.s.mode = CHAIN_MODE_GCM; + return STATUS_SUCCESS; + } + else if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_CFB )) + { + key->u.s.mode = CHAIN_MODE_CFB; + return STATUS_SUCCESS; + } + else + { + FIXME( "unsupported mode %s\n", debugstr_w((WCHAR *)value) ); + return STATUS_NOT_IMPLEMENTED; + } + } + else if (!wcscmp( prop, BCRYPT_KEY_LENGTH )) + { + if (size < sizeof(DWORD)) return STATUS_INVALID_PARAMETER; + key->u.a.bitlen = *(DWORD*)value; + return STATUS_SUCCESS; + } + else if (!wcscmp( prop, BCRYPT_DH_PARAMETERS )) + { + BCRYPT_DH_PARAMETER_HEADER *hdr = (BCRYPT_DH_PARAMETER_HEADER *)value; + struct key_asymmetric_import_params params; + + if (key->u.a.flags & KEY_FLAG_FINALIZED) return STATUS_INVALID_HANDLE; + if (key->alg_id != ALG_ID_DH || size < sizeof(*hdr) || hdr->cbLength != size || + hdr->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC || hdr->cbKeyLength != key->u.a.bitlen / 8) + return STATUS_INVALID_PARAMETER; + + params.key = key; + params.flags = KEY_IMPORT_FLAG_DH_PARAMETERS; + params.buf = value; + params.len = size; + return key_asymmetric_import( ¶ms ); + } + + FIXME( "unsupported key property %s\n", debugstr_w(prop) ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS get_hash_property( const struct hash *hash, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) { NTSTATUS status; - status = generic_alg_property( id, prop, buf, size, ret_size ); + status = generic_alg_property( hash->alg_id, prop, buf, size, ret_size ); if (status == STATUS_NOT_IMPLEMENTED) FIXME( "unsupported property %s\n", debugstr_w(prop) ); return status; } -NTSTATUS WINAPI BCryptGetProperty( BCRYPT_HANDLE handle, LPCWSTR prop, UCHAR *buffer, ULONG count, ULONG *res, ULONG flags ) +static NTSTATUS get_dh_property( const struct key *key, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) { - struct object *object = handle; + struct key_asymmetric_export_params params; - TRACE( "%p, %s, %p, %u, %p, %08x\n", handle, wine_dbgstr_w(prop), buffer, count, res, flags ); + if (wcscmp( prop, BCRYPT_DH_PARAMETERS )) return STATUS_NOT_SUPPORTED; + if (!(key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE; + + params.key = (struct key *)key; + params.flags = KEY_EXPORT_FLAG_DH_PARAMETERS; + params.buf = buf; + params.len = size; + params.ret_len = ret_size; + return key_asymmetric_export( ¶ms ); +} + +static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +{ + if (!wcscmp( prop, BCRYPT_KEY_STRENGTH )) + { + *ret_size = sizeof(DWORD); + if (size < sizeof(DWORD)) return STATUS_BUFFER_TOO_SMALL; + if (buf) + { + if (is_symmetric_key(key)) *(DWORD *)buf = key->u.s.block_size * 8; + else *(DWORD *)buf = key->u.a.bitlen; + } + return STATUS_SUCCESS; + } + + switch (key->alg_id) + { + case ALG_ID_3DES: + return get_3des_property( key->u.s.mode, prop, buf, size, ret_size ); + + case ALG_ID_AES: + if (!wcscmp( prop, BCRYPT_AUTH_TAG_LENGTH )) return STATUS_NOT_SUPPORTED; + return get_aes_property( key->u.s.mode, prop, buf, size, ret_size ); + + case ALG_ID_DH: + return get_dh_property( key, prop, buf, size, ret_size ); + + default: + FIXME( "unsupported algorithm %u\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } +} + +NTSTATUS WINAPI BCryptGetProperty( BCRYPT_HANDLE handle, const WCHAR *prop, UCHAR *buffer, ULONG count, ULONG *res, + ULONG flags ) +{ + struct object *object = get_object( handle, 0 ); + + TRACE( "%p, %s, %p, %lu, %p, %#lx\n", handle, wine_dbgstr_w(prop), buffer, count, res, flags ); if (!object) return STATUS_INVALID_HANDLE; if (!prop || !res) return STATUS_INVALID_PARAMETER; @@ -989,140 +868,1877 @@ NTSTATUS WINAPI BCryptGetProperty( BCRYPT_HANDLE handle, LPCWSTR prop, UCHAR *bu case MAGIC_ALG: { const struct algorithm *alg = (const struct algorithm *)object; - return get_alg_property( alg->id, prop, buffer, count, res ); + return get_alg_property( alg, prop, buffer, count, res ); + } + case MAGIC_KEY: + { + const struct key *key = (const struct key *)object; + return get_key_property( key, prop, buffer, count, res ); } case MAGIC_HASH: { const struct hash *hash = (const struct hash *)object; - return get_hash_property( hash->alg_id, prop, buffer, count, res ); + return get_hash_property( hash, prop, buffer, count, res ); } default: - WARN( "unknown magic %08x\n", object->magic ); + WARN( "unknown magic %#lx\n", object->magic ); return STATUS_INVALID_HANDLE; } } -NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDLE *handle, UCHAR *object, ULONG objectlen, - UCHAR *secret, ULONG secretlen, ULONG flags ) +static void hash_prepare( struct hash *hash ) { - struct algorithm *alg = algorithm; +#ifndef __REACTOS__ + UCHAR buffer[MAX_HASH_BLOCK_BITS / 8] = {0}; + ULONG block_bytes, i; + struct hash *hash_temp; +#endif + + /* initialize hash */ + hash_init( hash ); + if (!(hash->flags & HASH_FLAG_HMAC)) return; + +#ifndef __REACTOS__ + /* initialize hmac */ + hmac_init( hash ); + hash_get_size(hash, &block_bytes); + memset( buffer, 0, sizeof(buffer) ); + if (hash->secret_len > block_bytes) + { + if (!(hash_temp = calloc( 1, sizeof(*hash_temp) ))) return; + hash_temp->hdr.magic = MAGIC_HASH; + hash_temp->alg_id = hash->alg_id; + + hash_init( hash_temp ); + hash_update( hash_temp, hash->secret, hash->secret_len ); + hash_finish( hash_temp, buffer ); + free( hash_temp ); + } + else memcpy( buffer, hash->secret, hash->secret_len ); + + for (i = 0; i < block_bytes; i++) buffer[i] ^= 0x5c; + hmac_update( hash, buffer, block_bytes ); + for (i = 0; i < block_bytes; i++) buffer[i] ^= (0x5c ^ 0x36); + hash_update( hash, buffer, block_bytes ); +#endif +} + +static NTSTATUS hash_create( const struct algorithm *alg, UCHAR *secret, ULONG secret_len, ULONG flags, + struct hash **ret_hash ) +{ + struct hash *hash; + if (!(hash = calloc( 1, sizeof(*hash) ))) return STATUS_NO_MEMORY; + hash->hdr.magic = MAGIC_HASH; + hash->alg_id = alg->id; + + if (alg->flags & BCRYPT_ALG_HANDLE_HMAC_FLAG) hash->flags = HASH_FLAG_HMAC; + if ((alg->flags & BCRYPT_HASH_REUSABLE_FLAG) || (flags & BCRYPT_HASH_REUSABLE_FLAG)) + hash->flags |= HASH_FLAG_REUSABLE; + + if (secret_len && !(hash->secret = malloc( secret_len ))) + { + free( hash ); + return STATUS_NO_MEMORY; + } + memcpy( hash->secret, secret, secret_len ); + hash->secret_len = secret_len; + + hash_prepare( hash ); + *ret_hash = hash; + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE handle, BCRYPT_HASH_HANDLE *ret_handle, UCHAR *object, + ULONG object_len, UCHAR *secret, ULONG secret_len, ULONG flags ) +{ + struct algorithm *alg = get_alg_object( handle ); struct hash *hash; NTSTATUS status; - TRACE( "%p, %p, %p, %u, %p, %u, %08x - stub\n", algorithm, handle, object, objectlen, - secret, secretlen, flags ); - if (flags) + TRACE( "%p, %p, %p, %lu, %p, %lu, %#lx\n", handle, ret_handle, object, object_len, secret, secret_len, flags ); + if (flags & ~BCRYPT_HASH_REUSABLE_FLAG) { - FIXME( "unimplemented flags %08x\n", flags ); + FIXME( "unimplemented flags %#lx\n", flags ); return STATUS_NOT_IMPLEMENTED; } - - if (!alg || alg->hdr.magic != MAGIC_ALG) return STATUS_INVALID_HANDLE; if (object) FIXME( "ignoring object buffer\n" ); - if (!(hash = HeapAlloc( GetProcessHeap(), 0, sizeof(*hash) ))) return STATUS_NO_MEMORY; - hash->hdr.magic = MAGIC_HASH; - hash->alg_id = alg->id; - hash->hmac = alg->hmac; + if (!alg) return STATUS_INVALID_HANDLE; + if (!ret_handle) return STATUS_INVALID_PARAMETER; - if (hash->hmac) - { - status = hmac_init( hash, secret, secretlen ); - } - else - { - status = hash_init( hash ); - } - - if (status != STATUS_SUCCESS) - { - HeapFree( GetProcessHeap(), 0, hash ); - return status; - } - - *handle = hash; + if ((status = hash_create( alg, secret, secret_len, flags, &hash ))) return status; + *ret_handle = hash; + TRACE( "returning handle %p\n", *ret_handle ); return STATUS_SUCCESS; } +NTSTATUS WINAPI BCryptDuplicateHash( BCRYPT_HASH_HANDLE handle, BCRYPT_HASH_HANDLE *handle_copy, + UCHAR *object, ULONG objectlen, ULONG flags ) +{ + struct hash *hash_orig = get_hash_object( handle ); + struct hash *hash_copy; + + TRACE( "%p, %p, %p, %lu, %#lx\n", handle, handle_copy, object, objectlen, flags ); + + if (!hash_orig) return STATUS_INVALID_HANDLE; + if (!handle_copy) return STATUS_INVALID_PARAMETER; + if (object) FIXME( "ignoring object buffer\n" ); + + if (!(hash_copy = malloc( sizeof(*hash_copy) ))) return STATUS_NO_MEMORY; + + memcpy( hash_copy, hash_orig, sizeof(*hash_orig) ); + if (hash_orig->secret && !(hash_copy->secret = malloc( hash_orig->secret_len ))) + { + free( hash_copy ); + return STATUS_NO_MEMORY; + } + memcpy( hash_copy->secret, hash_orig->secret, hash_orig->secret_len ); + + *handle_copy = hash_copy; + TRACE( "returning handle %p\n", *handle_copy ); + return STATUS_SUCCESS; +} + +static void hash_destroy( struct hash *hash ) +{ + if (!hash) return; + free( hash->secret ); + destroy_object( &hash->hdr ); +} + NTSTATUS WINAPI BCryptDestroyHash( BCRYPT_HASH_HANDLE handle ) { - struct hash *hash = handle; + struct hash *hash = get_hash_object( handle ); TRACE( "%p\n", handle ); - if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE; - HeapFree( GetProcessHeap(), 0, hash ); + if (!hash) return STATUS_INVALID_PARAMETER; + hash_destroy( hash ); return STATUS_SUCCESS; } NTSTATUS WINAPI BCryptHashData( BCRYPT_HASH_HANDLE handle, UCHAR *input, ULONG size, ULONG flags ) { - struct hash *hash = handle; + struct hash *hash = get_hash_object( handle ); - TRACE( "%p, %p, %u, %08x\n", handle, input, size, flags ); + TRACE( "%p, %p, %lu, %#lx\n", handle, input, size, flags ); - if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE; + if (!hash) return STATUS_INVALID_HANDLE; if (!input) return STATUS_SUCCESS; - if (hash->hmac) + if (hash_update( hash, input, size )) return STATUS_INVALID_PARAMETER; + + return STATUS_SUCCESS; +} + +static void hash_finalize( struct hash *hash, UCHAR *output, ULONG size ) +{ +#ifndef __REACTOS__ + UCHAR buffer[MAX_HASH_OUTPUT_BYTES]; + ULONG hash_size; + + if (!(hash->flags & HASH_FLAG_HMAC)) { - return hmac_update( hash, input, size ); - } - else - { - return hash_update( hash, input, size ); +#endif + hash_finish( hash, output ); + if (hash->flags & HASH_FLAG_REUSABLE) hash_prepare( hash ); +#ifndef __REACTOS__ + return; } + + hash_get_size( hash, &hash_size ); + hash_finish( hash, buffer ); + hmac_update( hash, buffer, hash_size ); + hmac_finish( hash, output ); + + if (hash->flags & HASH_FLAG_REUSABLE) hash_prepare( hash ); +#endif } NTSTATUS WINAPI BCryptFinishHash( BCRYPT_HASH_HANDLE handle, UCHAR *output, ULONG size, ULONG flags ) { - struct hash *hash = handle; + struct hash *hash = get_hash_object( handle ); + ULONG hash_size; - TRACE( "%p, %p, %u, %08x\n", handle, output, size, flags ); + TRACE( "%p, %p, %lu, %#lx\n", handle, output, size, flags ); - if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE; + if (!hash) return STATUS_INVALID_HANDLE; if (!output) return STATUS_INVALID_PARAMETER; + hash_get_size( hash, &hash_size ); + if (size != hash_size) return STATUS_INVALID_PARAMETER; - if (hash->hmac) + hash_finalize( hash, output, size ); + return STATUS_SUCCESS; +} + +static NTSTATUS hash_single( struct algorithm *alg, UCHAR *secret, ULONG secret_len, UCHAR *input, ULONG input_len, + UCHAR *output, ULONG output_len ) +{ + struct hash *hash; + NTSTATUS status; + + if ((status = hash_create( alg, secret, secret_len, 0, &hash ))) return status; + if (input_len && hash_update( hash, input, input_len )) { - return hmac_finish( hash, output, size ); + hash_destroy( hash ); + return STATUS_INVALID_PARAMETER; + } + hash_finalize( hash, output, output_len ); + hash_destroy( hash ); + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE handle, UCHAR *secret, ULONG secret_len, UCHAR *input, ULONG input_len, + UCHAR *output, ULONG output_len ) +{ + struct algorithm *alg = get_alg_object( handle ); + + TRACE( "%p, %p, %lu, %p, %lu, %p, %lu\n", handle, secret, secret_len, input, input_len, output, output_len ); + + if (!alg) return STATUS_INVALID_HANDLE; + if (!output || output_len != builtin_algorithms[alg->id].hash_length) return STATUS_INVALID_PARAMETER; + + return hash_single(alg, secret, secret_len, input, input_len, output, output_len ); +} + +static NTSTATUS key_asymmetric_create( enum alg_id alg_id, ULONG bitlen, struct key **ret_key ) +{ + struct key *key; + + if (alg_id == ALG_ID_DH && bitlen < 512) return STATUS_INVALID_PARAMETER; + + if (!(key = calloc( 1, sizeof(*key) ))) return STATUS_NO_MEMORY; + key->hdr.magic = MAGIC_KEY; + key->alg_id = alg_id; + key->u.a.bitlen = bitlen; + + *ret_key = key; + return STATUS_SUCCESS; +} + +static BOOL is_equal_vector( const UCHAR *vector, ULONG len, const UCHAR *vector2, ULONG len2 ) +{ + if (!vector && !vector2) return TRUE; + if (len != len2) return FALSE; + return !memcmp( vector, vector2, len ); +} + +static NTSTATUS key_symmetric_set_vector( struct key *key, UCHAR *vector, ULONG vector_len, BOOL force_reset ) +{ + BOOL needs_reset = force_reset || !is_equal_vector( key->u.s.vector, key->u.s.vector_len, vector, vector_len ); + if (vector) + { + free( key->u.s.vector ); + key->u.s.vector = NULL; + key->u.s.vector_len = 0; + + if (!(key->u.s.vector = malloc( vector_len ))) return STATUS_NO_MEMORY; + memcpy( key->u.s.vector, vector, vector_len ); + key->u.s.vector_len = vector_len; + } + if (needs_reset) key_symmetric_vector_reset( key ); + return STATUS_SUCCESS; +} + +static struct key *key_symmetric_create( enum alg_id alg, enum chain_mode mode, ULONG block_size, const UCHAR *secret, + ULONG secret_len ) +{ + struct key *ret; + + if (!(ret = calloc( 1, sizeof(*ret) ))) return NULL; + InitializeCriticalSection( &ret->u.s.cs ); + ret->hdr.magic = MAGIC_KEY; + ret->alg_id = alg; + ret->u.s.mode = mode; + ret->u.s.block_size = block_size; + + if (!(ret->u.s.secret = malloc( secret_len ))) + { + DeleteCriticalSection( &ret->u.s.cs ); + free( ret ); + return NULL; + } + memcpy( ret->u.s.secret, secret, secret_len ); + ret->u.s.secret_len = secret_len; + + return ret; +} + +static void key_destroy( struct key *key ) +{ + if (is_symmetric_key( key )) + { + key_symmetric_destroy( key ); + free( key->u.s.vector ); + free( key->u.s.secret ); + DeleteCriticalSection( &key->u.s.cs ); + } + else + key_asymmetric_destroy( key ); + + destroy_object( &key->hdr ); +} + +static ULONG get_block_size( struct algorithm *alg ) +{ + ULONG ret = 0, size = sizeof(ret); + get_alg_property( alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&ret, sizeof(ret), &size ); + return ret; +} + +static NTSTATUS key_symmetric_generate( struct algorithm *alg, BCRYPT_KEY_HANDLE *ret_handle, const UCHAR *secret, + ULONG secret_len ) +{ + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; + ULONG block_size, size; + struct key *key; + NTSTATUS status; + + if (alg->id == ALG_ID_PBKDF2 && + !get_alg_property( alg, BCRYPT_KEY_LENGTHS, (UCHAR *)&key_lengths, sizeof(key_lengths), &size )) + { + if (secret_len > key_lengths.dwMaxLength / 8 || secret_len < key_lengths.dwMinLength / 8) + return STATUS_INVALID_PARAMETER; + block_size = secret_len; + } + else if (!(block_size = get_block_size( alg ))) return STATUS_INVALID_PARAMETER; + else if (!get_alg_property( alg, BCRYPT_KEY_LENGTHS, (UCHAR *)&key_lengths, sizeof(key_lengths), &size )) + { + if (secret_len > (size = key_lengths.dwMaxLength / 8)) + { + WARN( "secret_len %lu exceeds key max length %lu, setting to maximum\n", secret_len, size ); + secret_len = size; + } + else if (secret_len < (size = key_lengths.dwMinLength / 8)) + { + WARN( "secret_len %lu is less than minimum key length %lu\n", secret_len, size ); + return STATUS_INVALID_PARAMETER; + } + else if (key_lengths.dwIncrement && (secret_len * 8 - key_lengths.dwMinLength) % key_lengths.dwIncrement) + { + WARN( "secret_len %lu is not a valid key length\n", secret_len ); + return STATUS_INVALID_PARAMETER; + } + } + + if (!(key = key_symmetric_create( alg->id, alg->mode, block_size, secret, secret_len ))) status = STATUS_NO_MEMORY; + else + { + *ret_handle = key; + status = STATUS_SUCCESS; + } + + return status; +} + +static NTSTATUS key_symmetric_decrypt( struct key *key, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv, + ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags ) +{ + struct key_symmetric_set_auth_data_params auth_params; + struct key_symmetric_decrypt_params decrypt_params; + struct key_symmetric_get_tag_params tag_params; + ULONG bytes_left = input_len; + NTSTATUS status; + + if (key->u.s.mode == CHAIN_MODE_GCM) + { + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO *auth_info = padding; +#ifndef __REACTOS__ + UCHAR tag[16]; +#endif + + if (!auth_info) return STATUS_INVALID_PARAMETER; + if (!auth_info->pbNonce) return STATUS_INVALID_PARAMETER; + if (!auth_info->pbTag) return STATUS_INVALID_PARAMETER; + if (auth_info->cbTag < 12 || auth_info->cbTag > 16) return STATUS_INVALID_PARAMETER; + + if ((status = key_symmetric_set_vector( key, auth_info->pbNonce, auth_info->cbNonce, TRUE ))) + return status; + + *ret_len = input_len; + if (flags & BCRYPT_BLOCK_PADDING) return STATUS_INVALID_PARAMETER; + if (!output) return STATUS_SUCCESS; + if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL; + + auth_params.key = key; + auth_params.auth_data = auth_info->pbAuthData; + auth_params.len = auth_info->cbAuthData; +#ifdef __REACTOS__ + auth_params.encrypt = FALSE; +#endif + if ((status = key_symmetric_set_auth_data( &auth_params ))) return status; + + decrypt_params.key = key; + decrypt_params.input = input; + decrypt_params.input_len = input_len; + decrypt_params.output = output; + decrypt_params.output_len = output_len; + if ((status = key_symmetric_decrypt_internal( &decrypt_params ))) return status; + +#ifndef __REACTOS__ + tag_params.key = key; + tag_params.tag = tag; + tag_params.len = sizeof(tag); + if ((status = key_symmetric_get_tag( &tag_params ))) return status; + if (memcmp( tag, auth_info->pbTag, auth_info->cbTag )) return STATUS_AUTH_TAG_MISMATCH; +#else + tag_params.key = key; + tag_params.tag = auth_info->pbTag; + tag_params.len = auth_info->cbTag; + if ((status = key_symmetric_get_tag( &tag_params ))) return status; +#endif + + return STATUS_SUCCESS; + } + + *ret_len = input_len; + + if (input_len & (key->u.s.block_size - 1)) return STATUS_INVALID_BUFFER_SIZE; + if (!output) return STATUS_SUCCESS; + if (flags & BCRYPT_BLOCK_PADDING) + { + if (output_len + key->u.s.block_size < *ret_len) return STATUS_BUFFER_TOO_SMALL; + if (input_len < key->u.s.block_size) return STATUS_BUFFER_TOO_SMALL; + bytes_left -= key->u.s.block_size; + } + else if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL; + + if (key->u.s.mode == CHAIN_MODE_ECB && iv) return STATUS_INVALID_PARAMETER; + if ((status = key_symmetric_set_vector( key, iv, iv_len, flags & BCRYPT_BLOCK_PADDING ))) return status; + + decrypt_params.key = key; + decrypt_params.input = input; + decrypt_params.input_len = key->u.s.block_size; + decrypt_params.output = output; + decrypt_params.output_len = key->u.s.block_size; + while (bytes_left >= key->u.s.block_size) + { + if ((status = key_symmetric_decrypt_internal( &decrypt_params ))) return status; + if (key->u.s.mode == CHAIN_MODE_ECB && (status = key_symmetric_set_vector( key, NULL, 0, TRUE ))) + return status; + bytes_left -= key->u.s.block_size; + decrypt_params.input += key->u.s.block_size; + decrypt_params.output += key->u.s.block_size; + } + + if (flags & BCRYPT_BLOCK_PADDING) + { + UCHAR *buf, *dst = decrypt_params.output; + if (!(buf = malloc( key->u.s.block_size ))) return STATUS_NO_MEMORY; + decrypt_params.output = buf; + status = key_symmetric_decrypt_internal( &decrypt_params ); + if (!status && buf[ key->u.s.block_size - 1 ] <= key->u.s.block_size) + { + *ret_len -= buf[ key->u.s.block_size - 1 ]; + if (output_len < *ret_len) status = STATUS_BUFFER_TOO_SMALL; + else memcpy( dst, buf, key->u.s.block_size - buf[ key->u.s.block_size - 1 ] ); + } + else status = STATUS_UNSUCCESSFUL; /* FIXME: invalid padding */ + free( buf ); + } + + if (!status) + { + if (key->u.s.vector && input_len >= key->u.s.vector_len) + { + memcpy( key->u.s.vector, input + input_len - key->u.s.vector_len, key->u.s.vector_len ); + if (iv) memcpy( iv, key->u.s.vector, min( iv_len, key->u.s.vector_len )); + } + else if (key->u.s.vector) + FIXME( "Unexpected vector len %lu, *ret_len %lu.\n", key->u.s.vector_len, *ret_len ); + } + + return status; +} + +/* AES Key Wrap Algorithm (RFC3394) */ +static NTSTATUS aes_unwrap( const UCHAR *secret, ULONG secret_len, const UCHAR *cipher, ULONG cipher_len, UCHAR *plain ) +{ + UCHAR a[8], *r, b[16]; + ULONG len, t, i, n = cipher_len / 8; + int j; + struct key *key; + + memcpy( a, cipher, 8 ); + r = plain; + memcpy( r, cipher + 8, 8 * n ); + + if (!(key = key_symmetric_create( ALG_ID_AES, CHAIN_MODE_ECB, 16, secret, secret_len ))) return STATUS_NO_MEMORY; + + for (j = 5; j >= 0; j--) + { + r = plain + (n - 1) * 8; + for (i = n; i >= 1; i--) + { + memcpy( b, a, 8 ); + t = n * j + i; + b[7] ^= t; + b[6] ^= t >> 8; + b[5] ^= t >> 16; + b[4] ^= t >> 24; + + memcpy( b + 8, r, 8 ); + key_symmetric_decrypt( key, b, 16, NULL, NULL, 0, b, 16, &len, 0 ); + memcpy( a, b, 8 ); + memcpy( r, b + 8, 8 ); + r -= 8; + } + } + + key_destroy( key ); + + for (i = 0; i < 8; i++) if (a[i] != 0xa6) return STATUS_UNSUCCESSFUL; + return STATUS_SUCCESS; +} + +static NTSTATUS key_import( struct algorithm *alg, struct key *decrypt_key, const WCHAR *type, BCRYPT_KEY_HANDLE *key, + UCHAR *object, ULONG object_len, UCHAR *input, ULONG input_len ) +{ + ULONG len; + NTSTATUS status; + + if (decrypt_key && wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB )) + { + FIXME( "decryption of key not supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + + if (!wcscmp( type, BCRYPT_KEY_DATA_BLOB )) + { + BCRYPT_KEY_DATA_BLOB_HEADER *header = (BCRYPT_KEY_DATA_BLOB_HEADER *)input; + + if (input_len < sizeof(BCRYPT_KEY_DATA_BLOB_HEADER)) return STATUS_BUFFER_TOO_SMALL; + if (header->dwMagic != BCRYPT_KEY_DATA_BLOB_MAGIC) return STATUS_INVALID_PARAMETER; + if (header->dwVersion != BCRYPT_KEY_DATA_BLOB_VERSION1) + { + FIXME( "unknown key data blob version %lu\n", header->dwVersion ); + return STATUS_INVALID_PARAMETER; + } + len = header->cbKeyData; + if (len + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) > input_len) return STATUS_INVALID_PARAMETER; + + return key_symmetric_generate( alg, key, (UCHAR *)&header[1], len ); + } + else if (!wcscmp( type, BCRYPT_OPAQUE_KEY_BLOB )) + { + if (input_len < sizeof(len)) return STATUS_BUFFER_TOO_SMALL; + len = *(ULONG *)input; + if (len + sizeof(len) > input_len) return STATUS_INVALID_PARAMETER; + + return key_symmetric_generate( alg, key, input + sizeof(len), len ); + } + else if (!wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB )) + { + UCHAR output[32]; + + if (!decrypt_key || input_len < 8) return STATUS_INVALID_PARAMETER; + + len = input_len - 8; + if (len < BLOCK_LENGTH_AES || len & (BLOCK_LENGTH_AES - 1)) return STATUS_INVALID_PARAMETER; + + if ((status = aes_unwrap( decrypt_key->u.s.secret, decrypt_key->u.s.secret_len, input, len, output ))) + return status; + + return key_symmetric_generate( alg, key, output, len ); + } + + FIXME( "unsupported key type %s\n", debugstr_w(type) ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS key_symmetric_encrypt( struct key *key, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv, + ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags ) +{ + struct key_symmetric_set_auth_data_params auth_params; + struct key_symmetric_encrypt_params encrypt_params; + struct key_symmetric_get_tag_params tag_params; + ULONG bytes_left = input_len; + UCHAR *buf; + NTSTATUS status; + + if (key->u.s.mode == CHAIN_MODE_GCM) + { + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO *auth_info = padding; + + if (!auth_info) return STATUS_INVALID_PARAMETER; + if (!auth_info->pbNonce) return STATUS_INVALID_PARAMETER; + if (!auth_info->pbTag) return STATUS_INVALID_PARAMETER; + if (auth_info->cbTag < 12 || auth_info->cbTag > 16) return STATUS_INVALID_PARAMETER; + if (auth_info->dwFlags & BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG) + FIXME( "call chaining not implemented\n" ); + + if ((status = key_symmetric_set_vector( key, auth_info->pbNonce, auth_info->cbNonce, TRUE ))) + return status; + + *ret_len = input_len; + if (flags & BCRYPT_BLOCK_PADDING) return STATUS_INVALID_PARAMETER; + if (input && !output) return STATUS_SUCCESS; + if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL; + + auth_params.key = key; + auth_params.auth_data = auth_info->pbAuthData; + auth_params.len = auth_info->cbAuthData; +#ifdef __REACTOS__ + auth_params.encrypt = TRUE; +#endif + if ((status = key_symmetric_set_auth_data( &auth_params ))) return status; + + encrypt_params.key = key; + encrypt_params.input = input; + encrypt_params.input_len = input_len; + encrypt_params.output = output; + encrypt_params.output_len = output_len; + if ((status = key_symmetric_encrypt_internal( &encrypt_params ))) return status; + + tag_params.key = key; + tag_params.tag = auth_info->pbTag; + tag_params.len = auth_info->cbTag; + return key_symmetric_get_tag( &tag_params ); + } + + *ret_len = input_len; + + if (flags & BCRYPT_BLOCK_PADDING) + *ret_len = (input_len + key->u.s.block_size) & ~(key->u.s.block_size - 1); + else if (input_len & (key->u.s.block_size - 1)) + return STATUS_INVALID_BUFFER_SIZE; + + if (!output) return STATUS_SUCCESS; + if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL; + if (key->u.s.mode == CHAIN_MODE_ECB && iv) return STATUS_INVALID_PARAMETER; + if ((status = key_symmetric_set_vector( key, iv, iv_len, flags & BCRYPT_BLOCK_PADDING ))) return status; + + encrypt_params.key = key; + encrypt_params.input = input; + encrypt_params.input_len = key->u.s.block_size; + encrypt_params.output = output; + encrypt_params.output_len = key->u.s.block_size; + while (bytes_left >= key->u.s.block_size) + { + if ((status = key_symmetric_encrypt_internal( &encrypt_params ))) + return status; + if (key->u.s.mode == CHAIN_MODE_ECB && (status = key_symmetric_set_vector( key, NULL, 0, TRUE ))) + return status; + bytes_left -= key->u.s.block_size; + encrypt_params.input += key->u.s.block_size; + encrypt_params.output += key->u.s.block_size; + } + + if (flags & BCRYPT_BLOCK_PADDING) + { + if (!(buf = malloc( key->u.s.block_size ))) return STATUS_NO_MEMORY; + memcpy( buf, encrypt_params.input, bytes_left ); + memset( buf + bytes_left, key->u.s.block_size - bytes_left, key->u.s.block_size - bytes_left ); + encrypt_params.input = buf; + status = key_symmetric_encrypt_internal( &encrypt_params ); + free( buf ); + } + + if (!status) + { + if (key->u.s.vector && *ret_len >= key->u.s.vector_len) + { + memcpy( key->u.s.vector, output + *ret_len - key->u.s.vector_len, key->u.s.vector_len ); + if (iv) memcpy( iv, key->u.s.vector, min( iv_len, key->u.s.vector_len )); + } + else if (key->u.s.vector) + FIXME( "Unexpected vector len %lu, *ret_len %lu.\n", key->u.s.vector_len, *ret_len ); + } + + return status; +} + +/* AES Key Wrap Algorithm (RFC3394) */ +static NTSTATUS aes_wrap( const UCHAR *secret, ULONG secret_len, const UCHAR *plain, ULONG plain_len, UCHAR *cipher ) +{ + UCHAR *a, *r, b[16]; + ULONG len, t, i, j, n = plain_len / 8; + struct key *key; + + a = cipher; + r = cipher + 8; + + memset( a, 0xa6, 8 ); + memcpy( r, plain, 8 * n ); + + if (!(key = key_symmetric_create( ALG_ID_AES, CHAIN_MODE_ECB, 16, secret, secret_len ))) return STATUS_NO_MEMORY; + + for (j = 0; j <= 5; j++) + { + r = cipher + 8; + for (i = 1; i <= n; i++) + { + memcpy( b, a, 8 ); + memcpy( b + 8, r, 8 ); + key_symmetric_encrypt( key, b, 16, NULL, NULL, 0, b, 16, &len, 0 ); + memcpy( a, b, 8 ); + t = n * j + i; + a[7] ^= t; + a[6] ^= t >> 8; + a[5] ^= t >> 16; + a[4] ^= t >> 24; + memcpy( r, b + 8, 8 ); + r += 8; + } + } + + key_destroy( key ); + return STATUS_SUCCESS; +} + +static NTSTATUS key_export( struct key *key, struct key *encrypt_key, const WCHAR *type, UCHAR *output, + ULONG output_len, ULONG *size ) +{ + struct key_asymmetric_export_params params; + NTSTATUS status; + + if (encrypt_key && wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB )) + { + FIXME( "encryption of key not supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + + if (!wcscmp( type, BCRYPT_KEY_DATA_BLOB )) + { + BCRYPT_KEY_DATA_BLOB_HEADER *header = (BCRYPT_KEY_DATA_BLOB_HEADER *)output; + ULONG req_size = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + key->u.s.secret_len; + + *size = req_size; + if (output_len < req_size) return STATUS_BUFFER_TOO_SMALL; + if (output) + { + header->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC; + header->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1; + header->cbKeyData = key->u.s.secret_len; + memcpy( &header[1], key->u.s.secret, key->u.s.secret_len ); + } + return STATUS_SUCCESS; + } + else if (!wcscmp( type, BCRYPT_OPAQUE_KEY_BLOB )) + { + ULONG len, req_size = sizeof(len) + key->u.s.secret_len; + + *size = req_size; + if (output_len < req_size) return STATUS_BUFFER_TOO_SMALL; + if (output) + { + *(ULONG *)output = key->u.s.secret_len; + memcpy( output + sizeof(len), key->u.s.secret, key->u.s.secret_len ); + } + return STATUS_SUCCESS; + } + else if (!wcscmp( type, BCRYPT_DSA_PRIVATE_BLOB ) || !wcscmp( type, LEGACY_DSA_V2_PRIVATE_BLOB ) || + !wcscmp( type, BCRYPT_ECCPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_DH_PRIVATE_BLOB )) + { + params.key = key; + params.flags = 0; + params.buf = output; + params.len = output_len; + params.ret_len = size; + return key_asymmetric_export( ¶ms ); + } + else if (!wcscmp( type, BCRYPT_RSAPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_RSAFULLPRIVATE_BLOB )) + { + params.key = key; + params.flags = (wcscmp( type, BCRYPT_RSAPRIVATE_BLOB )) ? KEY_EXPORT_FLAG_RSA_FULL : 0; + params.buf = output; + params.len = output_len; + params.ret_len = size; + return key_asymmetric_export( ¶ms ); + } + else if (!wcscmp( type, BCRYPT_DSA_PUBLIC_BLOB ) || !wcscmp( type, LEGACY_DSA_V2_PUBLIC_BLOB ) || + !wcscmp( type, BCRYPT_ECCPUBLIC_BLOB ) || !wcscmp( type, BCRYPT_RSAPUBLIC_BLOB ) || + !wcscmp( type, BCRYPT_DH_PUBLIC_BLOB )) + { + params.key = key; + params.flags = KEY_EXPORT_FLAG_PUBLIC; + params.buf = output; + params.len = output_len; + params.ret_len = size; + return key_asymmetric_export( ¶ms ); + } + else if (!wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB )) + { + ULONG req_size = key->u.s.secret_len + 8; + + if (!encrypt_key) return STATUS_INVALID_PARAMETER; + + *size = req_size; + if (output) + { + if (output_len < req_size) return STATUS_BUFFER_TOO_SMALL; + if ((status = aes_wrap( encrypt_key->u.s.secret, encrypt_key->u.s.secret_len, key->u.s.secret, key->u.s.secret_len, output ))) + return status; + } + return STATUS_SUCCESS; + } + + FIXME( "unsupported key type %s\n", debugstr_w(type) ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS convert_legacy_rsaprivate_blob( struct algorithm *alg, BCRYPT_RSAKEY_BLOB **rsa_data, + ULONG *rsa_len, UCHAR *input, ULONG input_len ) +{ + struct { + PUBLICKEYSTRUC header; + RSAPUBKEY rsapubkey; + } *blob = (void *)input; + ULONG len, pos; + UCHAR *conv; + int i; + + if (alg->id != ALG_ID_RSA) return STATUS_NOT_SUPPORTED; + if (input_len < sizeof(*blob)) return NTE_BAD_DATA; + if (blob->header.bType != PRIVATEKEYBLOB || blob->header.bVersion != CUR_BLOB_VERSION || + blob->header.aiKeyAlg != CALG_RSA_KEYX || + blob->rsapubkey.magic != BCRYPT_RSAPRIVATE_MAGIC || + input_len != sizeof(*blob) + blob->rsapubkey.bitlen / 16 * 9) return NTE_BAD_DATA; + if (blob->rsapubkey.bitlen & 0xf) return STATUS_INVALID_PARAMETER; + + len = sizeof(**rsa_data) + sizeof(blob->rsapubkey.pubexp) + blob->rsapubkey.bitlen / 4; + if (!(conv = malloc( len ))) return STATUS_NO_MEMORY; + + *rsa_data = (BCRYPT_RSAKEY_BLOB *)conv; + (*rsa_data)->Magic = blob->rsapubkey.magic; + (*rsa_data)->BitLength = blob->rsapubkey.bitlen; + (*rsa_data)->cbPublicExp = sizeof(blob->rsapubkey.pubexp); + (*rsa_data)->cbModulus = blob->rsapubkey.bitlen / 8; + (*rsa_data)->cbPrime1 = blob->rsapubkey.bitlen / 16; + (*rsa_data)->cbPrime2 = blob->rsapubkey.bitlen / 16; + len = sizeof(**rsa_data); + + for(i = 0; i < (*rsa_data)->cbPublicExp; i++) + conv[len++] = ((UCHAR *)&blob->rsapubkey.pubexp)[(*rsa_data)->cbPublicExp - i - 1]; + pos = sizeof(*blob); + for(i = 0; i < (*rsa_data)->cbModulus; i++) + conv[len++] = input[pos + (*rsa_data)->cbModulus - i - 1]; + pos += (*rsa_data)->cbModulus; + for(i = 0; i < (*rsa_data)->cbPrime1; i++) + conv[len++] = input[pos + (*rsa_data)->cbPrime1 - i - 1]; + pos += (*rsa_data)->cbPrime1; + for(i = 0; i < (*rsa_data)->cbPrime2; i++) + conv[len++] = input[pos + (*rsa_data)->cbPrime2 - i - 1]; + + *rsa_len = len; + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY_HANDLE *ret_key, UCHAR *input, + ULONG input_len ) +{ + struct key_asymmetric_import_params params; + struct key *key; + NTSTATUS status; + ULONG size; + + if (!wcscmp( type, BCRYPT_ECCPUBLIC_BLOB )) + { + BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)input; + DWORD key_size, magic; + + if (input_len < sizeof(*ecc_blob)) return STATUS_INVALID_PARAMETER; + + switch (alg->id) + { + case ALG_ID_ECDH_P256: + key_size = 32; + magic = BCRYPT_ECDH_PUBLIC_P256_MAGIC; + break; + + case ALG_ID_ECDH_P384: + key_size = 48; + magic = BCRYPT_ECDH_PUBLIC_P384_MAGIC; + break; + + case ALG_ID_ECDSA_P256: + key_size = 32; + magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC; + break; + + case ALG_ID_ECDSA_P384: + key_size = 48; + magic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC; + break; + + default: + FIXME( "algorithm %u does not yet support importing blob of type %s\n", alg->id, debugstr_w(type) ); + return STATUS_NOT_SUPPORTED; + } + + if (ecc_blob->dwMagic != magic) return STATUS_INVALID_PARAMETER; + if (ecc_blob->cbKey != key_size || input_len < sizeof(*ecc_blob) + ecc_blob->cbKey * 2) + return STATUS_INVALID_PARAMETER; + + if ((status = key_asymmetric_create( alg->id, key_size * 8, &key ))) return status; + params.key = key; + params.flags = KEY_IMPORT_FLAG_PUBLIC; + params.buf = input; + params.len = input_len; + if ((status = key_asymmetric_import( ¶ms ))) + { + key_destroy( key ); + return status; + } + } + else if (!wcscmp( type, BCRYPT_ECCPRIVATE_BLOB )) + { + BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)input; + DWORD key_size, magic; + + if (input_len < sizeof(*ecc_blob)) return STATUS_INVALID_PARAMETER; + + switch (alg->id) + { + case ALG_ID_ECDH_P256: + key_size = 32; + magic = BCRYPT_ECDH_PRIVATE_P256_MAGIC; + break; + + case ALG_ID_ECDH_P384: + key_size = 48; + magic = BCRYPT_ECDH_PRIVATE_P384_MAGIC; + break; + + case ALG_ID_ECDSA_P256: + key_size = 32; + magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC; + break; + + default: + FIXME( "algorithm %u does not yet support importing blob of type %s\n", alg->id, debugstr_w(type) ); + return STATUS_NOT_SUPPORTED; + } + + if (ecc_blob->dwMagic != magic) return STATUS_INVALID_PARAMETER; + if (ecc_blob->cbKey != key_size || input_len < sizeof(*ecc_blob) + ecc_blob->cbKey * 3) + return STATUS_INVALID_PARAMETER; + + if ((status = key_asymmetric_create( alg->id, key_size * 8, &key ))) return status; + params.key = key; + params.flags = 0; + params.buf = input; + params.len = input_len; + if ((status = key_asymmetric_import( ¶ms ))) + { + key_destroy( key ); + return status; + } + } + else if (!wcscmp( type, BCRYPT_RSAPUBLIC_BLOB )) + { + BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)input; + + if (input_len < sizeof(*rsa_blob)) return STATUS_INVALID_PARAMETER; + if ((alg->id != ALG_ID_RSA && alg->id != ALG_ID_RSA_SIGN) || rsa_blob->Magic != BCRYPT_RSAPUBLIC_MAGIC) + return STATUS_NOT_SUPPORTED; + + size = sizeof(*rsa_blob) + rsa_blob->cbPublicExp + rsa_blob->cbModulus; + if (size != input_len) return NTE_BAD_DATA; + + if ((status = key_asymmetric_create( alg->id, rsa_blob->BitLength, &key ))) return status; + params.key = key; + params.flags = KEY_IMPORT_FLAG_PUBLIC; + params.buf = input; + params.len = input_len; + if ((status = key_asymmetric_import( ¶ms ))) + { + key_destroy( key ); + return status; + } + } + else if (!wcscmp( type, BCRYPT_RSAPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_RSAFULLPRIVATE_BLOB )) + { + BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)input; + + if (input_len < sizeof(*rsa_blob)) return STATUS_INVALID_PARAMETER; + if (alg->id != ALG_ID_RSA || (rsa_blob->Magic != BCRYPT_RSAPRIVATE_MAGIC && + rsa_blob->Magic != BCRYPT_RSAFULLPRIVATE_MAGIC)) return STATUS_NOT_SUPPORTED; + + if ((status = key_asymmetric_create( alg->id, rsa_blob->BitLength, &key ))) return status; + params.key = key; + params.flags = 0; + params.buf = input; + params.len = input_len; + if ((status = key_asymmetric_import( ¶ms ))) + { + key_destroy( key ); + return status; + } + } + else if (!wcscmp( type, LEGACY_RSAPRIVATE_BLOB )) + { + BCRYPT_RSAKEY_BLOB *rsa_blob; + + status = convert_legacy_rsaprivate_blob( alg, &rsa_blob, &input_len, input, input_len ); + if (status != STATUS_SUCCESS) + return status; + + status = key_import_pair( alg, BCRYPT_RSAPRIVATE_BLOB, ret_key, (UCHAR *)rsa_blob, input_len ); + SecureZeroMemory( rsa_blob, input_len ); + free( rsa_blob ); + return status; + } + else if (!wcscmp( type, BCRYPT_DSA_PUBLIC_BLOB )) + { + BCRYPT_DSA_KEY_BLOB *dsa_blob = (BCRYPT_DSA_KEY_BLOB *)input; + + if (input_len < sizeof(*dsa_blob)) return STATUS_INVALID_PARAMETER; + if (alg->id != ALG_ID_DSA || dsa_blob->dwMagic != BCRYPT_DSA_PUBLIC_MAGIC) + return STATUS_NOT_SUPPORTED; + + if ((status = key_asymmetric_create( alg->id, dsa_blob->cbKey * 8, &key ))) return status; + params.key = key; + params.flags = KEY_IMPORT_FLAG_PUBLIC; + params.buf = input; + params.len = input_len; + if ((status = key_asymmetric_import( ¶ms ))) + { + key_destroy( key ); + return status; + } + } + else if (!wcscmp( type, LEGACY_DSA_V2_PRIVATE_BLOB )) + { + BLOBHEADER *hdr = (BLOBHEADER *)input; + DSSPUBKEY *pubkey; + + if (input_len < sizeof(*hdr)) return STATUS_INVALID_PARAMETER; + + if (hdr->bType != PRIVATEKEYBLOB && hdr->bVersion != 2 && hdr->aiKeyAlg != CALG_DSS_SIGN) + { + FIXME( "blob type %u version %u alg id %u not supported\n", hdr->bType, hdr->bVersion, hdr->aiKeyAlg ); + return STATUS_NOT_SUPPORTED; + } + if (alg->id != ALG_ID_DSA) + { + FIXME( "algorithm %u does not support importing blob of type %s\n", alg->id, debugstr_w(type) ); + return STATUS_NOT_SUPPORTED; + } + + if (input_len < sizeof(*hdr) + sizeof(*pubkey)) return STATUS_INVALID_PARAMETER; + pubkey = (DSSPUBKEY *)(hdr + 1); + if (pubkey->magic != MAGIC_DSS2) return STATUS_NOT_SUPPORTED; + + if (input_len < sizeof(*hdr) + sizeof(*pubkey) + (pubkey->bitlen / 8) * 2 + 40 + sizeof(DSSSEED)) + return STATUS_INVALID_PARAMETER; + + if ((status = key_asymmetric_create( alg->id, pubkey->bitlen, &key ))) return status; + key->u.a.flags |= KEY_FLAG_LEGACY_DSA_V2; + params.key = key; + params.flags = 0; + params.buf = input; + params.len = input_len; + if ((status = key_asymmetric_import( ¶ms ))) + { + key_destroy( key ); + return status; + } + } + else if (!wcscmp( type, LEGACY_DSA_V2_PUBLIC_BLOB )) /* not supported on native */ + { + BLOBHEADER *hdr = (BLOBHEADER *)input; + DSSPUBKEY *pubkey; + + if (alg->id != ALG_ID_DSA) return STATUS_NOT_SUPPORTED; + if (input_len < sizeof(*hdr)) return STATUS_INVALID_PARAMETER; + + if (hdr->bType != PUBLICKEYBLOB && hdr->bVersion != 2 && hdr->aiKeyAlg != CALG_DSS_SIGN) + { + FIXME( "blob type %u version %u alg id %u not supported\n", hdr->bType, hdr->bVersion, hdr->aiKeyAlg ); + return STATUS_NOT_SUPPORTED; + } + + if (input_len < sizeof(*hdr) + sizeof(*pubkey)) return STATUS_INVALID_PARAMETER; + pubkey = (DSSPUBKEY *)(hdr + 1); + if (pubkey->magic != MAGIC_DSS1) return STATUS_NOT_SUPPORTED; + + size = sizeof(*hdr) + sizeof(*pubkey) + (pubkey->bitlen / 8) * 3 + 20 + sizeof(DSSSEED); + if (input_len < size) return STATUS_INVALID_PARAMETER; + + if ((status = key_asymmetric_create( alg->id, pubkey->bitlen, &key ))) return status; + key->u.a.flags |= KEY_FLAG_LEGACY_DSA_V2; + params.key = key; + params.flags = KEY_IMPORT_FLAG_PUBLIC; + params.buf = input; + params.len = input_len; + if ((status = key_asymmetric_import( ¶ms ))) + { + key_destroy( key ); + return status; + } + } + else if (!wcscmp( type, BCRYPT_DH_PRIVATE_BLOB )) + { + BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)input; + + if (input_len != sizeof(*dh_blob) + dh_blob->cbKey * 4) return STATUS_INVALID_PARAMETER; + if (alg->id != ALG_ID_DH || dh_blob->dwMagic != BCRYPT_DH_PRIVATE_MAGIC) + return STATUS_NOT_SUPPORTED; + + if ((status = key_asymmetric_create( alg->id, dh_blob->cbKey * 8, &key ))) return status; + params.key = key; + params.flags = 0; + params.buf = input; + params.len = input_len; + if ((status = key_asymmetric_import( ¶ms ))) + { + key_destroy( key ); + return status; + } + } + else if (!wcscmp( type, BCRYPT_DH_PUBLIC_BLOB )) + { + BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)input; + + if (input_len != sizeof(*dh_blob) + dh_blob->cbKey * 3) return STATUS_INVALID_PARAMETER; + if (alg->id != ALG_ID_DH || dh_blob->dwMagic != BCRYPT_DH_PUBLIC_MAGIC) + return STATUS_NOT_SUPPORTED; + + if ((status = key_asymmetric_create( alg->id, dh_blob->cbKey * 8, &key ))) return status; + params.key = key; + params.flags = KEY_IMPORT_FLAG_PUBLIC; + params.buf = input; + params.len = input_len; + if ((status = key_asymmetric_import( ¶ms ))) + { + key_destroy( key ); + return status; + } } else { - return hash_finish( hash, output, size ); + FIXME( "unsupported key type %s\n", debugstr_w(type) ); + return STATUS_NOT_SUPPORTED; + } + + if (!status) + { + key->u.a.flags |= KEY_FLAG_FINALIZED; + *ret_key = key; + } + return status; +} + +NTSTATUS WINAPI BCryptGenerateSymmetricKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE *ret_handle, + UCHAR *object, ULONG object_len, UCHAR *secret, ULONG secret_len, + ULONG flags ) +{ + struct algorithm *alg = get_alg_object( handle ); + NTSTATUS status; + + TRACE( "%p, %p, %p, %lu, %p, %lu, %#lx\n", handle, ret_handle, object, object_len, secret, secret_len, flags ); + if (object) FIXME( "ignoring object buffer\n" ); + if (!alg) return STATUS_INVALID_HANDLE; + if ((status = key_symmetric_generate( alg, ret_handle, secret, secret_len ))) return status; + TRACE( "returning handle %p\n", *ret_handle ); + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI BCryptGenerateKeyPair( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE *ret_handle, ULONG key_len, + ULONG flags ) +{ + struct algorithm *alg = get_alg_object( handle ); + struct key *key; + NTSTATUS status; + + TRACE( "%p, %p, %lu, %#lx\n", handle, ret_handle, key_len, flags ); + + if (!alg) return STATUS_INVALID_HANDLE; + if (!ret_handle) return STATUS_INVALID_PARAMETER; + + if ((status = key_asymmetric_create( alg->id, key_len, &key ))) return status; + *ret_handle = key; + TRACE( "returning handle %p\n", *ret_handle ); + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI BCryptFinalizeKeyPair( BCRYPT_KEY_HANDLE handle, ULONG flags ) +{ + struct key *key = get_key_object( handle ); + NTSTATUS ret; + + TRACE( "%p, %#lx\n", key, flags ); + + if (!key || key->u.a.flags & KEY_FLAG_FINALIZED) return STATUS_INVALID_HANDLE; + + if (!(ret = key_asymmetric_generate( key ))) key->u.a.flags |= KEY_FLAG_FINALIZED; + return ret; +} + +NTSTATUS WINAPI BCryptImportKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE decrypt_key_handle, const WCHAR *type, + BCRYPT_KEY_HANDLE *ret_handle, UCHAR *object, ULONG object_len, UCHAR *input, + ULONG input_len, ULONG flags ) +{ + struct algorithm *alg = get_alg_object( handle ); + struct key *decrypt_key = NULL; + NTSTATUS status; + + TRACE( "%p, %p, %s, %p, %p, %lu, %p, %lu, %#lx\n", handle, decrypt_key_handle, debugstr_w(type), ret_handle, + object, object_len, input, input_len, flags ); + + if (!alg) return STATUS_INVALID_HANDLE; + if (!ret_handle || !type || !input) return STATUS_INVALID_PARAMETER; + if (decrypt_key_handle && !(decrypt_key = get_key_object( decrypt_key_handle ))) return STATUS_INVALID_HANDLE; + + if ((status = key_import( alg, decrypt_key, type, ret_handle, object, object_len, input, input_len ))) return status; + TRACE( "returning handle %p\n", *ret_handle ); + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI BCryptExportKey( BCRYPT_KEY_HANDLE export_key_handle, BCRYPT_KEY_HANDLE encrypt_key_handle, + const WCHAR *type, UCHAR *output, ULONG output_len, ULONG *size, ULONG flags ) +{ + struct key *key = get_key_object( export_key_handle ); + struct key *encrypt_key = NULL; + + TRACE( "%p, %p, %s, %p, %lu, %p, %#lx\n", export_key_handle, encrypt_key_handle, debugstr_w(type), output, + output_len, size, flags ); + + if (!key) return STATUS_INVALID_HANDLE; + if (!type || !size) return STATUS_INVALID_PARAMETER; + if (encrypt_key_handle && !(encrypt_key = get_key_object( encrypt_key_handle ))) return STATUS_INVALID_HANDLE; + + return key_export( key, encrypt_key, type, output, output_len, size ); +} + +static NTSTATUS key_duplicate( struct key *key_orig, struct key **ret_key ) +{ + struct key_asymmetric_duplicate_params params; + struct key *key_copy; + NTSTATUS status; + UCHAR *buffer; + + if (!(key_copy = calloc( 1, sizeof(*key_copy) ))) return STATUS_NO_MEMORY; + key_copy->hdr = key_orig->hdr; + key_copy->alg_id = key_orig->alg_id; + + if (is_symmetric_key( key_orig )) + { + if (!(buffer = malloc( key_orig->u.s.secret_len ))) + { + free( key_copy ); + return STATUS_NO_MEMORY; + } + memcpy( buffer, key_orig->u.s.secret, key_orig->u.s.secret_len ); + + key_copy->u.s.mode = key_orig->u.s.mode; + key_copy->u.s.block_size = key_orig->u.s.block_size; + key_copy->u.s.secret = buffer; + key_copy->u.s.secret_len = key_orig->u.s.secret_len; + InitializeCriticalSection( &key_copy->u.s.cs ); + *ret_key = key_copy; + return STATUS_SUCCESS; + } + + key_copy->u.a.bitlen = key_orig->u.a.bitlen; + key_copy->u.a.flags = key_orig->u.a.flags; + key_copy->u.a.dss_seed = key_orig->u.a.dss_seed; + + params.key_orig = key_orig; + params.key_copy = key_copy; + if (!(status = key_asymmetric_duplicate ( ¶ms ))) *ret_key = key_copy; + else free( key_copy ); + return status; +} + +NTSTATUS WINAPI BCryptDuplicateKey( BCRYPT_KEY_HANDLE handle, BCRYPT_KEY_HANDLE *handle_copy, UCHAR *object, + ULONG object_len, ULONG flags ) +{ + struct key *key_orig = get_key_object( handle ); + struct key *key_copy; + NTSTATUS status; + + TRACE( "%p, %p, %p, %lu, %#lx\n", handle, handle_copy, object, object_len, flags ); + if (object) FIXME( "ignoring object buffer\n" ); + + if (!key_orig) return STATUS_INVALID_HANDLE; + if (!handle_copy) return STATUS_INVALID_PARAMETER; + + if ((status = key_duplicate( key_orig, &key_copy ))) return status; + + *handle_copy = key_copy; + TRACE( "returning handle %p\n", *handle_copy ); + return STATUS_SUCCESS; +} + +static const WCHAR *resolve_blob_type( const WCHAR *type, UCHAR *input, ULONG input_len ) +{ + BCRYPT_KEY_BLOB *blob = (BCRYPT_KEY_BLOB *)input; + + if (!type) return NULL; + if (wcscmp( type, BCRYPT_PUBLIC_KEY_BLOB )) return type; + if (input_len < sizeof(*blob)) return NULL; + + switch (blob->Magic) + { + case BCRYPT_ECDH_PUBLIC_P256_MAGIC: + case BCRYPT_ECDH_PUBLIC_P384_MAGIC: + case BCRYPT_ECDSA_PUBLIC_P256_MAGIC: + case BCRYPT_ECDSA_PUBLIC_P384_MAGIC: + return BCRYPT_ECCPUBLIC_BLOB; + + case BCRYPT_RSAPUBLIC_MAGIC: + return BCRYPT_RSAPUBLIC_BLOB; + + case BCRYPT_DSA_PUBLIC_MAGIC: + return BCRYPT_DSA_PUBLIC_BLOB; + + default: + FIXME( "unsupported key magic %#lx\n", blob->Magic ); + return NULL; } } -NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE algorithm, UCHAR *secret, ULONG secretlen, - UCHAR *input, ULONG inputlen, UCHAR *output, ULONG outputlen ) +NTSTATUS WINAPI BCryptImportKeyPair( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE decrypt_key_handle, const WCHAR *type, + BCRYPT_KEY_HANDLE *ret_handle, UCHAR *input, ULONG input_len, ULONG flags ) { + struct algorithm *alg = get_alg_object( handle ); NTSTATUS status; - BCRYPT_HASH_HANDLE handle; - TRACE( "%p, %p, %u, %p, %u, %p, %u\n", algorithm, secret, secretlen, - input, inputlen, output, outputlen ); + TRACE( "%p, %p, %s, %p, %p, %lu, %#lx\n", handle, decrypt_key_handle, debugstr_w(type), ret_handle, input, + input_len, flags ); - status = BCryptCreateHash( algorithm, &handle, NULL, 0, secret, secretlen, 0); - if (status != STATUS_SUCCESS) + if (!alg) return STATUS_INVALID_HANDLE; + if (!ret_handle || !input || !(type = resolve_blob_type( type, input, input_len ))) + return STATUS_INVALID_PARAMETER; + if (decrypt_key_handle) { + FIXME( "decryption of key not yet supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + + if ((status = key_import_pair( alg, type, ret_handle, input, input_len ))) return status; + TRACE( "returning handle %p\n", *ret_handle ); + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI BCryptSignHash( BCRYPT_KEY_HANDLE handle, void *padding, UCHAR *input, ULONG input_len, + UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags ) +{ + struct key_asymmetric_sign_params params; + struct key *key = get_key_object( handle ); + + TRACE( "%p, %p, %p, %lu, %p, %lu, %p, %#lx\n", handle, padding, input, input_len, output, output_len, + ret_len, flags ); + + if (!key) return STATUS_INVALID_HANDLE; + if (!is_signature_key( key )) return STATUS_NOT_SUPPORTED; + + params.key = key; + params.padding = padding; + params.input = input; + params.input_len = input_len; + params.output = output; + params.output_len = output_len; + params.ret_len = ret_len; + params.flags = flags; + return key_asymmetric_sign( ¶ms ); +} + +NTSTATUS WINAPI BCryptVerifySignature( BCRYPT_KEY_HANDLE handle, void *padding, UCHAR *hash, ULONG hash_len, + UCHAR *signature, ULONG signature_len, ULONG flags ) +{ + struct key_asymmetric_verify_params params; + struct key *key = get_key_object( handle ); + + TRACE( "%p, %p, %p, %lu, %p, %lu, %#lx\n", handle, padding, hash, hash_len, signature, signature_len, flags ); + + if (!key) return STATUS_INVALID_HANDLE; + if (!is_signature_key( key )) return STATUS_NOT_SUPPORTED; + if (!hash || !hash_len || !signature || !signature_len) return STATUS_INVALID_PARAMETER; + + params.key = key; + params.padding = padding; + params.hash = hash; + params.hash_len = hash_len; + params.signature = signature; + params.signature_len = signature_len; + params.flags = flags; + return key_asymmetric_verify( ¶ms ); +} + +NTSTATUS WINAPI BCryptDestroyKey( BCRYPT_KEY_HANDLE handle ) +{ + struct key *key = get_key_object( handle ); + + TRACE( "%p\n", handle ); + + if (!key) return STATUS_INVALID_HANDLE; + key_destroy( key ); + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI BCryptEncrypt( BCRYPT_KEY_HANDLE handle, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv, + ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags ) +{ + struct key *key = get_key_object( handle ); + struct key_asymmetric_encrypt_params asymmetric_params; + NTSTATUS ret; + + TRACE( "%p, %p, %lu, %p, %p, %lu, %p, %lu, %p, %#lx\n", handle, input, input_len, padding, iv, iv_len, output, + output_len, ret_len, flags ); + + if (!key) return STATUS_INVALID_HANDLE; + + if (is_symmetric_key( key )) + { + if (flags & ~BCRYPT_BLOCK_PADDING) + { + FIXME( "flags %#lx not implemented\n", flags ); + return STATUS_NOT_IMPLEMENTED; + } + EnterCriticalSection( &key->u.s.cs ); + ret = key_symmetric_encrypt( key, input, input_len, padding, iv, iv_len, output, output_len, ret_len, flags ); + LeaveCriticalSection( &key->u.s.cs ); + } + else + { + if (flags & BCRYPT_PAD_NONE) + { + FIXME( "flags %#lx not implemented\n", flags ); + return STATUS_NOT_IMPLEMENTED; + } + if (!is_asymmetric_encryption_key( key )) return STATUS_NOT_SUPPORTED; + + asymmetric_params.input = input; + asymmetric_params.input_len = input_len; + asymmetric_params.padding = padding; + asymmetric_params.key = key; + asymmetric_params.output = output; + asymmetric_params.output_len = output_len; + asymmetric_params.ret_len = ret_len; + asymmetric_params.flags = flags; + ret = key_asymmetric_encrypt( &asymmetric_params ); + } + + return ret; +} + +NTSTATUS WINAPI BCryptDecrypt( BCRYPT_KEY_HANDLE handle, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv, + ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags ) +{ + struct key *key = get_key_object( handle ); + struct key_asymmetric_decrypt_params params; + NTSTATUS ret; + + TRACE( "%p, %p, %lu, %p, %p, %lu, %p, %lu, %p, %#lx\n", handle, input, input_len, padding, iv, iv_len, output, + output_len, ret_len, flags ); + + if (!key) return STATUS_INVALID_HANDLE; + + if (is_symmetric_key( key )) + { + if (flags & ~BCRYPT_BLOCK_PADDING) + { + FIXME( "flags %#lx not supported\n", flags ); + return STATUS_NOT_IMPLEMENTED; + } + + EnterCriticalSection( &key->u.s.cs ); + ret = key_symmetric_decrypt( key, input, input_len, padding, iv, iv_len, output, output_len, ret_len, flags ); + LeaveCriticalSection( &key->u.s.cs ); + } + else + { + if (flags & BCRYPT_PAD_NONE) + { + FIXME( "flags %#lx not implemented\n", flags ); + return STATUS_NOT_IMPLEMENTED; + } + if (!is_asymmetric_encryption_key( key )) return STATUS_NOT_SUPPORTED; + + params.key = key; + params.input = input; + params.input_len = input_len; + params.padding = padding; + params.output = output; + params.output_len = output_len; + params.ret_len = ret_len; + params.flags = flags; + ret = key_asymmetric_decrypt( ¶ms ); + } + + return ret; +} + +NTSTATUS WINAPI BCryptSetProperty( BCRYPT_HANDLE handle, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags ) +{ + struct object *object = get_object( handle, 0 ); + + TRACE( "%p, %s, %p, %lu, %#lx\n", handle, debugstr_w(prop), value, size, flags ); + + if (!handle) return STATUS_INVALID_HANDLE; + if (is_alg_pseudo_handle( handle )) return STATUS_ACCESS_DENIED; + + switch (object->magic) + { + case MAGIC_ALG: + { + struct algorithm *alg = (struct algorithm *)object; + return set_alg_property( alg, prop, value, size, flags ); + } + case MAGIC_KEY: + { + struct key *key = (struct key *)object; + return set_key_property( key, prop, value, size, flags ); + } + default: + WARN( "unknown magic %#lx\n", object->magic ); + return STATUS_INVALID_HANDLE; + } +} + +#define HMAC_PAD_LEN 64 +NTSTATUS WINAPI BCryptDeriveKeyCapi( BCRYPT_HASH_HANDLE handle, BCRYPT_ALG_HANDLE halg, UCHAR *key, ULONG keylen, ULONG flags ) +{ + struct hash *hash = get_hash_object( handle ); + UCHAR buf[MAX_HASH_OUTPUT_BYTES * 2]; + ULONG len, hash_size; + + TRACE( "%p, %p, %p, %lu, %#lx\n", handle, halg, key, keylen, flags ); + + if (!hash) return STATUS_INVALID_HANDLE; + if (!key || !keylen) return STATUS_INVALID_PARAMETER; + hash_get_size( hash, &hash_size ); + if (keylen > hash_size * 2) return STATUS_INVALID_PARAMETER; + if (halg) + { + FIXME( "algorithm handle not supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + + hash_finalize( hash, buf, sizeof(buf) ); + len = hash_size; + if (len < keylen) + { + UCHAR pad1[HMAC_PAD_LEN], pad2[HMAC_PAD_LEN]; + ULONG i; + + for (i = 0; i < sizeof(pad1); i++) + { + pad1[i] = 0x36 ^ (i < len ? buf[i] : 0); + pad2[i] = 0x5c ^ (i < len ? buf[i] : 0); + } + + hash_prepare( hash ); + hash_update( hash, pad1, sizeof(pad1) ); + hash_finish( hash, buf ); + + hash_prepare( hash ); + hash_update( hash, pad2, sizeof(pad2) ); + hash_finish( hash, buf + len ); + } + memcpy( key, buf, keylen ); + return STATUS_SUCCESS; +} + +static NTSTATUS pbkdf2( struct hash *hash, UCHAR *pwd, ULONG pwd_len, UCHAR *salt, ULONG salt_len, + ULONGLONG iterations, ULONG i, UCHAR *dst, ULONG hash_len ) +{ + UCHAR bytes[4], *buf; + ULONG j, k; + + if (!iterations) return STATUS_INVALID_PARAMETER; + if (!(buf = malloc( hash_len ))) return STATUS_NO_MEMORY; + + for (j = 0; j < iterations; j++) + { + if (j == 0) + { + /* use salt || INT(i) */ + if (salt_len && hash_update( hash, salt, salt_len )) + { + free( buf ); + return STATUS_INVALID_PARAMETER; + } + bytes[0] = (i >> 24) & 0xff; + bytes[1] = (i >> 16) & 0xff; + bytes[2] = (i >> 8) & 0xff; + bytes[3] = i & 0xff; + hash_update( hash, bytes, 4 ); + } + else hash_update( hash, buf, hash_len ); /* use U_j */ + + hash_finalize( hash, buf, hash_len ); + if (j == 0) memcpy( dst, buf, hash_len ); + else for (k = 0; k < hash_len; k++) dst[k] ^= buf[k]; + } + + free( buf ); + return STATUS_SUCCESS; +} + +static NTSTATUS derive_key_pbkdf2( struct algorithm *alg, UCHAR *pwd, ULONG pwd_len, UCHAR *salt, ULONG salt_len, + ULONGLONG iterations, UCHAR *dk, ULONG dk_len ) +{ + ULONG hash_len, block_count, bytes_left, i; + struct hash *hash; + UCHAR *partial; + NTSTATUS status; + + hash_len = builtin_algorithms[alg->id].hash_length; + if (dk_len <= 0 || dk_len > ((((ULONGLONG)1) << 32) - 1) * hash_len) return STATUS_INVALID_PARAMETER; + + block_count = 1 + ((dk_len - 1) / hash_len); /* ceil(dk_len / hash_len) */ + bytes_left = dk_len - (block_count - 1) * hash_len; + + if ((status = hash_create( alg, pwd, pwd_len, BCRYPT_HASH_REUSABLE_FLAG, &hash ))) return status; + + /* full blocks */ + for (i = 1; i < block_count; i++) + { + if ((status = pbkdf2( hash, pwd, pwd_len, salt, salt_len, iterations, i, dk + ((i - 1) * hash_len), hash_len ))) + { + hash_destroy( hash ); + return status; + } + } + + /* final partial block */ + if (!(partial = malloc( hash_len ))) + { + hash_destroy( hash ); + return STATUS_NO_MEMORY; + } + + if ((status = pbkdf2( hash, pwd, pwd_len, salt, salt_len, iterations, block_count, partial, hash_len ))) + { + hash_destroy( hash ); + free( partial ); + return status; + } + memcpy( dk + ((block_count - 1) * hash_len), partial, bytes_left ); + + hash_destroy( hash ); + free( partial ); + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI BCryptDeriveKeyPBKDF2( BCRYPT_ALG_HANDLE handle, UCHAR *pwd, ULONG pwd_len, UCHAR *salt, ULONG salt_len, + ULONGLONG iterations, UCHAR *dk, ULONG dk_len, ULONG flags ) +{ + struct algorithm *alg = get_alg_object( handle ); + + TRACE( "%p, %p, %lu, %p, %lu, %s, %p, %lu, %#lx\n", handle, pwd, pwd_len, salt, salt_len, + wine_dbgstr_longlong(iterations), dk, dk_len, flags ); + + if (!alg) return STATUS_INVALID_HANDLE; + return derive_key_pbkdf2( alg, pwd, pwd_len, salt, salt_len, iterations, dk, dk_len ); +} + +NTSTATUS WINAPI BCryptSecretAgreement( BCRYPT_KEY_HANDLE privkey_handle, BCRYPT_KEY_HANDLE pubkey_handle, + BCRYPT_SECRET_HANDLE *ret_handle, ULONG flags ) +{ + struct key *privkey = get_key_object( privkey_handle ); + struct key *pubkey = get_key_object( pubkey_handle ); + struct secret *secret; + NTSTATUS status; + + TRACE( "%p, %p, %p, %#lx\n", privkey_handle, pubkey_handle, ret_handle, flags ); + + if (!privkey || !pubkey) return STATUS_INVALID_HANDLE; + if (!is_agreement_key( privkey ) || !is_agreement_key( pubkey )) return STATUS_NOT_SUPPORTED; + if (!ret_handle) return STATUS_INVALID_PARAMETER; + + if (!(secret = calloc( 1, sizeof(*secret) ))) return STATUS_NO_MEMORY; + secret->hdr.magic = MAGIC_SECRET; + if ((status = key_duplicate( privkey, &secret->privkey ))) + { + free( secret ); + return status; + } + if ((status = key_duplicate( pubkey, &secret->pubkey ))) + { + key_destroy( secret->privkey ); + free( secret ); return status; } - status = BCryptHashData( handle, input, inputlen, 0 ); - if (status != STATUS_SUCCESS) + *ret_handle = secret; + TRACE( "returning handle %p\n", *ret_handle ); + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI BCryptDestroySecret( BCRYPT_SECRET_HANDLE handle ) +{ + struct secret *secret = get_secret_object( handle ); + + TRACE( "%p\n", handle ); + + if (!secret) return STATUS_INVALID_HANDLE; + key_destroy( secret->privkey ); + key_destroy( secret->pubkey ); + destroy_object( &secret->hdr ); + return STATUS_SUCCESS; +} + +static void reverse_bytes( UCHAR *buf, ULONG len ) +{ + ULONG i; + for (i = 0; i < len / 2; i++) { - BCryptDestroyHash( handle ); + UCHAR tmp = buf[i]; + buf[i] = buf[len - i - 1]; + buf[len - i - 1] = tmp; + } +} + +static NTSTATUS derive_key_raw( struct secret *secret, UCHAR *output, ULONG output_len, ULONG *ret_len ) +{ + struct key_asymmetric_derive_key_params params; + NTSTATUS status; + + params.privkey = secret->privkey; + params.pubkey = secret->pubkey; + params.output = output; + params.output_len = output_len; + params.ret_len = ret_len; + if (!(status = key_asymmetric_derive_key( ¶ms )) && output) reverse_bytes( output, *ret_len ); + return status; +} + +static struct algorithm *get_hash_alg( BCryptBuffer *buf, BOOL hmac ) +{ + const WCHAR *str = buf->pvBuffer; + BCRYPT_ALG_HANDLE handle = NULL; + + if (!wcscmp( str, BCRYPT_SHA1_ALGORITHM )) + handle = hmac ? BCRYPT_HMAC_SHA1_ALG_HANDLE : BCRYPT_SHA1_ALG_HANDLE; + else if (!wcscmp( str, BCRYPT_SHA256_ALGORITHM )) + handle = hmac ? BCRYPT_HMAC_SHA256_ALG_HANDLE : BCRYPT_SHA256_ALG_HANDLE; + else if (!wcscmp( str, BCRYPT_SHA384_ALGORITHM )) + handle = hmac ? BCRYPT_HMAC_SHA384_ALG_HANDLE : BCRYPT_SHA384_ALG_HANDLE; + else if (!wcscmp( str, BCRYPT_SHA512_ALGORITHM )) + handle = hmac ? BCRYPT_HMAC_SHA512_ALG_HANDLE : BCRYPT_SHA512_ALG_HANDLE; + + if (handle) return get_alg_object( handle ); + FIXME( "hash algorithm %s not supported\n", debugstr_w(str) ); + return NULL; +} + +static NTSTATUS derive_key_hash( struct secret *secret, BCryptBufferDesc *desc, UCHAR *output, ULONG output_len, + ULONG *ret_len ) +{ + struct key_asymmetric_derive_key_params params; + ULONG hash_len, derived_key_len = secret->privkey->u.a.bitlen / 8; + UCHAR hash_buf[MAX_HASH_OUTPUT_BYTES]; + struct algorithm *alg = NULL; + UCHAR *derived_key; + NTSTATUS status; + ULONG i; + + for (i = 0; i < (desc ? desc->cBuffers : 0); i++) + { + if (desc->pBuffers[i].BufferType == KDF_HASH_ALGORITHM) + { + alg = get_hash_alg( desc->pBuffers + i, FALSE ); + if (!alg) return STATUS_NOT_SUPPORTED; + } + else FIXME( "buffer type %lu not supported\n", desc->pBuffers[i].BufferType ); + } + if (!alg) alg = get_alg_object( BCRYPT_SHA1_ALG_HANDLE ); + + if (!(derived_key = malloc( derived_key_len ))) return STATUS_NO_MEMORY; + + params.privkey = secret->privkey; + params.pubkey = secret->pubkey; + params.output = derived_key; + params.output_len = derived_key_len; + params.ret_len = ret_len; + if ((status = key_asymmetric_derive_key( ¶ms ))) + { + free( derived_key ); return status; } - status = BCryptFinishHash( handle, output, outputlen, 0 ); - if (status != STATUS_SUCCESS) + hash_len = builtin_algorithms[alg->id].hash_length; +#if !defined(__REACTOS__) + assert( hash_len <= sizeof(hash_buf) ); +#endif + if (!(status = hash_single( alg, NULL, 0, derived_key, *params.ret_len, hash_buf, hash_len ))) { - BCryptDestroyHash( handle ); - return status; + if (!output) *ret_len = hash_len; + else + { + *ret_len = min( hash_len, output_len ); + memcpy( output, hash_buf, *ret_len ); + } } - return BCryptDestroyHash( handle ); + free( derived_key ); + return status; +} + +NTSTATUS WINAPI BCryptDeriveKey( BCRYPT_SECRET_HANDLE handle, const WCHAR *kdf, BCryptBufferDesc *desc, + UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags ) +{ + struct secret *secret = get_secret_object( handle ); + + TRACE( "%p, %s, %p, %p, %lu, %p, %#lx\n", secret, debugstr_w(kdf), desc, output, output_len, + ret_len, flags ); + + if (!secret) return STATUS_INVALID_HANDLE; + if (!kdf || !ret_len) return STATUS_INVALID_PARAMETER; + + if (!wcscmp(kdf, BCRYPT_KDF_RAW_SECRET)) + { + return derive_key_raw( secret, output, output_len, ret_len ); + } + else if (!wcscmp(kdf, BCRYPT_KDF_HASH)) + { + return derive_key_hash( secret, desc, output, output_len, ret_len ); + } + + FIXME( "kdf %s not supportedi\n", debugstr_w(kdf) ); + return STATUS_NOT_SUPPORTED; +} + +NTSTATUS WINAPI BCryptKeyDerivation( BCRYPT_KEY_HANDLE handle, BCryptBufferDesc *desc, + UCHAR *output, ULONG output_size, ULONG *ret_len, ULONG flags ) +{ + struct key *key = get_key_object( handle ); + struct algorithm *alg = NULL; + ULONGLONG iter_count = 10000; + ULONG salt_size = 0; + UCHAR *salt = NULL; + NTSTATUS status; + ULONG i; + + TRACE( "%p, %p, %p, %lu, %p, %#lx\n", key, desc, output, output_size, ret_len, flags ); + + if (!key || !desc || !ret_len) return STATUS_INVALID_PARAMETER; + if (key->alg_id != ALG_ID_PBKDF2) + { + FIXME( "unsupported key %d\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + for (i = 0; i < desc->cBuffers; i++) + { + switch (desc->pBuffers[i].BufferType) + { + case KDF_HASH_ALGORITHM: + alg = get_hash_alg( desc->pBuffers + i, TRUE ); + break; + case KDF_SALT: + salt = desc->pBuffers[i].pvBuffer; + salt_size = desc->pBuffers[i].cbBuffer; + break; + case KDF_ITERATION_COUNT: + if (desc->pBuffers[i].cbBuffer != sizeof(ULONGLONG)) return STATUS_INVALID_PARAMETER; + iter_count = *(ULONGLONG *)desc->pBuffers[i].pvBuffer; + break; + default: + FIXME( "buffer type %lu not supported\n", desc->pBuffers[i].BufferType ); + break; + } + } + + status = derive_key_pbkdf2( alg, key->u.s.secret, key->u.s.secret_len, + salt, salt_size, iter_count, output, output_size ); + if (!status) *ret_len = output_size; + return status; } BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) @@ -1132,20 +2748,11 @@ BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) case DLL_PROCESS_ATTACH: instance = hinst; DisableThreadLibraryCalls( hinst ); -#if defined(HAVE_GNUTLS_HASH) && !defined(HAVE_COMMONCRYPTO_COMMONDIGEST_H) - gnutls_initialize(); -#elif defined(SONAME_LIBMBEDTLS) && !defined(HAVE_COMMONCRYPTO_COMMONDIGEST_H) && !defined(__REACTOS__) - mbedtls_initialize(); -#endif + process_attach( NULL ); break; - case DLL_PROCESS_DETACH: if (reserved) break; -#if defined(HAVE_GNUTLS_HASH) && !defined(HAVE_COMMONCRYPTO_COMMONDIGEST_H) - gnutls_uninitialize(); -#elif defined(SONAME_LIBMBEDTLS) && !defined(HAVE_COMMONCRYPTO_COMMONDIGEST_H) && !defined(__REACTOS__) - mbedtls_uninitialize(); -#endif + process_detach( NULL ); break; } return TRUE; diff --git a/dll/win32/bcrypt/commoncrypto.c b/dll/win32/bcrypt/commoncrypto.c new file mode 100644 index 00000000000..a3ae97e925e --- /dev/null +++ b/dll/win32/bcrypt/commoncrypto.c @@ -0,0 +1,235 @@ +#include "precomp.h" + +#ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H +NTSTATUS process_attach( void *args ) +{ + return STATUS_SUCCESS; +} + +NTSTATUS process_detach( void *args ) +{ + return STATUS_SUCCESS; +} + +NTSTATUS hash_init( struct hash *hash ) +{ + switch (hash->alg_id) + { + case ALG_ID_MD5: + CC_MD5_Init( &hash->u.md5_ctx ); + break; + + case ALG_ID_SHA1: + CC_SHA1_Init( &hash->u.sha1_ctx ); + break; + + case ALG_ID_SHA256: + CC_SHA256_Init( &hash->u.sha256_ctx ); + break; + + case ALG_ID_SHA384: + CC_SHA384_Init( &hash->u.sha512_ctx ); + break; + + case ALG_ID_SHA512: + CC_SHA512_Init( &hash->u.sha512_ctx ); + break; + + default: + ERR( "unhandled id %u\n", hash->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + return STATUS_SUCCESS; +} + +NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) +{ + CCHmacAlgorithm cc_algorithm; + switch (hash->alg_id) + { + case ALG_ID_MD5: + cc_algorithm = kCCHmacAlgMD5; + break; + + case ALG_ID_SHA1: + cc_algorithm = kCCHmacAlgSHA1; + break; + + case ALG_ID_SHA256: + cc_algorithm = kCCHmacAlgSHA256; + break; + + case ALG_ID_SHA384: + cc_algorithm = kCCHmacAlgSHA384; + break; + + case ALG_ID_SHA512: + cc_algorithm = kCCHmacAlgSHA512; + break; + + default: + ERR( "unhandled id %u\n", hash->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + CCHmacInit( &hash->u.hmac_ctx, cc_algorithm, key, key_size ); + return STATUS_SUCCESS; +} + + +NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) +{ + switch (hash->alg_id) + { + case ALG_ID_MD5: + CC_MD5_Update( &hash->u.md5_ctx, input, size ); + break; + + case ALG_ID_SHA1: + CC_SHA1_Update( &hash->u.sha1_ctx, input, size ); + break; + + case ALG_ID_SHA256: + CC_SHA256_Update( &hash->u.sha256_ctx, input, size ); + break; + + case ALG_ID_SHA384: + CC_SHA384_Update( &hash->u.sha512_ctx, input, size ); + break; + + case ALG_ID_SHA512: + CC_SHA512_Update( &hash->u.sha512_ctx, input, size ); + break; + + default: + ERR( "unhandled id %u\n", hash->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + return STATUS_SUCCESS; +} + +NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) +{ + CCHmacUpdate( &hash->u.hmac_ctx, input, size ); + return STATUS_SUCCESS; +} + +NTSTATUS hash_finish( struct hash *hash, UCHAR *output ) +{ + switch (hash->alg_id) + { + case ALG_ID_MD5: + CC_MD5_Final( output, &hash->u.md5_ctx ); + break; + + case ALG_ID_SHA1: + CC_SHA1_Final( output, &hash->u.sha1_ctx ); + break; + + case ALG_ID_SHA256: + CC_SHA256_Final( output, &hash->u.sha256_ctx ); + break; + + case ALG_ID_SHA384: + CC_SHA384_Final( output, &hash->u.sha512_ctx ); + break; + + case ALG_ID_SHA512: + CC_SHA512_Final( output, &hash->u.sha512_ctx ); + break; + + default: + ERR( "unhandled id %u\n", hash->alg_id ); + break; + } + return STATUS_SUCCESS; +} + +NTSTATUS hmac_finish( struct hash *hash, UCHAR *output ) +{ + CCHmacFinal( &hash->u.hmac_ctx, output ); + + return STATUS_SUCCESS; +} + +NTSTATUS key_symmetric_set_auth_data( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_symmetric_vector_reset( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_symmetric_encrypt_internal( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_symmetric_decrypt_internal( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_symmetric_get_tag( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_symmetric_destroy( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_asymmetric_generate( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_asymmetric_export( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_asymmetric_import( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_asymmetric_verify( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_asymmetric_sign( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_asymmetric_destroy( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_asymmetric_duplicate( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_asymmetric_decrypt( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_asymmetric_encrypt( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_asymmetric_derive_key( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +#endif /* HAVE_COMMONCRYPTO_COMMONDIGEST_H */ diff --git a/dll/win32/bcrypt/gnutls.c b/dll/win32/bcrypt/gnutls.c new file mode 100644 index 00000000000..48860e1d832 --- /dev/null +++ b/dll/win32/bcrypt/gnutls.c @@ -0,0 +1,2901 @@ +/* + * Copyright 2009 Henri Verbeet for CodeWeavers + * Copyright 2018 Hans Leidekker for CodeWeavers + * + * 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 + * + */ + +#include "precomp.h" + +#ifdef HAVE_GNUTLS_HASH + +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + +#if GNUTLS_VERSION_MAJOR < 3 +#define GNUTLS_CIPHER_AES_192_CBC 92 +#define GNUTLS_CIPHER_AES_128_GCM 93 +#define GNUTLS_CIPHER_AES_256_GCM 94 +#define GNUTLS_PK_ECC 4 + +#define GNUTLS_CURVE_TO_BITS(curve) (unsigned int)(((unsigned int)1<<31)|((unsigned int)(curve))) + +typedef enum +{ + GNUTLS_ECC_CURVE_INVALID, + GNUTLS_ECC_CURVE_SECP224R1, + GNUTLS_ECC_CURVE_SECP256R1, + GNUTLS_ECC_CURVE_SECP384R1, + GNUTLS_ECC_CURVE_SECP521R1, +} gnutls_ecc_curve_t; +#endif + +#if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 6) +#define GNUTLS_CIPHER_AES_128_CFB8 29 +#define GNUTLS_CIPHER_AES_192_CFB8 30 +#define GNUTLS_CIPHER_AES_256_CFB8 31 + +#define GNUTLS_PK_RSA_PSS 6 +#define GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS (1 << 7) +typedef struct gnutls_x509_spki_st *gnutls_x509_spki_t; +#endif + +#if GUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 8) +#define GNUTLS_KEYGEN_DH 4 +#endif + +union key_data +{ + gnutls_cipher_hd_t cipher; + struct + { + gnutls_privkey_t privkey; + gnutls_pubkey_t pubkey; + gnutls_dh_params_t dh_params; + } 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; +} + +/* Not present in gnutls version < 3.0 */ +static int (*pgnutls_cipher_tag)(gnutls_cipher_hd_t, void *, size_t); +static int (*pgnutls_cipher_add_auth)(gnutls_cipher_hd_t, const void *, size_t); +static gnutls_sign_algorithm_t (*pgnutls_pk_to_sign)(gnutls_pk_algorithm_t, gnutls_digest_algorithm_t); +static int (*pgnutls_pubkey_import_ecc_raw)(gnutls_pubkey_t, gnutls_ecc_curve_t, + const gnutls_datum_t *, const gnutls_datum_t *); +static int (*pgnutls_pubkey_export_ecc_raw)(gnutls_pubkey_t key, gnutls_ecc_curve_t *curve, + gnutls_datum_t *x, gnutls_datum_t *y); +static int (*pgnutls_privkey_import_ecc_raw)(gnutls_privkey_t, gnutls_ecc_curve_t, const gnutls_datum_t *, + const gnutls_datum_t *, const gnutls_datum_t *); +static int (*pgnutls_pubkey_verify_hash2)(gnutls_pubkey_t, gnutls_sign_algorithm_t, unsigned int, + const gnutls_datum_t *, const gnutls_datum_t *); +static int (*pgnutls_pubkey_encrypt_data)(gnutls_pubkey_t, unsigned int flags, const gnutls_datum_t *, + gnutls_datum_t *); + +/* Not present in gnutls version < 2.11.0 */ +static int (*pgnutls_pubkey_import_rsa_raw)(gnutls_pubkey_t, const gnutls_datum_t *, const gnutls_datum_t *); + +/* Not present in gnutls version < 2.12.0 */ +static int (*pgnutls_pubkey_import_dsa_raw)(gnutls_pubkey_t, const gnutls_datum_t *, const gnutls_datum_t *, + const gnutls_datum_t *, const gnutls_datum_t *); +static int (*pgnutls_pubkey_import_privkey)(gnutls_pubkey_t, gnutls_privkey_t, unsigned int, unsigned int); +static int (*pgnutls_privkey_decrypt_data)(gnutls_privkey_t, unsigned int flags, const gnutls_datum_t *, + gnutls_datum_t *); + +/* Not present in gnutls version < 3.3.0 */ +static int (*pgnutls_pubkey_export_dsa_raw)(gnutls_pubkey_t, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *, + gnutls_datum_t *); +static int (*pgnutls_pubkey_export_rsa_raw)(gnutls_pubkey_t, gnutls_datum_t *, gnutls_datum_t *); +static int (*pgnutls_privkey_export_ecc_raw)(gnutls_privkey_t, gnutls_ecc_curve_t *, + gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *); +static int (*pgnutls_privkey_export_rsa_raw)(gnutls_privkey_t, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *, + gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *, + gnutls_datum_t *); +static int (*pgnutls_privkey_export_dsa_raw)(gnutls_privkey_t, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *, + gnutls_datum_t *, gnutls_datum_t *); +static int (*pgnutls_privkey_import_rsa_raw)(gnutls_privkey_t, const gnutls_datum_t *, const gnutls_datum_t *, + const gnutls_datum_t *, const gnutls_datum_t *, const gnutls_datum_t *, + const gnutls_datum_t *, const gnutls_datum_t *, const gnutls_datum_t *); + +/* Not present in gnutls version < 3.5.0 */ +static int (*pgnutls_privkey_generate2)(gnutls_privkey_t, gnutls_pk_algorithm_t, unsigned int, unsigned int, + const gnutls_keygen_data_st *, unsigned); + +/* Not present in gnutls version < 3.6.0 */ +static int (*pgnutls_decode_rs_value)(const gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *); +static int (*pgnutls_x509_spki_init)(gnutls_x509_spki_t *); +static void (*pgnutls_x509_spki_deinit)(gnutls_x509_spki_t); +static void (*pgnutls_x509_spki_set_rsa_pss_params)(gnutls_x509_spki_t, gnutls_digest_algorithm_t, unsigned int); +static int (*pgnutls_pubkey_set_spki)(gnutls_pubkey_t, const gnutls_x509_spki_t, unsigned int); +static int (*pgnutls_privkey_set_spki)(gnutls_privkey_t, const gnutls_x509_spki_t, unsigned int); + +/* Not present in gnutls version < 3.8.2 */ +static int (*pgnutls_privkey_derive_secret)(gnutls_privkey_t, gnutls_pubkey_t, const gnutls_datum_t *, + gnutls_datum_t *, unsigned int); +static int (*pgnutls_privkey_export_dh_raw)(gnutls_privkey_t, gnutls_dh_params_t, gnutls_datum_t *, gnutls_datum_t *, + unsigned int); +static int (*pgnutls_pubkey_export_dh_raw)(gnutls_pubkey_t, gnutls_dh_params_t, gnutls_datum_t *, unsigned); +static int (*pgnutls_privkey_import_dh_raw)(gnutls_privkey_t, const gnutls_dh_params_t, const gnutls_datum_t *, + const gnutls_datum_t *); +static int (*pgnutls_pubkey_import_dh_raw)(gnutls_pubkey_t, const gnutls_dh_params_t, const gnutls_datum_t *); + +/* Not present in gnutls version < 3.8.4 */ +static int (*pgnutls_x509_spki_set_rsa_oaep_params)(gnutls_x509_spki_t, gnutls_digest_algorithm_t, gnutls_datum_t *); + +static void *libgnutls_handle; +#define MAKE_FUNCPTR(f) static typeof(f) * p##f +MAKE_FUNCPTR(gnutls_cipher_decrypt2); +MAKE_FUNCPTR(gnutls_cipher_deinit); +MAKE_FUNCPTR(gnutls_cipher_encrypt2); +MAKE_FUNCPTR(gnutls_cipher_init); +MAKE_FUNCPTR(gnutls_dh_params_deinit); +MAKE_FUNCPTR(gnutls_dh_params_export_raw); +MAKE_FUNCPTR(gnutls_dh_params_import_raw); +MAKE_FUNCPTR(gnutls_dh_params_init); +MAKE_FUNCPTR(gnutls_global_deinit); +MAKE_FUNCPTR(gnutls_global_init); +MAKE_FUNCPTR(gnutls_global_set_log_function); +MAKE_FUNCPTR(gnutls_global_set_log_level); +MAKE_FUNCPTR(gnutls_hash); +MAKE_FUNCPTR(gnutls_hash_deinit); +MAKE_FUNCPTR(gnutls_hash_init); +MAKE_FUNCPTR(gnutls_hmac); +MAKE_FUNCPTR(gnutls_hmac_deinit); +MAKE_FUNCPTR(gnutls_hmac_init); +MAKE_FUNCPTR(gnutls_perror); +MAKE_FUNCPTR(gnutls_privkey_decrypt_data); +MAKE_FUNCPTR(gnutls_privkey_deinit); +MAKE_FUNCPTR(gnutls_privkey_import_dsa_raw); +MAKE_FUNCPTR(gnutls_privkey_init); +MAKE_FUNCPTR(gnutls_privkey_sign_hash); +MAKE_FUNCPTR(gnutls_pubkey_deinit); +MAKE_FUNCPTR(gnutls_pubkey_encrypt_data); +MAKE_FUNCPTR(gnutls_pubkey_import_privkey); +MAKE_FUNCPTR(gnutls_pubkey_init); +#undef MAKE_FUNCPTR + +static int compat_gnutls_cipher_tag(gnutls_cipher_hd_t handle, void *tag, size_t tag_size) +{ + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; +} + +static int compat_gnutls_cipher_add_auth(gnutls_cipher_hd_t handle, const void *ptext, size_t ptext_size) +{ + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; +} + +static int compat_gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key, gnutls_ecc_curve_t curve, + const gnutls_datum_t *x, const gnutls_datum_t *y) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_pubkey_export_ecc_raw(gnutls_pubkey_t key, gnutls_ecc_curve_t *curve, + gnutls_datum_t *x, gnutls_datum_t *y) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_pubkey_export_dsa_raw(gnutls_pubkey_t key, gnutls_datum_t *p, gnutls_datum_t *q, + gnutls_datum_t *g, gnutls_datum_t *y) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_pubkey_export_rsa_raw(gnutls_pubkey_t key, gnutls_datum_t *m, gnutls_datum_t *e) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_privkey_export_rsa_raw(gnutls_privkey_t key, gnutls_datum_t *m, gnutls_datum_t *e, + gnutls_datum_t *d, gnutls_datum_t *p, gnutls_datum_t *q, + gnutls_datum_t *u, gnutls_datum_t *e1, gnutls_datum_t *e2) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_privkey_export_ecc_raw(gnutls_privkey_t key, gnutls_ecc_curve_t *curve, + gnutls_datum_t *x, gnutls_datum_t *y, gnutls_datum_t *k) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_privkey_import_ecc_raw(gnutls_privkey_t key, gnutls_ecc_curve_t curve, + const gnutls_datum_t *x, const gnutls_datum_t *y, + const gnutls_datum_t *k) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_privkey_export_dsa_raw(gnutls_privkey_t key, gnutls_datum_t *p, gnutls_datum_t *q, + gnutls_datum_t *g, gnutls_datum_t *y, gnutls_datum_t *x) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static gnutls_sign_algorithm_t compat_gnutls_pk_to_sign(gnutls_pk_algorithm_t pk, gnutls_digest_algorithm_t hash) +{ + return GNUTLS_SIGN_UNKNOWN; +} + +static int compat_gnutls_pubkey_verify_hash2(gnutls_pubkey_t key, gnutls_sign_algorithm_t algo, + unsigned int flags, const gnutls_datum_t *hash, + const gnutls_datum_t *signature) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_pubkey_import_rsa_raw(gnutls_pubkey_t key, const gnutls_datum_t *m, const gnutls_datum_t *e) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_pubkey_import_dsa_raw(gnutls_pubkey_t key, const gnutls_datum_t *p, const gnutls_datum_t *q, + const gnutls_datum_t *g, const gnutls_datum_t *y) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_decode_rs_value(const gnutls_datum_t * sig_value, gnutls_datum_t * r, gnutls_datum_t * s) +{ + return GNUTLS_E_INTERNAL_ERROR; +} + +static int compat_gnutls_privkey_import_rsa_raw(gnutls_privkey_t key, const gnutls_datum_t *m, const gnutls_datum_t *e, + const gnutls_datum_t *d, const gnutls_datum_t *p, const gnutls_datum_t *q, + const gnutls_datum_t *u, const gnutls_datum_t *e1, const gnutls_datum_t *e2) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_privkey_decrypt_data(gnutls_privkey_t key, unsigned int flags, const gnutls_datum_t *cipher_text, + gnutls_datum_t *plain_text) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_pubkey_encrypt_data(gnutls_pubkey_t key, unsigned int flags, const gnutls_datum_t *cipher_text, + gnutls_datum_t *plain_text) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_x509_spki_init(gnutls_x509_spki_t *spki) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static void compat_gnutls_x509_spki_deinit(gnutls_x509_spki_t spki) +{ +} + +static int compat_gnutls_x509_spki_set_rsa_oaep_params(gnutls_x509_spki_t spki, gnutls_digest_algorithm_t dig, + gnutls_datum_t *label) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static void compat_gnutls_x509_spki_set_rsa_pss_params(gnutls_x509_spki_t spki, gnutls_digest_algorithm_t dig, + unsigned int salt_size) +{ +} + +static int compat_gnutls_pubkey_set_spki(gnutls_pubkey_t key, const gnutls_x509_spki_t spki, unsigned int flags) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_privkey_set_spki(gnutls_privkey_t key, const gnutls_x509_spki_t spki, unsigned int flags) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_privkey_derive_secret(gnutls_privkey_t privkey, gnutls_pubkey_t pubkey, const gnutls_datum_t *nonce, + gnutls_datum_t *secret, unsigned int flags) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_privkey_export_dh_raw(gnutls_privkey_t privkey, gnutls_dh_params_t params, gnutls_datum_t *y, + gnutls_datum_t *x, unsigned int flags ) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_pubkey_export_dh_raw(gnutls_pubkey_t pubkey, gnutls_dh_params_t params, gnutls_datum_t *y, + unsigned flags) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_privkey_import_dh_raw(gnutls_privkey_t privkey, const gnutls_dh_params_t params, + const gnutls_datum_t *y, const gnutls_datum_t *x) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_pubkey_import_dh_raw(gnutls_pubkey_t pubkey, const gnutls_dh_params_t params, + const gnutls_datum_t *y) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_privkey_generate2(gnutls_privkey_t privkey, gnutls_pk_algorithm_t alg, unsigned int bits, + unsigned int flags, const gnutls_keygen_data_st *data, unsigned data_size) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static void gnutls_log( int level, const char *msg ) +{ + TRACE( "<%d> %s", level, msg ); +} + +NTSTATUS process_attach( void *args ) +{ + const char *env_str; + int ret; + + if ((env_str = getenv("GNUTLS_SYSTEM_PRIORITY_FILE"))) + { + WARN("GNUTLS_SYSTEM_PRIORITY_FILE is %s.\n", debugstr_a(env_str)); + } + else + { + WARN("Setting GNUTLS_SYSTEM_PRIORITY_FILE to \"/dev/null\".\n"); + setenv("GNUTLS_SYSTEM_PRIORITY_FILE", "/dev/null", 0); + } + + if (!(libgnutls_handle = dlopen( SONAME_LIBGNUTLS, RTLD_NOW ))) + { + ERR_(winediag)( "failed to load libgnutls, no support for encryption\n" ); + return STATUS_DLL_NOT_FOUND; + } + +#define LOAD_FUNCPTR(f) \ + if (!(p##f = dlsym( libgnutls_handle, #f ))) \ + { \ + ERR( "failed to load %s\n", #f ); \ + goto fail; \ + } + + LOAD_FUNCPTR(gnutls_cipher_decrypt2) + LOAD_FUNCPTR(gnutls_cipher_deinit) + LOAD_FUNCPTR(gnutls_cipher_encrypt2) + LOAD_FUNCPTR(gnutls_cipher_init) + LOAD_FUNCPTR(gnutls_dh_params_deinit) + LOAD_FUNCPTR(gnutls_dh_params_export_raw) + LOAD_FUNCPTR(gnutls_dh_params_import_raw) + LOAD_FUNCPTR(gnutls_dh_params_init) + LOAD_FUNCPTR(gnutls_global_deinit) + LOAD_FUNCPTR(gnutls_global_init) + LOAD_FUNCPTR(gnutls_global_set_log_function) + LOAD_FUNCPTR(gnutls_global_set_log_level) + LOAD_FUNCPTR(gnutls_hash); + LOAD_FUNCPTR(gnutls_hash_deinit); + LOAD_FUNCPTR(gnutls_hash_init); + LOAD_FUNCPTR(gnutls_hmac); + LOAD_FUNCPTR(gnutls_hmac_deinit); + LOAD_FUNCPTR(gnutls_hmac_init); + LOAD_FUNCPTR(gnutls_perror); + LOAD_FUNCPTR(gnutls_privkey_deinit); + LOAD_FUNCPTR(gnutls_privkey_import_dsa_raw); + LOAD_FUNCPTR(gnutls_privkey_init); + LOAD_FUNCPTR(gnutls_privkey_sign_hash); + LOAD_FUNCPTR(gnutls_pubkey_deinit); + LOAD_FUNCPTR(gnutls_pubkey_import_privkey); + LOAD_FUNCPTR(gnutls_pubkey_init); +#undef LOAD_FUNCPTR + +#define LOAD_FUNCPTR_OPT(f) \ + if (!(p##f = dlsym( libgnutls_handle, #f ))) \ + { \ + WARN( "failed to load %s\n", #f ); \ + p##f = compat_##f; \ + } + + LOAD_FUNCPTR_OPT(gnutls_cipher_tag) + LOAD_FUNCPTR_OPT(gnutls_cipher_add_auth) + LOAD_FUNCPTR_OPT(gnutls_decode_rs_value) + LOAD_FUNCPTR_OPT(gnutls_pk_to_sign) + LOAD_FUNCPTR_OPT(gnutls_privkey_decrypt_data) + LOAD_FUNCPTR_OPT(gnutls_privkey_derive_secret) + LOAD_FUNCPTR_OPT(gnutls_privkey_export_dh_raw) + LOAD_FUNCPTR_OPT(gnutls_privkey_export_dsa_raw) + LOAD_FUNCPTR_OPT(gnutls_privkey_export_ecc_raw) + LOAD_FUNCPTR_OPT(gnutls_privkey_export_rsa_raw) + LOAD_FUNCPTR_OPT(gnutls_privkey_generate2) + LOAD_FUNCPTR_OPT(gnutls_privkey_import_dh_raw) + LOAD_FUNCPTR_OPT(gnutls_privkey_import_ecc_raw) + LOAD_FUNCPTR_OPT(gnutls_privkey_import_rsa_raw) + LOAD_FUNCPTR_OPT(gnutls_privkey_set_spki) + LOAD_FUNCPTR_OPT(gnutls_pubkey_encrypt_data) + LOAD_FUNCPTR_OPT(gnutls_pubkey_export_dh_raw) + LOAD_FUNCPTR_OPT(gnutls_pubkey_export_dsa_raw) + LOAD_FUNCPTR_OPT(gnutls_pubkey_export_ecc_raw) + LOAD_FUNCPTR_OPT(gnutls_pubkey_export_rsa_raw) + LOAD_FUNCPTR_OPT(gnutls_pubkey_import_dh_raw) + LOAD_FUNCPTR_OPT(gnutls_pubkey_import_dsa_raw) + LOAD_FUNCPTR_OPT(gnutls_pubkey_import_ecc_raw) + LOAD_FUNCPTR_OPT(gnutls_pubkey_import_rsa_raw) + LOAD_FUNCPTR_OPT(gnutls_pubkey_set_spki) + LOAD_FUNCPTR_OPT(gnutls_pubkey_verify_hash2) + LOAD_FUNCPTR_OPT(gnutls_x509_spki_deinit) + LOAD_FUNCPTR_OPT(gnutls_x509_spki_init) + LOAD_FUNCPTR_OPT(gnutls_x509_spki_set_rsa_oaep_params) + LOAD_FUNCPTR_OPT(gnutls_x509_spki_set_rsa_pss_params) + +#undef LOAD_FUNCPTR_OPT + + if ((ret = pgnutls_global_init()) != GNUTLS_E_SUCCESS) + { + pgnutls_perror( ret ); + goto fail; + } + + if (TRACE_ON( bcrypt )) + { + char *env = getenv("GNUTLS_DEBUG_LEVEL"); + int level = env ? atoi(env) : 4; + pgnutls_global_set_log_level(level); + pgnutls_global_set_log_function( gnutls_log ); + } + + return STATUS_SUCCESS; + +fail: + dlclose( libgnutls_handle ); + libgnutls_handle = NULL; + return STATUS_DLL_NOT_FOUND; +} + +NTSTATUS process_detach( void *args ) +{ + if (libgnutls_handle) + { + pgnutls_global_deinit(); + dlclose( libgnutls_handle ); + libgnutls_handle = NULL; + } + return STATUS_SUCCESS; +} + +struct buffer +{ + BYTE *buffer; + DWORD length; + DWORD pos; + BOOL error; +}; + +static void buffer_init( struct buffer *buffer ) +{ + buffer->buffer = NULL; + buffer->length = 0; + buffer->pos = 0; + buffer->error = FALSE; +} + +static void buffer_free( struct buffer *buffer ) +{ + free( buffer->buffer ); +} + +static void buffer_append( struct buffer *buffer, BYTE *data, DWORD len ) +{ + if (!len) return; + + if (buffer->pos + len > buffer->length) + { + DWORD new_length = max( max( buffer->pos + len, buffer->length * 2 ), 64 ); + BYTE *new_buffer; + + if (!(new_buffer = realloc( buffer->buffer, new_length ))) + { + ERR( "out of memory\n" ); + buffer->error = TRUE; + return; + } + + buffer->buffer = new_buffer; + buffer->length = new_length; + } + + memcpy( &buffer->buffer[buffer->pos], data, len ); + buffer->pos += len; +} + +static void buffer_append_byte( struct buffer *buffer, BYTE value ) +{ + buffer_append( buffer, &value, sizeof(value) ); +} + +static void buffer_append_asn1_length( struct buffer *buffer, DWORD length ) +{ + DWORD num_bytes; + + if (length < 128) + { + buffer_append_byte( buffer, length ); + return; + } + + if (length <= 0xff) num_bytes = 1; + else if (length <= 0xffff) num_bytes = 2; + else if (length <= 0xffffff) num_bytes = 3; + else num_bytes = 4; + + buffer_append_byte( buffer, 0x80 | num_bytes ); + while (num_bytes--) buffer_append_byte( buffer, length >> (num_bytes * 8) ); +} + +static void buffer_append_asn1_integer( struct buffer *buffer, BYTE *data, DWORD len ) +{ + DWORD leading_zero = (*data & 0x80) != 0; + + buffer_append_byte( buffer, 0x02 ); /* tag */ + buffer_append_asn1_length( buffer, len + leading_zero ); + if (leading_zero) buffer_append_byte( buffer, 0 ); + buffer_append( buffer, data, len ); +} + +static void buffer_append_asn1_sequence( struct buffer *buffer, struct buffer *content ) +{ + if (content->error) + { + buffer->error = TRUE; + return; + } + + buffer_append_byte( buffer, 0x30 ); /* tag */ + buffer_append_asn1_length( buffer, content->pos ); + buffer_append( buffer, content->buffer, content->pos ); +} + +static void buffer_append_asn1_r_s( struct buffer *buffer, BYTE *r, DWORD r_len, BYTE *s, DWORD s_len ) +{ + struct buffer value; + + buffer_init( &value ); + buffer_append_asn1_integer( &value, r, r_len ); + buffer_append_asn1_integer( &value, s, s_len ); + buffer_append_asn1_sequence( buffer, &value ); + buffer_free( &value ); +} + +static gnutls_cipher_algorithm_t get_gnutls_cipher( const struct key *key ) +{ + switch (key->alg_id) + { + case ALG_ID_3DES: + WARN( "handle block size\n" ); + switch (key->u.s.mode) + { + case CHAIN_MODE_CBC: + return GNUTLS_CIPHER_3DES_CBC; + default: + break; + } + FIXME( "3DES mode %u with key length %u not supported\n", key->u.s.mode, key->u.s.secret_len ); + return GNUTLS_CIPHER_UNKNOWN; + + case ALG_ID_AES: + WARN( "handle block size\n" ); + switch (key->u.s.mode) + { + case CHAIN_MODE_GCM: + if (key->u.s.secret_len == 16) return GNUTLS_CIPHER_AES_128_GCM; + if (key->u.s.secret_len == 32) return GNUTLS_CIPHER_AES_256_GCM; + break; + case CHAIN_MODE_ECB: /* can be emulated with CBC + empty IV */ + case CHAIN_MODE_CBC: + if (key->u.s.secret_len == 16) return GNUTLS_CIPHER_AES_128_CBC; + if (key->u.s.secret_len == 24) return GNUTLS_CIPHER_AES_192_CBC; + if (key->u.s.secret_len == 32) return GNUTLS_CIPHER_AES_256_CBC; + break; + case CHAIN_MODE_CFB: + if (key->u.s.secret_len == 16) return GNUTLS_CIPHER_AES_128_CFB8; + if (key->u.s.secret_len == 24) return GNUTLS_CIPHER_AES_192_CFB8; + if (key->u.s.secret_len == 32) return GNUTLS_CIPHER_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 ); + return GNUTLS_CIPHER_UNKNOWN; + + default: + FIXME( "algorithm %u not supported\n", key->alg_id ); + return GNUTLS_CIPHER_UNKNOWN; + } +} + +NTSTATUS key_symmetric_vector_reset( void *args ) +{ + struct key *key = args; + + if (!key_data(key)->cipher) return STATUS_SUCCESS; + TRACE( "invalidating cipher handle\n" ); + pgnutls_cipher_deinit( key_data(key)->cipher ); + key_data(key)->cipher = NULL; + return STATUS_SUCCESS; +} + +static NTSTATUS init_cipher_handle( struct key *key ) +{ + gnutls_cipher_algorithm_t cipher; + gnutls_datum_t secret, vector; + int ret; + + if (key_data(key)->cipher) return STATUS_SUCCESS; + if ((cipher = get_gnutls_cipher( key )) == GNUTLS_CIPHER_UNKNOWN) return STATUS_NOT_SUPPORTED; + + secret.data = key->u.s.secret; + secret.size = key->u.s.secret_len; + + vector.data = key->u.s.vector; + vector.size = key->u.s.vector_len; + + if ((ret = pgnutls_cipher_init( &key_data(key)->cipher, cipher, &secret, key->u.s.vector ? &vector : NULL ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + return STATUS_SUCCESS; +} + +NTSTATUS key_symmetric_set_auth_data( void *args ) +{ + const struct key_symmetric_set_auth_data_params *params = args; + NTSTATUS status; + int ret; + + if (!params->auth_data) return STATUS_SUCCESS; + if ((status = init_cipher_handle( params->key ))) return status; + + if ((ret = pgnutls_cipher_add_auth( key_data(params->key)->cipher, params->auth_data, params->len ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + return STATUS_SUCCESS; +} + +NTSTATUS key_symmetric_encrypt( void *args ) +{ + const struct key_symmetric_encrypt_params *params = args; + NTSTATUS status; + int ret; + + if ((status = init_cipher_handle( params->key ))) return status; + + if ((ret = pgnutls_cipher_encrypt2( key_data(params->key)->cipher, params->input, params->input_len, + params->output, params->output_len ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + return STATUS_SUCCESS; +} + +NTSTATUS key_symmetric_decrypt( void *args ) +{ + const struct key_symmetric_decrypt_params *params = args; + NTSTATUS status; + int ret; + + if ((status = init_cipher_handle( params->key ))) return status; + + if ((ret = pgnutls_cipher_decrypt2( key_data(params->key)->cipher, params->input, params->input_len, + params->output, params->output_len ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + return STATUS_SUCCESS; +} + +NTSTATUS key_symmetric_get_tag( void *args ) +{ + const struct key_symmetric_get_tag_params *params = args; + NTSTATUS status; + int ret; + + if ((status = init_cipher_handle( params->key ))) return status; + + if ((ret = pgnutls_cipher_tag( key_data(params->key)->cipher, params->tag, params->len ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + return STATUS_SUCCESS; +} + +NTSTATUS key_symmetric_destroy( void *args ) +{ + struct key *key = args; + + if (key_data(key)->cipher) pgnutls_cipher_deinit( key_data(key)->cipher ); + return STATUS_SUCCESS; +} + +static ULONG export_gnutls_datum( UCHAR *buffer, ULONG buflen, gnutls_datum_t *d, BOOL zero_pad ) +{ + ULONG size = d->size; + UCHAR *src = d->data; + ULONG offset = 0; + + assert( size <= buflen + 1 ); + if (size == buflen + 1) + { + assert( !src[0] ); + src++; + size--; + } + if (zero_pad) + { + offset = buflen - size; + if (buffer) memset( buffer, 0, offset ); + size = buflen; + } + + if (buffer) memcpy( buffer + offset, src, size - offset ); + return size; +} + +#define EXPORT_SIZE(d,l,p) export_gnutls_datum( NULL, l, &d, p ) +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; + gnutls_datum_t m, e; + ULONG size = key->u.a.bitlen / 8; + UCHAR *dst; + int ret; + + if (key_data(key)->a.pubkey) + ret = pgnutls_pubkey_export_rsa_raw( key_data(key)->a.pubkey, &m, &e ); + else + return STATUS_INVALID_PARAMETER; + + if (ret) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + *ret_len = sizeof(*rsa_blob) + EXPORT_SIZE( e, size, 0 ) + EXPORT_SIZE( m, size, 1 ); + if (len >= *ret_len && buf) + { + dst = (UCHAR *)(rsa_blob + 1); + rsa_blob->cbPublicExp = export_gnutls_datum( dst, size, &e, 0 ); + + dst += rsa_blob->cbPublicExp; + rsa_blob->cbModulus = export_gnutls_datum( dst, size, &m, 1 ); + + rsa_blob->Magic = BCRYPT_RSAPUBLIC_MAGIC; + rsa_blob->BitLength = key->u.a.bitlen; + rsa_blob->cbPrime1 = 0; + rsa_blob->cbPrime2 = 0; + } + + free( e.data ); free( m.data ); + return STATUS_SUCCESS; +} + +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; + gnutls_ecc_curve_t curve; + gnutls_datum_t x, y; + DWORD magic, size; + UCHAR *dst; + int ret; + + switch (key->alg_id) + { + 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_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; + + default: + FIXME( "algorithm %u not supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + if (key_data(key)->a.pubkey) + ret = pgnutls_pubkey_export_ecc_raw( key_data(key)->a.pubkey, &curve, &x, &y ); + else + return STATUS_INVALID_PARAMETER; + + if (ret) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if (curve != GNUTLS_ECC_CURVE_SECP256R1 && curve != GNUTLS_ECC_CURVE_SECP384R1) + { + FIXME( "curve %u not supported\n", curve ); + free( x.data ); free( y.data ); + return STATUS_NOT_IMPLEMENTED; + } + + *ret_len = sizeof(*ecc_blob) + EXPORT_SIZE( x, size, 1 ) + EXPORT_SIZE( y, size, 1 ); + if (len >= *ret_len && buf) + { + ecc_blob->dwMagic = magic; + ecc_blob->cbKey = size; + + dst = (UCHAR *)(ecc_blob + 1); + dst += export_gnutls_datum( dst, size, &x, 1 ); + export_gnutls_datum( dst, size, &y, 1 ); + } + + free( x.data ); free( y.data ); + 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; + gnutls_datum_t p, q, g, y; + ULONG size = key->u.a.bitlen / 8; + NTSTATUS status = STATUS_SUCCESS; + UCHAR *dst; + int ret; + + if (key->u.a.bitlen > 1024) + { + FIXME( "bitlen > 1024 not supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + + if (key_data(key)->a.pubkey) + ret = pgnutls_pubkey_export_dsa_raw( key_data(key)->a.pubkey, &p, &q, &g, &y ); + else + return STATUS_INVALID_PARAMETER; + + if (ret) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if (EXPORT_SIZE( q, sizeof(dsa_blob->q), 1 ) > sizeof(dsa_blob->q)) + { + status = STATUS_INVALID_PARAMETER; + goto done; + } + + *ret_len = sizeof(*dsa_blob) + EXPORT_SIZE( p, size, 1 ) + EXPORT_SIZE( g, size, 1 ) + EXPORT_SIZE( y, size, 1 ); + if (len >= *ret_len && buf) + { + dst = (UCHAR *)(dsa_blob + 1); + dst += export_gnutls_datum( dst, size, &p, 1 ); + dst += export_gnutls_datum( dst, size, &g, 1 ); + export_gnutls_datum( dst, size, &y, 1 ); + + dst = dsa_blob->q; + export_gnutls_datum( dst, sizeof(dsa_blob->q), &q, 1 ); + + dsa_blob->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC; + dsa_blob->cbKey = size; + memset( dsa_blob->Count, 0, sizeof(dsa_blob->Count) ); /* FIXME */ + memset( dsa_blob->Seed, 0, sizeof(dsa_blob->Seed) ); /* FIXME */ + } + +done: + free( p.data ); free( q.data ); free( g.data ); free( y.data ); + return status; +} + +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; + gnutls_datum_t p, q, g, y; + ULONG size = key->u.a.bitlen / 8; + NTSTATUS status = STATUS_SUCCESS; + UCHAR *dst; + int ret; + + if (key->u.a.bitlen > 1024) + { + FIXME( "bitlen > 1024 not supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + + if (key_data(key)->a.pubkey) + ret = pgnutls_pubkey_export_dsa_raw( key_data(key)->a.pubkey, &p, &q, &g, &y ); + else if (key_data(key)->a.privkey) + ret = pgnutls_privkey_export_dsa_raw( key_data(key)->a.privkey, &p, &q, &g, &y, NULL ); + else + return STATUS_INVALID_PARAMETER; + + if (ret) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if (EXPORT_SIZE( q, Q_SIZE, 1 ) > Q_SIZE) + { + status = STATUS_INVALID_PARAMETER; + goto done; + } + + *ret_len = sizeof(*hdr) + sizeof(*dsskey) + sizeof(key->u.a.dss_seed) + + EXPORT_SIZE( p, size, 1 ) + Q_SIZE + EXPORT_SIZE( g, size, 1 ) + EXPORT_SIZE( y, size, 1 ); + if (len >= *ret_len && buf) + { + 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); + export_gnutls_datum( dst, size, &p, 1 ); + reverse_bytes( dst, size ); + dst += size; + + export_gnutls_datum( dst, Q_SIZE, &q, 1 ); + reverse_bytes( dst, Q_SIZE ); + dst += Q_SIZE; + + export_gnutls_datum( dst, size, &g, 1 ); + reverse_bytes( dst, size ); + dst += size; + + export_gnutls_datum( dst, size, &y, 1 ); + reverse_bytes( dst, size ); + dst += size; + + memcpy( dst, &key->u.a.dss_seed, sizeof(key->u.a.dss_seed) ); + } + +done: + free( p.data ); free( q.data ); free( g.data ); free( y.data ); + return status; +} + +static gnutls_privkey_t create_privkey( gnutls_pk_algorithm_t pk_alg, unsigned int bitlen, + const gnutls_keygen_data_st *data, unsigned int data_size ) +{ + gnutls_privkey_t privkey; + int ret; + + if ((ret = pgnutls_privkey_init( &privkey ))) + { + pgnutls_perror( ret ); + return NULL; + } + + if ((ret = pgnutls_privkey_generate2( privkey, pk_alg, bitlen, 0, data, data_size ))) + { + pgnutls_perror( ret ); + pgnutls_privkey_deinit( privkey ); + return NULL; + } + + return privkey; +} + +static gnutls_pubkey_t create_pubkey_from_privkey( gnutls_privkey_t privkey ) +{ + gnutls_pubkey_t pubkey; + int ret; + + if ((ret = pgnutls_pubkey_init( &pubkey ))) + { + pgnutls_perror( ret ); + return NULL; + } + + if ((ret = pgnutls_pubkey_import_privkey( pubkey, privkey, 0, 0 ))) + { + pgnutls_perror( ret ); + pgnutls_pubkey_deinit( pubkey ); + return NULL; + } + + return pubkey; +} + +static gnutls_dh_params_t get_dh_params( gnutls_privkey_t privkey ) +{ + gnutls_dh_params_t params; + gnutls_datum_t x; + int ret; + + if ((ret = pgnutls_dh_params_init( ¶ms ))) + { + pgnutls_perror( ret ); + return NULL; + } + + if ((ret = pgnutls_privkey_export_dh_raw( privkey, params, NULL, &x, 0 ))) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + return NULL; + } + + free( x.data ); + return params; +} + +NTSTATUS key_asymmetric_generate( void *args ) +{ + struct key *key = args; + gnutls_pk_algorithm_t pk_alg; + gnutls_privkey_t privkey; + gnutls_pubkey_t pubkey; + unsigned int bitlen; + + if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; + if (key_data(key)->a.privkey) return STATUS_INVALID_HANDLE; + + switch (key->alg_id) + { + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + pk_alg = GNUTLS_PK_RSA; + bitlen = key->u.a.bitlen; + break; + + case ALG_ID_DH: + pk_alg = GNUTLS_PK_DH; + bitlen = key->u.a.bitlen; + break; + + case ALG_ID_DSA: + pk_alg = GNUTLS_PK_DSA; + bitlen = key->u.a.bitlen; + break; + + case ALG_ID_ECDH_P256: + case ALG_ID_ECDSA_P256: + pk_alg = GNUTLS_PK_ECC; /* compatible with ECDSA and ECDH */ + bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP256R1 ); + break; + + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P384: + pk_alg = GNUTLS_PK_ECC; /* compatible with ECDSA and ECDH */ + bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP384R1 ); + break; + + default: + FIXME( "algorithm %u not supported\n", key->alg_id ); + return STATUS_NOT_SUPPORTED; + } + + if (key->alg_id == ALG_ID_DH && key_data(key)->a.dh_params) + { + gnutls_keygen_data_st data; + + data.type = GNUTLS_KEYGEN_DH; + data.data = (unsigned char *)key_data(key)->a.dh_params; + data.size = 0; + if (!(privkey = create_privkey( pk_alg, bitlen, &data, 1 ))) return STATUS_INTERNAL_ERROR; + } + else if (!(privkey = create_privkey( pk_alg, bitlen, NULL, 0 ))) return STATUS_INTERNAL_ERROR; + + if (key->alg_id == ALG_ID_DH && !key_data(key)->a.dh_params && + !(key_data(key)->a.dh_params = get_dh_params( privkey ))) + { + pgnutls_privkey_deinit( privkey ); + return STATUS_INTERNAL_ERROR; + } + + if (!(pubkey = create_pubkey_from_privkey( privkey ))) + { + pgnutls_privkey_deinit( privkey ); + return STATUS_INTERNAL_ERROR; + } + + key_data(key)->a.privkey = privkey; + key_data(key)->a.pubkey = pubkey; + return STATUS_SUCCESS; +} + +static NTSTATUS key_export_ecc( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len ) +{ + BCRYPT_ECCKEY_BLOB *ecc_blob; + gnutls_ecc_curve_t curve; + gnutls_datum_t x, y, d; + DWORD magic, size; + UCHAR *dst; + int ret; + + switch (key->alg_id) + { + 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_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; + + default: + FIXME( "algorithm %u does not yet support exporting ecc blob\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER; + + if ((ret = pgnutls_privkey_export_ecc_raw( key_data(key)->a.privkey, &curve, &x, &y, &d ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if (curve != GNUTLS_ECC_CURVE_SECP256R1 && curve != GNUTLS_ECC_CURVE_SECP384R1) + { + FIXME( "curve %u not supported\n", curve ); + free( x.data ); free( y.data ); free( d.data ); + return STATUS_NOT_IMPLEMENTED; + } + + *ret_len = sizeof(*ecc_blob) + EXPORT_SIZE( x, size, 1 ) + EXPORT_SIZE( y, size, 1 ) + EXPORT_SIZE( d, size, 1 ); + if (len >= *ret_len && buf) + { + ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf; + ecc_blob->dwMagic = magic; + ecc_blob->cbKey = size; + + dst = (UCHAR *)(ecc_blob + 1); + dst += export_gnutls_datum( dst, size, &x, 1 ); + dst += export_gnutls_datum( dst, size, &y, 1 ); + export_gnutls_datum( dst, size, &d, 1 ); + } + + free( x.data ); free( y.data ); free( d.data ); + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_ecc( struct key *key, UCHAR *buf, ULONG len ) +{ + BCRYPT_ECCKEY_BLOB *ecc_blob; + gnutls_ecc_curve_t curve; + gnutls_privkey_t handle; + gnutls_datum_t x, y, k; + int ret; + + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDSA_P256: + curve = GNUTLS_ECC_CURVE_SECP256R1; + break; + + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P384: + curve = GNUTLS_ECC_CURVE_SECP384R1; + break; + + default: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + if ((ret = pgnutls_privkey_init( &handle ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf; + x.data = (unsigned char *)(ecc_blob + 1); + x.size = ecc_blob->cbKey; + y.data = x.data + ecc_blob->cbKey; + y.size = ecc_blob->cbKey; + k.data = y.data + ecc_blob->cbKey; + k.size = ecc_blob->cbKey; + + if ((ret = pgnutls_privkey_import_ecc_raw( handle, curve, &x, &y, &k ))) + { + pgnutls_perror( ret ); + pgnutls_privkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey ); + key_data(key)->a.privkey = handle; + 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; + gnutls_datum_t m, e, d, p, q, u, e1, e2; + ULONG size = key->u.a.bitlen / 8; + BOOL full = (flags & KEY_EXPORT_FLAG_RSA_FULL); + UCHAR *dst; + int ret; + + if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER; + + if ((ret = pgnutls_privkey_export_rsa_raw( key_data(key)->a.privkey, &m, &e, &d, &p, &q, &u, &e1, &e2 ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + *ret_len = sizeof(*rsa_blob) + EXPORT_SIZE( e, size, 0 ) + EXPORT_SIZE( m, size, 1 ) + + EXPORT_SIZE( p, size / 2, 1 ) + EXPORT_SIZE( q, size / 2, 1 ); + + if (full) *ret_len += EXPORT_SIZE( e1, size / 2, 1 ) + EXPORT_SIZE( e2, size / 2, 1 ) + + EXPORT_SIZE( u, size / 2, 1 ) + EXPORT_SIZE( d, size, 1 ); + + if (len >= *ret_len && buf) + { + rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf; + rsa_blob->Magic = full ? BCRYPT_RSAFULLPRIVATE_MAGIC : BCRYPT_RSAPRIVATE_MAGIC; + rsa_blob->BitLength = key->u.a.bitlen; + + dst = (UCHAR *)(rsa_blob + 1); + rsa_blob->cbPublicExp = export_gnutls_datum( dst, size, &e, 0 ); + + dst += rsa_blob->cbPublicExp; + rsa_blob->cbModulus = export_gnutls_datum( dst, size, &m, 1 ); + + dst += rsa_blob->cbModulus; + rsa_blob->cbPrime1 = export_gnutls_datum( dst, size / 2, &p, 1 ); + + dst += rsa_blob->cbPrime1; + rsa_blob->cbPrime2 = export_gnutls_datum( dst, size / 2, &q, 1 ); + + if (full) + { + dst += rsa_blob->cbPrime2; + export_gnutls_datum( dst, size / 2, &e1, 1 ); + + dst += rsa_blob->cbPrime1; + export_gnutls_datum( dst, size / 2, &e2, 1 ); + + dst += rsa_blob->cbPrime2; + export_gnutls_datum( dst, size / 2, &u, 1 ); + + dst += rsa_blob->cbPrime1; + export_gnutls_datum( dst, size, &d, 1 ); + } + } + + free( m.data ); free( e.data ); free( d.data ); free( p.data ); free( q.data ); free( u.data ); + free( e1.data ); free( e2.data ); + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_rsa( struct key *key, UCHAR *buf, ULONG len ) +{ + BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf; + gnutls_datum_t m, e, p, q; + gnutls_privkey_t handle; + int ret; + + if ((ret = pgnutls_privkey_init( &handle ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + e.data = (unsigned char *)(rsa_blob + 1); + e.size = rsa_blob->cbPublicExp; + m.data = e.data + e.size; + m.size = rsa_blob->cbModulus; + p.data = m.data + m.size; + p.size = rsa_blob->cbPrime1; + q.data = p.data + p.size; + q.size = rsa_blob->cbPrime2; + + if ((ret = pgnutls_privkey_import_rsa_raw( handle, &m, &e, NULL, &p, &q, NULL, NULL, NULL ))) + { + pgnutls_perror( ret ); + pgnutls_privkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey ); + key_data(key)->a.privkey = handle; + return STATUS_SUCCESS; +} + +static NTSTATUS key_export_dsa_capi( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len ) +{ + BLOBHEADER *hdr; + DSSPUBKEY *pubkey; + gnutls_datum_t p, q, g, y, x; + ULONG size = key->u.a.bitlen / 8; + UCHAR *dst; + int ret; + + if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER; + + if ((ret = pgnutls_privkey_export_dsa_raw( key_data(key)->a.privkey, &p, &q, &g, &y, &x ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if (q.size > 21 || x.size > 21) + { + ERR( "can't export key in this format\n" ); + free( p.data ); free( q.data ); free( g.data ); free( y.data ); free( x.data ); + return STATUS_NOT_SUPPORTED; + } + + *ret_len = sizeof(*hdr) + sizeof(*pubkey) + sizeof(key->u.a.dss_seed) + + EXPORT_SIZE( p, size, 1 ) + 20 + EXPORT_SIZE( g, size, 1 ) + 20; + if (len >= *ret_len && buf) + { + hdr = (BLOBHEADER *)buf; + hdr->bType = PRIVATEKEYBLOB; + hdr->bVersion = 2; + hdr->reserved = 0; + hdr->aiKeyAlg = CALG_DSS_SIGN; + + pubkey = (DSSPUBKEY *)(hdr + 1); + pubkey->magic = MAGIC_DSS2; + pubkey->bitlen = key->u.a.bitlen; + + dst = (UCHAR *)(pubkey + 1); + export_gnutls_datum( dst, size, &p, 1 ); + reverse_bytes( dst, size ); + dst += size; + + export_gnutls_datum( dst, 20, &q, 1 ); + reverse_bytes( dst, 20 ); + dst += 20; + + export_gnutls_datum( dst, size, &g, 1 ); + reverse_bytes( dst, size ); + dst += size; + + export_gnutls_datum( dst, 20, &x, 1 ); + reverse_bytes( dst, 20 ); + dst += 20; + + memcpy( dst, &key->u.a.dss_seed, sizeof(key->u.a.dss_seed) ); + } + + free( p.data ); free( q.data ); free( g.data ); free( y.data ); free( x.data ); + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dsa_capi( struct key *key, UCHAR *buf, ULONG len ) +{ + BLOBHEADER *hdr = (BLOBHEADER *)buf; + DSSPUBKEY *pubkey; + gnutls_privkey_t handle; + gnutls_datum_t p, q, g, x; + unsigned char *data, p_data[128], q_data[20], g_data[128], x_data[20]; + int i, ret, size; + + if ((ret = pgnutls_privkey_init( &handle ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + pubkey = (DSSPUBKEY *)(hdr + 1); + if ((size = pubkey->bitlen / 8) > sizeof(p_data)) + { + FIXME( "size %u not supported\n", size ); + pgnutls_privkey_deinit( handle ); + return STATUS_NOT_SUPPORTED; + } + data = (unsigned char *)(pubkey + 1); + + p.data = p_data; + p.size = size; + for (i = 0; i < p.size; i++) p.data[i] = data[p.size - i - 1]; + data += p.size; + + q.data = q_data; + q.size = sizeof(q_data); + for (i = 0; i < q.size; i++) q.data[i] = data[q.size - i - 1]; + data += q.size; + + g.data = g_data; + g.size = size; + for (i = 0; i < g.size; i++) g.data[i] = data[g.size - i - 1]; + data += g.size; + + x.data = x_data; + x.size = sizeof(x_data); + for (i = 0; i < x.size; i++) x.data[i] = data[x.size - i - 1]; + data += x.size; + + if ((ret = pgnutls_privkey_import_dsa_raw( handle, &p, &q, &g, NULL, &x ))) + { + pgnutls_perror( ret ); + pgnutls_privkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + memcpy( &key->u.a.dss_seed, data, sizeof(key->u.a.dss_seed) ); + + if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey ); + key_data(key)->a.privkey = handle; + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_ecc_public( struct key *key, UCHAR *buf, ULONG len ) +{ + BCRYPT_ECCKEY_BLOB *ecc_blob; + gnutls_ecc_curve_t curve; + gnutls_datum_t x, y; + gnutls_pubkey_t handle; + int ret; + + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDSA_P256: + curve = GNUTLS_ECC_CURVE_SECP256R1; break; + + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P384: + curve = GNUTLS_ECC_CURVE_SECP384R1; break; + + default: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + if ((ret = pgnutls_pubkey_init( &handle ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf; + x.data = buf + sizeof(*ecc_blob); + x.size = ecc_blob->cbKey; + y.data = buf + sizeof(*ecc_blob) + ecc_blob->cbKey; + y.size = ecc_blob->cbKey; + + if ((ret = pgnutls_pubkey_import_ecc_raw( handle, curve, &x, &y ))) + { + pgnutls_perror( ret ); + pgnutls_pubkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); + key_data(key)->a.pubkey = handle; + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_rsa_public( struct key *key, UCHAR *buf, ULONG len ) +{ + BCRYPT_RSAKEY_BLOB *rsa_blob; + gnutls_pubkey_t handle; + gnutls_datum_t m, e; + int ret; + + if ((ret = pgnutls_pubkey_init( &handle ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf; + e.data = buf + sizeof(*rsa_blob); + e.size = rsa_blob->cbPublicExp; + m.data = buf + sizeof(*rsa_blob) + rsa_blob->cbPublicExp; + m.size = rsa_blob->cbModulus; + + if ((ret = pgnutls_pubkey_import_rsa_raw( handle, &m, &e ))) + { + pgnutls_perror( ret ); + pgnutls_pubkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); + key_data(key)->a.pubkey = handle; + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dsa_public( struct key *key, UCHAR *buf, ULONG len ) +{ + BCRYPT_DSA_KEY_BLOB *dsa_blob; + gnutls_datum_t p, q, g, y; + gnutls_pubkey_t handle; + int ret; + + if ((ret = pgnutls_pubkey_init( &handle ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + dsa_blob = (BCRYPT_DSA_KEY_BLOB *)buf; + p.data = buf + sizeof(*dsa_blob); + p.size = dsa_blob->cbKey; + q.data = dsa_blob->q; + q.size = sizeof(dsa_blob->q); + g.data = buf + sizeof(*dsa_blob) + dsa_blob->cbKey; + g.size = dsa_blob->cbKey; + y.data = buf + sizeof(*dsa_blob) + dsa_blob->cbKey * 2; + y.size = dsa_blob->cbKey; + + if ((ret = pgnutls_pubkey_import_dsa_raw( handle, &p, &q, &g, &y ))) + { + pgnutls_perror( ret ); + pgnutls_pubkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); + key_data(key)->a.pubkey = handle; + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dsa_capi_public( struct key *key, UCHAR *buf, ULONG len ) +{ + BLOBHEADER *hdr; + DSSPUBKEY *pubkey; + gnutls_datum_t p, q, g, y; + gnutls_pubkey_t handle; + unsigned char *data, p_data[128], q_data[20], g_data[128], y_data[128]; + int i, ret, size; + + if ((ret = pgnutls_pubkey_init( &handle ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + hdr = (BLOBHEADER *)buf; + pubkey = (DSSPUBKEY *)(hdr + 1); + size = pubkey->bitlen / 8; + data = (unsigned char *)(pubkey + 1); + + p.data = p_data; + p.size = size; + for (i = 0; i < p.size; i++) p.data[i] = data[p.size - i - 1]; + data += p.size; + + q.data = q_data; + q.size = sizeof(q_data); + for (i = 0; i < q.size; i++) q.data[i] = data[q.size - i - 1]; + data += q.size; + + g.data = g_data; + g.size = size; + for (i = 0; i < g.size; i++) g.data[i] = data[g.size - i - 1]; + data += g.size; + + y.data = y_data; + y.size = sizeof(y_data); + for (i = 0; i < y.size; i++) y.data[i] = data[y.size - i - 1]; + + if ((ret = pgnutls_pubkey_import_dsa_raw( handle, &p, &q, &g, &y ))) + { + pgnutls_perror( ret ); + pgnutls_pubkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); + key_data(key)->a.pubkey = handle; + return STATUS_SUCCESS; +} + +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; + ULONG size = key->u.a.bitlen / 8; + gnutls_dh_params_t params; + gnutls_datum_t p, g, y; + UCHAR *dst; + int ret = GNUTLS_E_INVALID_REQUEST; + + if ((ret = pgnutls_dh_params_init( ¶ms )) < 0) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_pubkey_export_dh_raw( key_data(key)->a.pubkey, params, &y, 0 ))) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_dh_params_export_raw( params, &p, &g, NULL )) < 0) + { + pgnutls_perror( ret ); + free( y.data ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + + *ret_len = sizeof(*dh_blob) + EXPORT_SIZE(p, size, 1) + EXPORT_SIZE(g, size, 1) + EXPORT_SIZE(y, size, 1); + if (len >= *ret_len && buf) + { + dst = (UCHAR *)(dh_blob + 1); + dst += export_gnutls_datum( dst, size, &p, 1 ); + dst += export_gnutls_datum( dst, size, &g, 1 ); + dst += export_gnutls_datum( dst, size, &y, 1 ); + + dh_blob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC; + dh_blob->cbKey = size; + } + + free( p.data ); free( g.data ); free( y.data ); + 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; + gnutls_datum_t p, g, y, x; + gnutls_dh_params_t params; + ULONG size = key->u.a.bitlen / 8; + UCHAR *dst; + int ret; + + if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER; + + if ((ret = pgnutls_dh_params_init( ¶ms )) < 0) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_privkey_export_dh_raw( key_data(key)->a.privkey, params, &y, &x, 0 ))) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_dh_params_export_raw( params, &p, &g, NULL )) < 0) + { + pgnutls_perror( ret ); + free( y.data ); free( x.data ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + + *ret_len = sizeof(*dh_blob) + EXPORT_SIZE(p, size, 1) + EXPORT_SIZE(g, size, 1) + + EXPORT_SIZE(y, size, 1) + EXPORT_SIZE(x, size, 1); + if (len >= *ret_len && buf) + { + dst = (UCHAR *)(dh_blob + 1); + dst += export_gnutls_datum( dst, size, &p, 1 ); + dst += export_gnutls_datum( dst, size, &g, 1 ); + dst += export_gnutls_datum( dst, size, &y, 1 ); + dst += export_gnutls_datum( dst, size, &x, 1 ); + + dh_blob->dwMagic = BCRYPT_DH_PRIVATE_MAGIC; + dh_blob->cbKey = size; + } + + free( p.data ); free( g.data ); free( y.data ); free( x.data ); + pgnutls_dh_params_deinit( params ); + return STATUS_SUCCESS; +} + +static NTSTATUS key_export_dh_params( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len ) +{ + BCRYPT_DH_PARAMETER_HEADER *hdr = (BCRYPT_DH_PARAMETER_HEADER *)buf; + unsigned int size = sizeof(*hdr) + key->u.a.bitlen / 8 * 2; + gnutls_datum_t p, g; + NTSTATUS status = STATUS_SUCCESS; + UCHAR *dst; + int ret; + + if (!key_data(key)->a.dh_params) return STATUS_INVALID_PARAMETER; + + if ((ret = pgnutls_dh_params_export_raw( key_data(key)->a.dh_params, &p, &g, NULL ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + *ret_len = size; + if (len < size) status = STATUS_BUFFER_TOO_SMALL; + else if (buf) + { + hdr->cbLength = size; + hdr->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC; + hdr->cbKeyLength = key->u.a.bitlen / 8; + + dst = (UCHAR *)(hdr + 1); + dst += export_gnutls_datum( dst, hdr->cbKeyLength, &p, 1 ); + dst += export_gnutls_datum( dst, hdr->cbKeyLength, &g, 1 ); + } + + free( p.data ); free( g.data ); + return status; +} + +NTSTATUS key_asymmetric_export( void *args ) +{ + const struct key_asymmetric_export_params *params = args; + struct key *key = params->key; + unsigned flags = params->flags; + + if (!(key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE; + + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + 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 (flags & KEY_EXPORT_FLAG_PUBLIC) + { + if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2) + return key_export_dsa_capi_public( key, params->buf, params->len, params->ret_len ); + return key_export_dsa_public( key, params->buf, params->len, params->ret_len ); + } + if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2) + return key_export_dsa_capi( 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: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } +} + +static NTSTATUS key_import_dh_public( struct key *key, UCHAR *buf, ULONG len ) +{ + BCRYPT_DH_KEY_BLOB *dh_blob; + gnutls_dh_params_t params; + gnutls_datum_t p, g, y; + gnutls_pubkey_t handle; + int ret; + + if ((ret = pgnutls_pubkey_init( &handle ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_dh_params_init( ¶ms )) < 0) + { + pgnutls_perror( ret ); + pgnutls_pubkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + dh_blob = (BCRYPT_DH_KEY_BLOB *)buf; + p.data = buf + sizeof(*dh_blob); + p.size = dh_blob->cbKey; + g.data = buf + sizeof(*dh_blob) + dh_blob->cbKey; + g.size = dh_blob->cbKey; + y.data = buf + sizeof(*dh_blob) + dh_blob->cbKey * 2; + y.size = dh_blob->cbKey; + + if ((ret = pgnutls_dh_params_import_raw( params, &p, &g )) < 0) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + pgnutls_pubkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_pubkey_import_dh_raw( handle, params, &y ))) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + pgnutls_pubkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); + key_data(key)->a.pubkey = handle; + + if (key_data(key)->a.dh_params) pgnutls_dh_params_deinit( key_data(key)->a.dh_params ); + key_data(key)->a.dh_params = params; + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dh( struct key *key, UCHAR *buf, ULONG len ) +{ + BCRYPT_DH_KEY_BLOB *dh_blob; + gnutls_dh_params_t params; + gnutls_datum_t p, g, y, x; + gnutls_privkey_t handle; + int ret; + + if ((ret = pgnutls_privkey_init( &handle ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_dh_params_init( ¶ms )) < 0) + { + pgnutls_perror( ret ); + pgnutls_privkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + dh_blob = (BCRYPT_DH_KEY_BLOB *)buf; + p.data = buf + sizeof(*dh_blob); + p.size = dh_blob->cbKey; + g.data = buf + sizeof(*dh_blob) + dh_blob->cbKey; + g.size = dh_blob->cbKey; + y.data = buf + sizeof(*dh_blob) + dh_blob->cbKey * 2; + y.size = dh_blob->cbKey; + x.data = buf + sizeof(*dh_blob) + dh_blob->cbKey * 3; + x.size = dh_blob->cbKey; + + if ((ret = pgnutls_dh_params_import_raw( params, &p, &g )) < 0) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + pgnutls_privkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_privkey_import_dh_raw( handle, params, &y, &x ))) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + pgnutls_privkey_deinit( handle ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey ); + key_data(key)->a.privkey = handle; + + if (key_data(key)->a.dh_params) pgnutls_dh_params_deinit( key_data(key)->a.dh_params ); + key_data(key)->a.dh_params = params; + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dh_params( struct key *key, UCHAR *buf, ULONG len ) +{ + BCRYPT_DH_PARAMETER_HEADER *dh_header = (BCRYPT_DH_PARAMETER_HEADER *)buf; + gnutls_dh_params_t params; + gnutls_datum_t p, g; + int ret; + + if ((ret = pgnutls_dh_params_init( ¶ms ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + p.data = (unsigned char *)(dh_header + 1); + p.size = dh_header->cbKeyLength; + g.data = p.data + dh_header->cbKeyLength; + g.size = dh_header->cbKeyLength; + + if ((ret = pgnutls_dh_params_import_raw( params, &p, &g ))) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.dh_params) pgnutls_dh_params_deinit( key_data(key)->a.dh_params ); + key_data(key)->a.dh_params = params; + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_import( void *args ) +{ + const struct key_asymmetric_import_params *params = args; + struct key *key = params->key; + unsigned flags = params->flags; + gnutls_pubkey_t pubkey; + NTSTATUS ret; + + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + if (flags & KEY_IMPORT_FLAG_PUBLIC) + return key_import_ecc_public( key, params->buf, params->len ); + ret = key_import_ecc( key, params->buf, params->len ); + break; + + 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 ); + ret = key_import_rsa( key, params->buf, params->len ); + break; + + 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) + { + ret = key_import_dsa_capi( key, params->buf, params->len ); + break; + } + 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 ); + ret = key_import_dh( key, params->buf, params->len ); + break; + + default: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + if (ret) return ret; + + if ((ret = pgnutls_pubkey_init( &pubkey ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if (pgnutls_pubkey_import_privkey( pubkey, key_data(params->key)->a.privkey, 0, 0 )) + { + /* Imported private key may be legitimately missing public key, so ignore the failure here. */ + pgnutls_pubkey_deinit( pubkey ); + } + else + { + if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); + key_data(key)->a.pubkey = pubkey; + } + return STATUS_SUCCESS; +} + +static NTSTATUS prepare_gnutls_signature_dsa( struct key *key, UCHAR *signature, ULONG signature_len, + gnutls_datum_t *gnutls_signature ) +{ + struct buffer buffer; + DWORD r_len = signature_len / 2; + DWORD s_len = r_len; + BYTE *r = signature; + BYTE *s = signature + r_len; + + buffer_init( &buffer ); + buffer_append_asn1_r_s( &buffer, r, r_len, s, s_len ); + if (buffer.error) + { + buffer_free( &buffer ); + return STATUS_NO_MEMORY; + } + + gnutls_signature->data = buffer.buffer; + gnutls_signature->size = buffer.pos; + return STATUS_SUCCESS; +} + +static NTSTATUS prepare_gnutls_signature_rsa( struct key *key, UCHAR *signature, ULONG signature_len, + gnutls_datum_t *gnutls_signature ) +{ + gnutls_signature->data = signature; + gnutls_signature->size = signature_len; + return STATUS_SUCCESS; +} + +static NTSTATUS prepare_gnutls_signature( struct key *key, UCHAR *signature, ULONG signature_len, + gnutls_datum_t *gnutls_signature ) +{ + switch (key->alg_id) + { + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + case ALG_ID_DSA: + return prepare_gnutls_signature_dsa( key, signature, signature_len, gnutls_signature ); + + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + return prepare_gnutls_signature_rsa( key, signature, signature_len, gnutls_signature ); + + default: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } +} + +static gnutls_digest_algorithm_t get_digest_from_id( const WCHAR *alg_id ) +{ + if (!wcscmp( alg_id, BCRYPT_SHA1_ALGORITHM )) return GNUTLS_DIG_SHA1; + if (!wcscmp( alg_id, BCRYPT_SHA256_ALGORITHM )) return GNUTLS_DIG_SHA256; + if (!wcscmp( alg_id, BCRYPT_SHA384_ALGORITHM )) return GNUTLS_DIG_SHA384; + if (!wcscmp( alg_id, BCRYPT_SHA512_ALGORITHM )) return GNUTLS_DIG_SHA512; + if (!wcscmp( alg_id, BCRYPT_MD2_ALGORITHM )) return GNUTLS_DIG_MD2; + if (!wcscmp( alg_id, BCRYPT_MD5_ALGORITHM )) return GNUTLS_DIG_MD5; + return GNUTLS_DIG_UNKNOWN; +} + +static NTSTATUS pubkey_set_rsa_pss_params( gnutls_pubkey_t key, gnutls_digest_algorithm_t dig, unsigned int salt_size ) +{ + gnutls_x509_spki_t spki; + int ret; + + if (((ret = pgnutls_x509_spki_init( &spki ) < 0))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + pgnutls_x509_spki_set_rsa_pss_params( spki, dig, salt_size ); + ret = pgnutls_pubkey_set_spki( key, spki, 0 ); + pgnutls_x509_spki_deinit( spki ); + if (ret < 0) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_verify( void *args ) +{ +#ifdef GNUTLS_VERIFY_ALLOW_BROKEN + unsigned int verify_flags = GNUTLS_VERIFY_ALLOW_BROKEN; +#else + unsigned int verify_flags = 0; +#endif + const struct key_asymmetric_verify_params *params = args; + struct key *key = params->key; + unsigned flags = params->flags; + gnutls_digest_algorithm_t hash_alg; + gnutls_sign_algorithm_t sign_alg; + gnutls_datum_t gnutls_hash, gnutls_signature; + gnutls_pk_algorithm_t pk_alg; + NTSTATUS status; + int ret; + + switch (key->alg_id) + { + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + { + if (flags) FIXME( "flags %#x not supported\n", flags ); + + /* only the hash size must match, not the actual hash function */ + switch (params->hash_len) + { + case 20: hash_alg = GNUTLS_DIG_SHA1; break; + case 32: hash_alg = GNUTLS_DIG_SHA256; break; + case 48: hash_alg = GNUTLS_DIG_SHA384; break; + + default: + FIXME( "hash size %u not yet supported\n", params->hash_len ); + return STATUS_INVALID_SIGNATURE; + } + pk_alg = GNUTLS_PK_ECC; + break; + } + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + { + if (flags & BCRYPT_PAD_PKCS1) + { + BCRYPT_PKCS1_PADDING_INFO *info = params->padding; + + if (!info) return STATUS_INVALID_PARAMETER; + if (!info->pszAlgId) + { + hash_alg = GNUTLS_DIG_UNKNOWN; + verify_flags |= GNUTLS_VERIFY_USE_TLS1_RSA; + } + else if ((hash_alg = get_digest_from_id(info->pszAlgId)) == GNUTLS_DIG_UNKNOWN) + { + FIXME( "hash algorithm %s not supported\n", debugstr_w(info->pszAlgId) ); + return STATUS_NOT_SUPPORTED; + } + pk_alg = GNUTLS_PK_RSA; + } + else if (flags & BCRYPT_PAD_PSS) + { + BCRYPT_PSS_PADDING_INFO *info = params->padding; + + if (!info) return STATUS_INVALID_PARAMETER; + if (!info->pszAlgId) return STATUS_INVALID_SIGNATURE; + if ((hash_alg = get_digest_from_id(info->pszAlgId)) == GNUTLS_DIG_UNKNOWN) + { + FIXME( "hash algorithm %s not supported\n", debugstr_w(info->pszAlgId) ); + return STATUS_NOT_SUPPORTED; + } + if ((status = pubkey_set_rsa_pss_params( key_data(key)->a.pubkey, hash_alg, info->cbSalt ))) return status; + pk_alg = GNUTLS_PK_RSA_PSS; + } + else return STATUS_INVALID_PARAMETER; + break; + } + case ALG_ID_DSA: + { + if (flags) FIXME( "flags %#x not supported\n", flags ); + if (params->hash_len != 20) + { + FIXME( "hash size %u not supported\n", params->hash_len ); + return STATUS_INVALID_PARAMETER; + } + hash_alg = GNUTLS_DIG_SHA1; + pk_alg = GNUTLS_PK_DSA; + break; + } + default: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + if ((sign_alg = pgnutls_pk_to_sign( pk_alg, hash_alg )) == GNUTLS_SIGN_UNKNOWN) + { + FIXME("GnuTLS does not support algorithm %u with hash len %u\n", key->alg_id, params->hash_len ); + return STATUS_NOT_IMPLEMENTED; + } + + if ((status = prepare_gnutls_signature( key, params->signature, params->signature_len, &gnutls_signature ))) + return status; + + gnutls_hash.data = params->hash; + gnutls_hash.size = params->hash_len; + + ret = pgnutls_pubkey_verify_hash2( key_data(key)->a.pubkey, sign_alg, verify_flags, &gnutls_hash, &gnutls_signature ); + if (gnutls_signature.data != params->signature) free( gnutls_signature.data ); + return (ret < 0) ? STATUS_INVALID_SIGNATURE : STATUS_SUCCESS; +} + +static unsigned int get_signature_length( enum alg_id id ) +{ + switch (id) + { + case ALG_ID_ECDSA_P256: return 64; + case ALG_ID_ECDSA_P384: return 96; + case ALG_ID_DSA: return 40; + default: + FIXME( "unhandled algorithm %u\n", id ); + return 0; + } +} + +static NTSTATUS format_gnutls_signature( enum alg_id type, gnutls_datum_t signature, + UCHAR *output, ULONG output_len, ULONG *ret_len ) +{ + switch (type) + { + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + { + *ret_len = signature.size; + if (output_len < signature.size) return STATUS_BUFFER_TOO_SMALL; + if (output) memcpy( output, signature.data, signature.size ); + return STATUS_SUCCESS; + } + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + case ALG_ID_DSA: + { + int err; + unsigned int sig_len = get_signature_length( type ); + gnutls_datum_t r, s; /* format as r||s */ + + if ((err = pgnutls_decode_rs_value( &signature, &r, &s ))) + { + pgnutls_perror( err ); + return STATUS_INTERNAL_ERROR; + } + + *ret_len = sig_len; + if (output_len < sig_len) return STATUS_BUFFER_TOO_SMALL; + + if (r.size > sig_len / 2 + 1 || s.size > sig_len / 2 + 1) + { + ERR( "we didn't get a correct signature\n" ); + return STATUS_INTERNAL_ERROR; + } + + if (output) + { + export_gnutls_datum( output, sig_len / 2, &r, 1 ); + export_gnutls_datum( output + sig_len / 2, sig_len / 2, &s, 1 ); + } + + free( r.data ); free( s.data ); + return STATUS_SUCCESS; + } + default: + return STATUS_INTERNAL_ERROR; + } +} + +static NTSTATUS privkey_set_rsa_pss_params( gnutls_privkey_t key, gnutls_digest_algorithm_t dig, unsigned int salt_size ) +{ + gnutls_x509_spki_t spki; + int ret; + + if (((ret = pgnutls_x509_spki_init( &spki ) < 0))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + pgnutls_x509_spki_set_rsa_pss_params( spki, dig, salt_size ); + ret = pgnutls_privkey_set_spki( key, spki, 0 ); + pgnutls_x509_spki_deinit( spki ); + if (ret < 0) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_sign( void *args ) +{ + const struct key_asymmetric_sign_params *params = args; + struct key *key = params->key; + unsigned int flags = params->flags, gnutls_flags = 0; + gnutls_datum_t hash, signature; + gnutls_digest_algorithm_t hash_alg; + NTSTATUS status; + int ret; + + if (key->alg_id == ALG_ID_ECDSA_P256 || key->alg_id == ALG_ID_ECDSA_P384) + { + /* With ECDSA, we find the digest algorithm from the hash length, and verify it */ + switch (params->input_len) + { + case 20: hash_alg = GNUTLS_DIG_SHA1; break; + case 32: hash_alg = GNUTLS_DIG_SHA256; break; + case 48: hash_alg = GNUTLS_DIG_SHA384; break; + case 64: hash_alg = GNUTLS_DIG_SHA512; break; + + default: + FIXME( "hash size %u not yet supported\n", params->input_len ); + return STATUS_INVALID_PARAMETER; + } + + if (flags == BCRYPT_PAD_PKCS1) + { + BCRYPT_PKCS1_PADDING_INFO *pad = params->padding; + if (pad && pad->pszAlgId && get_digest_from_id( pad->pszAlgId ) != hash_alg) + { + WARN( "incorrect hashing algorithm %s, expected %u\n", debugstr_w(pad->pszAlgId), hash_alg ); + return STATUS_INVALID_PARAMETER; + } + } + } + else if (key->alg_id == ALG_ID_DSA) + { + if (flags) FIXME( "flags %#x not supported\n", flags ); + if (params->input_len != 20) + { + FIXME( "hash size %u not supported\n", params->input_len ); + return STATUS_INVALID_PARAMETER; + } + hash_alg = GNUTLS_DIG_SHA1; + } + else if (flags == BCRYPT_PAD_PKCS1) + { + BCRYPT_PKCS1_PADDING_INFO *pad = params->padding; + + if (!pad) + { + WARN( "padding info not found\n" ); + return STATUS_INVALID_PARAMETER; + } + if (!pad->pszAlgId) hash_alg = GNUTLS_DIG_UNKNOWN; + else if ((hash_alg = get_digest_from_id( pad->pszAlgId )) == GNUTLS_DIG_UNKNOWN) + { + FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) ); + return STATUS_NOT_SUPPORTED; + } + } + else if (flags == BCRYPT_PAD_PSS) + { + BCRYPT_PSS_PADDING_INFO *pad = params->padding; + + if (!pad || !pad->pszAlgId) + { + WARN( "padding info not found\n" ); + return STATUS_INVALID_PARAMETER; + } + if (key->alg_id != ALG_ID_RSA && key->alg_id != ALG_ID_RSA_SIGN) + { + FIXME( "BCRYPT_PAD_PSS not supported for key algorithm %u\n", key->alg_id ); + return STATUS_NOT_SUPPORTED; + } + if ((hash_alg = get_digest_from_id( pad->pszAlgId )) == GNUTLS_DIG_UNKNOWN) + { + FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) ); + return STATUS_NOT_SUPPORTED; + } + + if ((status = privkey_set_rsa_pss_params( key_data(key)->a.privkey, hash_alg, pad->cbSalt ))) return status; + gnutls_flags = GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS; + } + else if (!flags) + { + WARN( "invalid flags %#x\n", flags ); + return STATUS_INVALID_PARAMETER; + } + else + { + FIXME( "flags %#x not implemented\n", flags ); + return STATUS_NOT_IMPLEMENTED; + } + + if (!params->output) + { + *params->ret_len = key->u.a.bitlen / 8; + return STATUS_SUCCESS; + } + if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER; + + hash.data = params->input; + hash.size = params->input_len; + + signature.data = NULL; + signature.size = 0; + + if ((ret = pgnutls_privkey_sign_hash( key_data(key)->a.privkey, hash_alg, gnutls_flags, &hash, &signature ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + status = format_gnutls_signature( key->alg_id, signature, params->output, params->output_len, params->ret_len ); + free( signature.data ); + return status; +} + +NTSTATUS key_asymmetric_destroy( void *args ) +{ + struct key *key = args; + + if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey ); + if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); + if (key_data(key)->a.dh_params) pgnutls_dh_params_deinit( key_data(key)->a.dh_params ); + return STATUS_SUCCESS; +} + +static NTSTATUS dup_privkey( struct key *key_orig, struct key *key_copy ) +{ + gnutls_privkey_t privkey; + int ret; + + if ((ret = pgnutls_privkey_init( &privkey ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + switch (key_orig->alg_id) + { + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + { + gnutls_datum_t m, e, d, p, q, u, e1, e2; + + if ((ret = pgnutls_privkey_export_rsa_raw( key_data(key_orig)->a.privkey, &m, &e, &d, &p, &q, &u, &e1, &e2 ))) + break; + ret = pgnutls_privkey_import_rsa_raw( privkey, &m, &e, &d, &p, &q, &u, &e1, &e2 ); + free( m.data ); free( e.data ); free( d.data ); free( p.data ); free( q.data ); free( u.data ); + free( e1.data ); free( e2.data ); + break; + } + case ALG_ID_DSA: + { + gnutls_datum_t p, q, g, y, x; + + if ((ret = pgnutls_privkey_export_dsa_raw( key_data(key_orig)->a.privkey, &p, &q, &g, &y, &x ))) break; + ret = pgnutls_privkey_import_dsa_raw( privkey, &p, &q, &g, &y, &x ); + free( p.data ); free( q.data ); free( g.data ); free( y.data ); free( x.data ); + if (!ret) key_copy->u.a.dss_seed = key_orig->u.a.dss_seed; + break; + } + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + { + gnutls_ecc_curve_t curve; + gnutls_datum_t x, y, k; + + if ((ret = pgnutls_privkey_export_ecc_raw( key_data(key_orig)->a.privkey, &curve, &x, &y, &k ))) break; + ret = pgnutls_privkey_import_ecc_raw( privkey, curve, &x, &y, &k ); + free( x.data ); free( y.data ); free( k.data ); + break; + } + case ALG_ID_DH: + { + gnutls_dh_params_t params; + gnutls_datum_t y, x; + + if ((ret = pgnutls_dh_params_init( ¶ms )) < 0) break; + if ((ret = pgnutls_privkey_export_dh_raw( key_data(key_orig)->a.privkey, params, &y, &x, 0 )) < 0) + { + pgnutls_dh_params_deinit( params ); + break; + } + ret = pgnutls_privkey_import_dh_raw( privkey, params, &y, &x ); + pgnutls_dh_params_deinit( params ); + free( x.data ); free( y.data ); + break; + } + default: + ERR( "unhandled algorithm %u\n", key_orig->alg_id ); + pgnutls_privkey_deinit( privkey ); + return STATUS_INTERNAL_ERROR; + } + + if (ret < 0) + { + pgnutls_perror( ret ); + pgnutls_privkey_deinit( privkey ); + return STATUS_INTERNAL_ERROR; + } + + key_data(key_copy)->a.privkey = privkey; + return STATUS_SUCCESS; +} + +static NTSTATUS dup_pubkey( struct key *key_orig, struct key *key_copy ) +{ + gnutls_pubkey_t pubkey; + int ret; + + if ((ret = pgnutls_pubkey_init( &pubkey ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + switch (key_orig->alg_id) + { + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + { + gnutls_datum_t m, e; + + if ((ret = pgnutls_pubkey_export_rsa_raw( key_data(key_orig)->a.pubkey, &m, &e ))) break; + ret = pgnutls_pubkey_import_rsa_raw( pubkey, &m, &e ); + free( m.data ); free( e.data ); + break; + } + case ALG_ID_DSA: + { + gnutls_datum_t p, q, g, y; + + if ((ret = pgnutls_pubkey_export_dsa_raw( key_data(key_orig)->a.pubkey, &p, &q, &g, &y ))) break; + ret = pgnutls_pubkey_import_dsa_raw( pubkey, &p, &q, &g, &y ); + free( p.data ); free( q.data ); free( g.data ); free( y.data ); + if (!ret) key_copy->u.a.dss_seed = key_orig->u.a.dss_seed; + break; + } + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + { + gnutls_ecc_curve_t curve; + gnutls_datum_t x, y; + + if ((ret = pgnutls_pubkey_export_ecc_raw( key_data(key_orig)->a.pubkey, &curve, &x, &y ))) break; + ret = pgnutls_pubkey_import_ecc_raw( pubkey, curve, &x, &y ); + free( x.data ); free( y.data ); + break; + } + case ALG_ID_DH: + { + gnutls_dh_params_t params; + gnutls_datum_t y; + + if ((ret = pgnutls_dh_params_init( ¶ms )) < 0) break; + if ((ret = pgnutls_pubkey_export_dh_raw( key_data(key_orig)->a.pubkey, params, &y, 0 )) < 0) + { + pgnutls_dh_params_deinit( params ); + break; + } + ret = pgnutls_pubkey_import_dh_raw( pubkey, params, &y ); + pgnutls_dh_params_deinit( params ); + free( y.data ); + break; + } + default: + ERR( "unhandled algorithm %u\n", key_orig->alg_id ); + pgnutls_pubkey_deinit( pubkey ); + return STATUS_INTERNAL_ERROR; + } + + if (ret < 0) + { + pgnutls_perror( ret ); + pgnutls_pubkey_deinit( pubkey ); + return STATUS_INTERNAL_ERROR; + } + + key_data(key_copy)->a.pubkey = pubkey; + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_duplicate( void *args ) +{ + const struct key_asymmetric_duplicate_params *params = args; + NTSTATUS status; + + if (key_data(params->key_orig)->a.privkey && (status = dup_privkey( params->key_orig, params->key_copy ))) + return status; + + if (key_data(params->key_orig)->a.pubkey && (status = dup_pubkey( params->key_orig, params->key_copy ))) + return status; + + return STATUS_SUCCESS; +} + +static NTSTATUS privkey_set_rsa_oaep_params( gnutls_privkey_t key, gnutls_digest_algorithm_t dig, gnutls_datum_t *label ) +{ + gnutls_x509_spki_t spki; + int ret; + + if (((ret = pgnutls_x509_spki_init( &spki ) < 0))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + pgnutls_x509_spki_set_rsa_oaep_params( spki, dig, label ); + ret = pgnutls_privkey_set_spki( key, spki, 0 ); + pgnutls_x509_spki_deinit( spki ); + if (ret < 0) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_decrypt( void *args ) +{ + const struct key_asymmetric_decrypt_params *params = args; + gnutls_datum_t e, d = { 0 }; + NTSTATUS status = STATUS_SUCCESS; + int ret; + + if (params->key->alg_id == ALG_ID_RSA && params->flags & BCRYPT_PAD_OAEP) + { + BCRYPT_OAEP_PADDING_INFO *pad = params->padding; + gnutls_digest_algorithm_t dig; + gnutls_datum_t label; + + if (!pad || !pad->pszAlgId) + { + WARN( "padding info not found\n" ); + return STATUS_INVALID_PARAMETER; + } + if ((dig = get_digest_from_id( pad->pszAlgId )) == GNUTLS_DIG_UNKNOWN) + { + FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) ); + return STATUS_NOT_SUPPORTED; + } + + label.data = pad->pbLabel; + label.size = pad->cbLabel; + if ((status = privkey_set_rsa_oaep_params( key_data(params->key)->a.privkey, dig, &label ))) return status; + } + + e.data = params->input; + e.size = params->input_len; + if ((ret = pgnutls_privkey_decrypt_data( key_data(params->key)->a.privkey, 0, &e, &d ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + *params->ret_len = d.size; + if (params->output_len >= d.size) memcpy( params->output, d.data, *params->ret_len ); + else if (params->output) status = STATUS_BUFFER_TOO_SMALL; + + free( d.data ); + return status; +} + +static NTSTATUS pubkey_set_rsa_oaep_params( gnutls_pubkey_t key, gnutls_digest_algorithm_t dig, gnutls_datum_t *label ) +{ + gnutls_x509_spki_t spki; + int ret; + + if (((ret = pgnutls_x509_spki_init( &spki ) < 0))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + pgnutls_x509_spki_set_rsa_oaep_params( spki, dig, label ); + ret = pgnutls_pubkey_set_spki( key, spki, 0 ); + pgnutls_x509_spki_deinit( spki ); + if (ret < 0) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_encrypt( void *args ) +{ + const struct key_asymmetric_encrypt_params *params = args; + gnutls_datum_t d, e = { 0 }; + NTSTATUS status = STATUS_SUCCESS; + int ret; + + if (!key_data(params->key)->a.pubkey) return STATUS_INVALID_HANDLE; + + if (params->key->alg_id == ALG_ID_RSA && params->flags & BCRYPT_PAD_OAEP) + { + BCRYPT_OAEP_PADDING_INFO *pad = params->padding; + gnutls_digest_algorithm_t dig; + gnutls_datum_t label; + + if (!pad || !pad->pszAlgId || !pad->pbLabel) + { + WARN( "padding info not found\n" ); + return STATUS_INVALID_PARAMETER; + } + if ((dig = get_digest_from_id( pad->pszAlgId )) == GNUTLS_DIG_UNKNOWN) + { + FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) ); + return STATUS_NOT_SUPPORTED; + } + + label.data = pad->pbLabel; + label.size = pad->cbLabel; + if ((status = pubkey_set_rsa_oaep_params( key_data(params->key)->a.pubkey, dig, &label ))) return status; + } + + d.data = params->input; + d.size = params->input_len; + if ((ret = pgnutls_pubkey_encrypt_data(key_data(params->key)->a.pubkey, 0, &d, &e))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + *params->ret_len = e.size; + if (params->output_len >= e.size) memcpy( params->output, e.data, *params->ret_len ); + else if (params->output_len == 0) status = STATUS_SUCCESS; + else status = STATUS_BUFFER_TOO_SMALL; + + free( e.data ); + return status; +} + +NTSTATUS key_asymmetric_derive_key( void *args ) +{ + const struct key_asymmetric_derive_key_params *params = args; + gnutls_datum_t s; + NTSTATUS status = STATUS_SUCCESS; + int ret; + + if ((ret = pgnutls_privkey_derive_secret( key_data(params->privkey)->a.privkey, + key_data(params->pubkey)->a.pubkey, NULL, &s, 0 ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + *params->ret_len = EXPORT_SIZE( s, params->privkey->u.a.bitlen / 8, 1 ); + if (params->output) + { + if (params->output_len < *params->ret_len) status = STATUS_BUFFER_TOO_SMALL; + else export_gnutls_datum( params->output, *params->ret_len, &s, 1 ); + } + + free( s.data ); + return status; +} + +NTSTATUS hash_init( struct hash *hash ) +{ + gnutls_digest_algorithm_t alg; + + if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; + + switch (hash->alg_id) + { + case ALG_ID_MD5: + alg = GNUTLS_DIG_MD5; + break; + case ALG_ID_SHA1: + alg = GNUTLS_DIG_SHA1; + break; + + case ALG_ID_SHA256: + alg = GNUTLS_DIG_SHA256; + break; + + case ALG_ID_SHA384: + alg = GNUTLS_DIG_SHA384; + break; + + case ALG_ID_SHA512: + alg = GNUTLS_DIG_SHA512; + break; + + default: + ERR( "unhandled id %u\n", hash->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + if (pgnutls_hash_init( &hash->u.hash_handle, alg )) return STATUS_INTERNAL_ERROR; + return STATUS_SUCCESS; +} + +NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) +{ + gnutls_mac_algorithm_t alg; + + if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; + + switch (hash->alg_id) + { + case ALG_ID_MD5: + alg = GNUTLS_MAC_MD5; + break; + case ALG_ID_SHA1: + alg = GNUTLS_MAC_SHA1; + break; + + case ALG_ID_SHA256: + alg = GNUTLS_MAC_SHA256; + break; + + case ALG_ID_SHA384: + alg = GNUTLS_MAC_SHA384; + break; + + case ALG_ID_SHA512: + alg = GNUTLS_MAC_SHA512; + break; + + default: + ERR( "unhandled id %u\n", hash->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + if (pgnutls_hmac_init( &hash->u.hmac_handle, alg, key, key_size )) return STATUS_INTERNAL_ERROR; + return STATUS_SUCCESS; +} + +NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) +{ + if (pgnutls_hash( hash->u.hash_handle, input, size )) return STATUS_INTERNAL_ERROR; + return STATUS_SUCCESS; +} + +NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) +{ + if (pgnutls_hmac( hash->u.hmac_handle, input, size )) return STATUS_INTERNAL_ERROR; + return STATUS_SUCCESS; +} + +NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) +{ + pgnutls_hash_deinit( hash->u.hash_handle, output ); + return STATUS_SUCCESS; +} + +NTSTATUS hmac_finish( struct hash *hash, UCHAR *output, ULONG size ) +{ + pgnutls_hmac_deinit( hash->u.hmac_handle, output ); + return STATUS_SUCCESS; +} + +#endif /* HAVE_GNUTLS_HASH */ diff --git a/dll/win32/bcrypt/mbedtls.c b/dll/win32/bcrypt/mbedtls.c new file mode 100644 index 00000000000..5406673337a --- /dev/null +++ b/dll/win32/bcrypt/mbedtls.c @@ -0,0 +1,2804 @@ +#include "precomp.h" + +#ifdef SONAME_LIBMBEDTLS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +WINE_DEFAULT_DEBUG_CHANNEL(bcrypt); + +#ifndef __REACTOS__ +static void *libmbedtls_handle; +#endif +static mbedtls_entropy_context entropy; +static mbedtls_ctr_drbg_context ctr_drbg; +static CRITICAL_SECTION rng_lock; +static BOOL rng_seeded; + +union key_data +{ + union + { + mbedtls_cipher_context_t cipher_ctx; + } s; + union + { + struct + { + mbedtls_pk_context privkey; + mbedtls_pk_context pubkey; + } pk; + struct + { + mbedtls_dhm_context privkey; + mbedtls_dhm_context pubkey; + } dh; +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + struct + { + mbedtls_dsa_context privkey; + mbedtls_dsa_context pubkey; + } dsa; +#endif + } 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; +} + +#ifndef __REACTOS__ +#define MAKE_FUNCPTR(f) static typeof(f) * p##f; +MAKE_FUNCPTR(mbedtls_md_init) +MAKE_FUNCPTR(mbedtls_md_info_from_type) +MAKE_FUNCPTR(mbedtls_md_get_size) +MAKE_FUNCPTR(mbedtls_md_setup) +MAKE_FUNCPTR(mbedtls_md_update) +MAKE_FUNCPTR(mbedtls_md_starts) +MAKE_FUNCPTR(mbedtls_md_finish) +MAKE_FUNCPTR(mbedtls_md_hmac_update) +MAKE_FUNCPTR(mbedtls_md_hmac_starts) +MAKE_FUNCPTR(mbedtls_md_hmac_finish) +MAKE_FUNCPTR(mbedtls_md_free) +MAKE_FUNCPTR(mbedtls_cipher_init) +MAKE_FUNCPTR(mbedtls_cipher_setup) +MAKE_FUNCPTR(mbedtls_cipher_info_from_type) +MAKE_FUNCPTR(mbedtls_cipher_setkey) +MAKE_FUNCPTR(mbedtls_cipher_set_iv) +MAKE_FUNCPTR(mbedtls_cipher_set_padding_mode) +MAKE_FUNCPTR(mbedtls_cipher_update) +MAKE_FUNCPTR(mbedtls_cipher_update_ad) +MAKE_FUNCPTR(mbedtls_cipher_check_tag) +MAKE_FUNCPTR(mbedtls_cipher_write_tag) +MAKE_FUNCPTR(mbedtls_cipher_finish) +MAKE_FUNCPTR(mbedtls_cipher_free) +MAKE_FUNCPTR(mbedtls_ctr_drbg_init) +MAKE_FUNCPTR(mbedtls_ctr_drbg_random) +MAKE_FUNCPTR(mbedtls_ctr_drbg_seed) +MAKE_FUNCPTR(mbedtls_ctr_drbg_free) +MAKE_FUNCPTR(mbedtls_entropy_init) +MAKE_FUNCPTR(mbedtls_entropy_func) +MAKE_FUNCPTR(mbedtls_entropy_free) +MAKE_FUNCPTR(mbedtls_pk_init) +MAKE_FUNCPTR(mbedtls_pk_get_type) +MAKE_FUNCPTR(mbedtls_pk_setup) +MAKE_FUNCPTR(mbedtls_pk_info_from_type) +MAKE_FUNCPTR(mbedtls_pk_parse_key) +MAKE_FUNCPTR(mbedtls_pk_parse_public_key) +MAKE_FUNCPTR(mbedtls_pk_verify_ext) +MAKE_FUNCPTR(mbedtls_pk_sign) +MAKE_FUNCPTR(mbedtls_pk_encrypt) +MAKE_FUNCPTR(mbedtls_pk_decrypt) +MAKE_FUNCPTR(mbedtls_pk_free) +MAKE_FUNCPTR(mbedtls_ecdh_init) +MAKE_FUNCPTR(mbedtls_ecdh_compute_shared) +MAKE_FUNCPTR(mbedtls_ecdh_free) +MAKE_FUNCPTR(mbedtls_rsa_gen_key) +MAKE_FUNCPTR(mbedtls_rsa_export) +MAKE_FUNCPTR(mbedtls_rsa_import_raw) +MAKE_FUNCPTR(mbedtls_rsa_export_raw) +MAKE_FUNCPTR(mbedtls_rsa_export_crt) +MAKE_FUNCPTR(mbedtls_rsa_set_padding) +MAKE_FUNCPTR(mbedtls_rsa_complete) +MAKE_FUNCPTR(mbedtls_rsa_check_pubkey) +MAKE_FUNCPTR(mbedtls_rsa_check_privkey) +MAKE_FUNCPTR(mbedtls_rsa_check_pub_priv) +MAKE_FUNCPTR(mbedtls_ecp_gen_key) +MAKE_FUNCPTR(mbedtls_ecp_group_load) +MAKE_FUNCPTR(mbedtls_ecp_point_read_binary) +MAKE_FUNCPTR(mbedtls_ecp_point_write_binary) +MAKE_FUNCPTR(mbedtls_ecp_check_pubkey) +MAKE_FUNCPTR(mbedtls_ecp_copy) +MAKE_FUNCPTR(mbedtls_mpi_init) +MAKE_FUNCPTR(mbedtls_mpi_size) +MAKE_FUNCPTR(mbedtls_mpi_lset) +MAKE_FUNCPTR(mbedtls_mpi_copy) +MAKE_FUNCPTR(mbedtls_mpi_cmp_mpi) +MAKE_FUNCPTR(mbedtls_mpi_cmp_int) +MAKE_FUNCPTR(mbedtls_mpi_read_binary) +MAKE_FUNCPTR(mbedtls_mpi_write_binary) +MAKE_FUNCPTR(mbedtls_mpi_read_string) +MAKE_FUNCPTR(mbedtls_mpi_gen_prime) +MAKE_FUNCPTR(mbedtls_mpi_sub_int) +MAKE_FUNCPTR(mbedtls_mpi_div_int) +MAKE_FUNCPTR(mbedtls_mpi_is_prime_ext) +MAKE_FUNCPTR(mbedtls_mpi_free) +MAKE_FUNCPTR(mbedtls_asn1_get_len) +MAKE_FUNCPTR(mbedtls_asn1_get_mpi) +MAKE_FUNCPTR(mbedtls_asn1_get_tag) +MAKE_FUNCPTR(mbedtls_asn1_write_len) +MAKE_FUNCPTR(mbedtls_asn1_write_mpi) +MAKE_FUNCPTR(mbedtls_asn1_write_tag) +MAKE_FUNCPTR(mbedtls_asn1_write_raw_buffer) +MAKE_FUNCPTR(mbedtls_dhm_init) +MAKE_FUNCPTR(mbedtls_dhm_make_params) +MAKE_FUNCPTR(mbedtls_dhm_calc_secret) +MAKE_FUNCPTR(mbedtls_dhm_free) +MAKE_FUNCPTR(mbedtls_dsa_init) +MAKE_FUNCPTR(mbedtls_dsa_set_group) +MAKE_FUNCPTR(mbedtls_dsa_genkey) +MAKE_FUNCPTR(mbedtls_dsa_check_pqg) +MAKE_FUNCPTR(mbedtls_dsa_check_pubkey) +MAKE_FUNCPTR(mbedtls_dsa_check_privkey) +MAKE_FUNCPTR(mbedtls_dsa_pubkey_from_privkey) +MAKE_FUNCPTR(mbedtls_dsa_verify) +MAKE_FUNCPTR(mbedtls_dsa_sign) +MAKE_FUNCPTR(mbedtls_dsa_free) +#undef MAKE_FUNCPTR +#endif + +NTSTATUS process_attach(void *args) +{ +#ifndef __REACTOS__ + if (!(libmbedtls_handle = wine_dlopen(SONAME_LIBMBEDTLS, RTLD_NOW, NULL, 0))) + { + ERR("failed to load libmbedtls, no support for crypto hashes\n"); + return STATUS_DLL_NOT_FOUND; + } + +#define LOAD_FUNCPTR(f) \ + if (!(p##f = wine_dlsym(libmbedtls_handle, #f, NULL, 0))) \ + { \ + ERR("failed to load %s\n", #f); \ + goto fail; \ + } + + LOAD_FUNCPTR(mbedtls_md_init) + LOAD_FUNCPTR(mbedtls_md_info_from_type) + LOAD_FUNCPTR(mbedtls_md_get_size) + LOAD_FUNCPTR(mbedtls_md_setup) + LOAD_FUNCPTR(mbedtls_md_update) + LOAD_FUNCPTR(mbedtls_md_starts) + LOAD_FUNCPTR(mbedtls_md_finish) + LOAD_FUNCPTR(mbedtls_md_hmac_update) + LOAD_FUNCPTR(mbedtls_md_hmac_starts) + LOAD_FUNCPTR(mbedtls_md_hmac_finish) + LOAD_FUNCPTR(mbedtls_md_free); + LOAD_FUNCPTR(mbedtls_cipher_init) + LOAD_FUNCPTR(mbedtls_cipher_setup) + LOAD_FUNCPTR(mbedtls_cipher_info_from_type) + LOAD_FUNCPTR(mbedtls_cipher_setkey) + LOAD_FUNCPTR(mbedtls_cipher_set_iv) + LOAD_FUNCPTR(mbedtls_cipher_set_padding_mode); + LOAD_FUNCPTR(mbedtls_cipher_update); + LOAD_FUNCPTR(mbedtls_cipher_update_ad); + LOAD_FUNCPTR(mbedtls_cipher_check_tag); + LOAD_FUNCPTR(mbedtls_cipher_write_tag); + LOAD_FUNCPTR(mbedtls_cipher_finish); + LOAD_FUNCPTR(mbedtls_cipher_free); + LOAD_FUNCPTR(mbedtls_ctr_drbg_init) + LOAD_FUNCPTR(mbedtls_ctr_drbg_random) + LOAD_FUNCPTR(mbedtls_ctr_drbg_seed) + LOAD_FUNCPTR(mbedtls_ctr_drbg_free) + LOAD_FUNCPTR(mbedtls_entropy_init) + LOAD_FUNCPTR(mbedtls_entropy_func) + LOAD_FUNCPTR(mbedtls_entropy_free) + LOAD_FUNCPTR(mbedtls_pk_init) + LOAD_FUNCPTR(mbedtls_pk_get_type) + LOAD_FUNCPTR(mbedtls_pk_setup) + LOAD_FUNCPTR(mbedtls_pk_info_from_type) + LOAD_FUNCPTR(mbedtls_pk_parse_key) + LOAD_FUNCPTR(mbedtls_pk_parse_public_key) + LOAD_FUNCPTR(mbedtls_pk_verify_ext) + LOAD_FUNCPTR(mbedtls_pk_sign) + LOAD_FUNCPTR(mbedtls_pk_encrypt) + LOAD_FUNCPTR(mbedtls_pk_decrypt) + LOAD_FUNCPTR(mbedtls_pk_free) + LOAD_FUNCPTR(mbedtls_ecdh_init) + LOAD_FUNCPTR(mbedtls_ecdh_compute_shared) + LOAD_FUNCPTR(mbedtls_ecdh_free) + LOAD_FUNCPTR(mbedtls_rsa_gen_key) + LOAD_FUNCPTR(mbedtls_rsa_import_raw) + LOAD_FUNCPTR(mbedtls_rsa_export_raw) + LOAD_FUNCPTR(mbedtls_rsa_export) + LOAD_FUNCPTR(mbedtls_rsa_export_crt) + LOAD_FUNCPTR(mbedtls_rsa_set_padding) + LOAD_FUNCPTR(mbedtls_rsa_complete) + LOAD_FUNCPTR(mbedtls_rsa_check_pubkey) + LOAD_FUNCPTR(mbedtls_rsa_check_privkey) + LOAD_FUNCPTR(mbedtls_rsa_check_pub_priv) + LOAD_FUNCPTR(mbedtls_ecp_gen_key) + LOAD_FUNCPTR(mbedtls_ecp_group_load) + LOAD_FUNCPTR(mbedtls_ecp_point_read_binary) + LOAD_FUNCPTR(mbedtls_ecp_point_write_binary) + LOAD_FUNCPTR(mbedtls_ecp_check_pubkey) + LOAD_FUNCPTR(mbedtls_ecp_copy) + LOAD_FUNCPTR(mbedtls_mpi_init) + LOAD_FUNCPTR(mbedtls_mpi_size) + LOAD_FUNCPTR(mbedtls_mpi_lset) + LOAD_FUNCPTR(mbedtls_mpi_copy) + LOAD_FUNCPTR(mbedtls_mpi_cmp_mpi) + LOAD_FUNCPTR(mbedtls_mpi_cmp_int) + LOAD_FUNCPTR(mbedtls_mpi_read_binary) + LOAD_FUNCPTR(mbedtls_mpi_write_binary) + LOAD_FUNCPTR(mbedtls_mpi_read_string) + LOAD_FUNCPTR(mbedtls_mpi_gen_prime) + LOAD_FUNCPTR(mbedtls_mpi_sub_int) + LOAD_FUNCPTR(mbedtls_mpi_div_int) + LOAD_FUNCPTR(mbedtls_mpi_is_prime_ext) + LOAD_FUNCPTR(mbedtls_mpi_free) + LOAD_FUNCPTR(mbedtls_asn1_get_len) + LOAD_FUNCPTR(mbedtls_asn1_get_mpi) + LOAD_FUNCPTR(mbedtls_asn1_get_tag) + LOAD_FUNCPTR(mbedtls_asn1_write_len) + LOAD_FUNCPTR(mbedtls_asn1_write_mpi) + LOAD_FUNCPTR(mbedtls_asn1_write_tag) + LOAD_FUNCPTR(mbedtls_asn1_write_raw_buffer) + LOAD_FUNCPTR(mbedtls_dhm_init) + LOAD_FUNCPTR(mbedtls_dhm_make_params) + LOAD_FUNCPTR(mbedtls_dhm_calc_secret) + LOAD_FUNCPTR(mbedtls_dhm_free) + LOAD_FUNCPTR(mbedtls_dsa_init) + LOAD_FUNCPTR(mbedtls_dsa_set_group) + LOAD_FUNCPTR(mbedtls_dsa_genkey) + LOAD_FUNCPTR(mbedtls_dsa_check_pqg) + LOAD_FUNCPTR(mbedtls_dsa_check_pubkey) + LOAD_FUNCPTR(mbedtls_dsa_check_privkey) + LOAD_FUNCPTR(mbedtls_dsa_pubkey_from_privkey) + LOAD_FUNCPTR(mbedtls_dsa_verify) + LOAD_FUNCPTR(mbedtls_dsa_sign) + LOAD_FUNCPTR(mbedtls_dsa_free) +#undef LOAD_FUNCPTR +#endif + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + InitializeCriticalSection(&rng_lock); + + return STATUS_SUCCESS; +#ifndef __REACTOS__ +fail: + wine_dlclose(libmbedtls_handle, NULL, 0); + libmbedtls_handle = NULL; + return STATUS_DLL_NOT_FOUND; +#endif +} + +#ifndef __REACTOS__ +#define mbedtls_md_init pmbedtls_md_init +#define mbedtls_md_info_from_type pmbedtls_md_info_from_type +#define mbedtls_md_get_size pmbedtls_md_get_size +#define mbedtls_md_setup pmbedtls_md_setup +#define mbedtls_md_update pmbedtls_md_update +#define mbedtls_md_starts pmbedtls_md_starts +#define mbedtls_md_finish pmbedtls_md_finish +#define mbedtls_md_hmac_update pmbedtls_md_hmac_update +#define mbedtls_md_hmac_starts pmbedtls_md_hmac_starts +#define mbedtls_md_hmac_finish pmbedtls_md_hmac_finish +#define mbedtls_md_free pmbedtls_md_free +#define mbedtls_cipher_init pmbedtls_cipher_init +#define mbedtls_cipher_setup pmbedtls_cipher_setup +#define mbedtls_cipher_info_from_type pmbedtls_cipher_info_from_type +#define mbedtls_cipher_setkey pmbedtls_cipher_setkey +#define mbedtls_cipher_set_iv pmbedtls_cipher_set_iv +#define mbedtls_cipher_set_padding_mode pmbedtls_cipher_set_padding_mode +#define mbedtls_cipher_update pmbedtls_cipher_update +#define mbedtls_cipher_update_ad pmbedtls_cipher_update_ad +#define mbedtls_cipher_check_tag pmbedtls_cipher_check_tag +#define mbedtls_cipher_write_tag pmbedtls_cipher_write_tag +#define mbedtls_cipher_finish pmbedtls_cipher_finish +#define mbedtls_cipher_free pmbedtls_cipher_free +#define mbedtls_ctr_drbg_init pmbedtls_ctr_drbg_init +#define mbedtls_ctr_drbg_random pmbedtls_ctr_drbg_random +#define mbedtls_ctr_drbg_seed pmbedtls_ctr_drbg_seed +#define mbedtls_ctr_drbg_free pmbedtls_ctr_drbg_free +#define mbedtls_entropy_init pmbedtls_entropy_init +#define mbedtls_entropy_func pmbedtls_entropy_func +#define mbedtls_entropy_free pmbedtls_entropy_free +#define mbedtls_pk_init pmbedtls_pk_init +#define mbedtls_pk_get_type pmbedtls_pk_get_type +#define mbedtls_pk_setup pmbedtls_pk_setup +#define mbedtls_pk_info_from_type pmbedtls_pk_info_from_type +#define mbedtls_pk_parse_key pmbedtls_pk_parse_key +#define mbedtls_pk_parse_public_key pmbedtls_pk_parse_public_key +#define mbedtls_pk_verify_ext pmbedtls_pk_verify_ext +#define mbedtls_pk_sign pmbedtls_pk_sign +#define mbedtls_pk_encrypt pmbedtls_pk_encrypt +#define mbedtls_pk_decrypt pmbedtls_pk_decrypt +#define mbedtls_pk_free pmbedtls_pk_free +#define mbedtls_ecdh_init pmbedtls_ecdh_init +#define mbedtls_ecdh_compute_shared pmbedtls_ecdh_compute_shared +#define mbedtls_ecdh_free pmbedtls_ecdh_free +#define mbedtls_rsa_gen_key pmbedtls_rsa_gen_key +#define mbedtls_rsa_import_raw pmbedtls_rsa_import_raw +#define mbedtls_rsa_export pmbedtls_rsa_export +#define mbedtls_rsa_export_raw pmbedtls_rsa_export_raw +#define mbedtls_rsa_export_crt pmbedtls_rsa_export_crt +#define mbedtls_rsa_set_padding pmbedtls_rsa_set_padding +#define mbedtls_rsa_complete pmbedtls_rsa_complete +#define mbedtls_rsa_check_pubkey pmbedtls_rsa_check_pubkey +#define mbedtls_rsa_check_privkey pmbedtls_rsa_check_privkey +#define mbedtls_rsa_check_pub_priv pmbedtls_rsa_check_pub_priv +#define mbedtls_ecp_gen_key pmbedtls_ecp_gen_key +#define mbedtls_ecp_group_load pmbedtls_ecp_group_load +#define mbedtls_ecp_point_read_binary pmbedtls_ecp_point_read_binary +#define mbedtls_ecp_point_write_binary pmbedtls_ecp_point_write_binary +#define mbedtls_ecp_check_pubkey pmbedtls_ecp_check_pubkey +#define mbedtls_ecp_copy pmbedtls_ecp_copy +#define mbedtls_mpi_init pmbedtls_mpi_init +#define mbedtls_mpi_size pmbedtls_mpi_size +#define mbedtls_mpi_lset pmbedtls_mpi_lset +#define mbedtls_mpi_copy pmbedtls_mpi_copy +#define mbedtls_mpi_cmp_mpi pmbedtls_mpi_cmp_mpi +#define mbedtls_mpi_cmp_int pmbedtls_mpi_cmp_int +#define mbedtls_mpi_read_binary pmbedtls_mpi_read_binary +#define mbedtls_mpi_write_binary pmbedtls_mpi_write_binary +#define mbedtls_mpi_read_string pmbedtls_mpi_read_string +#define mbedtls_mpi_gen_prime pmbedtls_mpi_gen_prime +#define mbedtls_mpi_sub_int pmbedtls_mpi_sub_int +#define mbedtls_mpi_div_int pmbedtls_mpi_div_int +#define mbedtls_mpi_is_prime_ext pmbedtls_mpi_is_prime_ext +#define mbedtls_mpi_free pmbedtls_mpi_free +#define mbedtls_asn1_get_len pmbedtls_asn1_get_len +#define mbedtls_asn1_get_mpi pmbedtls_asn1_get_mpi +#define mbedtls_asn1_get_tag pmbedtls_asn1_get_tag +#define mbedtls_asn1_write_len pmbedtls_asn1_write_len +#define mbedtls_asn1_write_mpi pmbedtls_asn1_write_mpi +#define mbedtls_asn1_write_tag pmbedtls_asn1_write_tag +#define mbedtls_asn1_write_raw_buffer pmbedtls_asn1_write_raw_buffer +#define mbedtls_dhm_init pmbedtls_dhm_init +#define mbedtls_dhm_make_params pmbedtls_dhm_make_params +#define mbedtls_dhm_calc_secret pmbedtls_dhm_calc_secret +#define mbedtls_dhm_free pmbedtls_dhm_free +#define mbedtls_dsa_init pmbedtls_dsa_init +#define mbedtls_dsa_set_group pmbedtls_dsa_set_group +#define mbedtls_dsa_genkey pmbedtls_dsa_genkey +#define mbedtls_dsa_check_pqg pmbedtls_dsa_check_pqg +#define mbedtls_dsa_check_pubkey pmbedtls_dsa_check_pubkey +#define mbedtls_dsa_check_privkey pmbedtls_dsa_check_privkey +#define mbedtls_dsa_pubkey_from_privkey pmbedtls_dsa_pubkey_from_privkey +#define mbedtls_dsa_verify pmbedtls_dsa_verify +#define mbedtls_dsa_sign pmbedtls_dsa_sign +#define mbedtls_dsa_free pmbedtls_dsa_free +#endif + +NTSTATUS process_detach(void *args) +{ +#ifndef __REACTOS__ + if (libmbedtls_handle) + { +#endif + mbedtls_entropy_free(&entropy); + mbedtls_ctr_drbg_free(&ctr_drbg); + DeleteCriticalSection(&rng_lock); + rng_seeded = FALSE; +#ifndef __REACTOS__ + wine_dlclose(libmbedtls_handle, NULL, 0); + libmbedtls_handle = NULL; + } +#endif + return STATUS_SUCCESS; +} + +static int bcrypt_rng(void *ctx, unsigned char *output, size_t len) +{ + int ret = 0; + + EnterCriticalSection(&rng_lock); + if (!rng_seeded) + { + ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); + if (!ret) + rng_seeded = TRUE; + else + ERR("cannot seed the random generator: %d\n", ret); + } + LeaveCriticalSection(&rng_lock); + + if (ret) + return ret; + + return mbedtls_ctr_drbg_random(&ctr_drbg, output, len); +} + +NTSTATUS hash_init(struct hash *hash) +{ + const mbedtls_md_info_t *md_info; + mbedtls_md_type_t md_type; + int ret; +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + mbedtls_md_init(&hash->hash_ctx); + switch (hash->alg_id) + { + case ALG_ID_MD2: + md_type = MBEDTLS_MD_MD2; + break; + + case ALG_ID_MD4: + md_type = MBEDTLS_MD_MD4; + break; + + case ALG_ID_MD5: + md_type = MBEDTLS_MD_MD5; + break; + + case ALG_ID_SHA1: + md_type = MBEDTLS_MD_SHA1; + break; + + case ALG_ID_SHA256: + md_type = MBEDTLS_MD_SHA256; + break; + + case ALG_ID_SHA384: + md_type = MBEDTLS_MD_SHA384; + break; + + case ALG_ID_SHA512: + md_type = MBEDTLS_MD_SHA512; + break; + + default: + ERR("unhandled id %u\n", hash->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + if ((md_info = mbedtls_md_info_from_type(md_type)) == NULL) + { + mbedtls_md_free(&hash->hash_ctx); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = mbedtls_md_setup(&hash->hash_ctx, md_info, hash->flags & HASH_FLAG_HMAC)) != 0) + { + mbedtls_md_free(&hash->hash_ctx); + return STATUS_INTERNAL_ERROR; + } + + if (hash->flags & HASH_FLAG_HMAC) + mbedtls_md_hmac_starts(&hash->hash_ctx, hash->secret, hash->secret_len); + else + mbedtls_md_starts(&hash->hash_ctx); + + return STATUS_SUCCESS; +} + +NTSTATUS hash_update(struct hash *hash, UCHAR *input, ULONG size) +{ +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + if (hash->flags & HASH_FLAG_HMAC) + mbedtls_md_hmac_update(&hash->hash_ctx, input, size); + else + mbedtls_md_update(&hash->hash_ctx, input, size); + + return STATUS_SUCCESS; +} + +NTSTATUS hash_finish(struct hash *hash, UCHAR *output) +{ +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + if (hash->flags & HASH_FLAG_HMAC) + mbedtls_md_hmac_finish(&hash->hash_ctx, output); + else + mbedtls_md_finish(&hash->hash_ctx, output); + + mbedtls_md_free(&hash->hash_ctx); + + return STATUS_SUCCESS; +} + +NTSTATUS hash_get_size(struct hash *hash, ULONG *output) +{ +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + if (!hash->hash_ctx.md_info) return STATUS_INTERNAL_ERROR; + if (!output) return STATUS_INTERNAL_ERROR; + *output = mbedtls_md_get_size(hash->hash_ctx.md_info); + + return STATUS_SUCCESS; +} + +mbedtls_cipher_type_t key_get_cipher(const struct key *key) +{ + switch (key->alg_id) + { + case ALG_ID_RC4: + WARN("handle block size\n"); + return MBEDTLS_CIPHER_ARC4_128; + + case ALG_ID_3DES: + WARN("handle block size\n"); + switch (key->u.s.mode) + { + case CHAIN_MODE_ECB: + return MBEDTLS_CIPHER_DES_EDE3_ECB; + case CHAIN_MODE_CBC: + return MBEDTLS_CIPHER_DES_EDE3_CBC; + default: + break; + } + FIXME("3DES mode %u with key length %u not supported\n", key->u.s.mode, key->u.s.secret_len); + return MBEDTLS_CIPHER_NONE; + + case ALG_ID_AES: + WARN("handle block size\n"); + switch (key->u.s.mode) + { + case CHAIN_MODE_GCM: + if (key->u.s.secret_len == 16) return MBEDTLS_CIPHER_AES_128_GCM; + if (key->u.s.secret_len == 24) return MBEDTLS_CIPHER_AES_192_GCM; + if (key->u.s.secret_len == 32) return MBEDTLS_CIPHER_AES_256_GCM; + break; + case CHAIN_MODE_ECB: /* can be emulated with CBC + empty IV */ + case CHAIN_MODE_CBC: + if (key->u.s.secret_len == 16) return MBEDTLS_CIPHER_AES_128_CBC; + if (key->u.s.secret_len == 24) return MBEDTLS_CIPHER_AES_192_CBC; + if (key->u.s.secret_len == 32) return MBEDTLS_CIPHER_AES_256_CBC; + break; + case CHAIN_MODE_CFB: + if (key->u.s.secret_len == 16) return MBEDTLS_CIPHER_AES_128_CFB8; + if (key->u.s.secret_len == 24) return MBEDTLS_CIPHER_AES_192_CFB8; + if (key->u.s.secret_len == 32) return MBEDTLS_CIPHER_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); + return MBEDTLS_CIPHER_NONE; + + default: + FIXME("algorithm %u not supported\n", key->alg_id); + return MBEDTLS_CIPHER_NONE; + } + return MBEDTLS_CIPHER_NONE; +} + +NTSTATUS key_symmetric_vector_reset(void *args) +{ + struct key *key = args; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + if (key_data(key)->s.cipher_ctx.cipher_info != NULL && key->u.s.vector != NULL && key->u.s.vector_len) + { + if (mbedtls_cipher_set_iv(&key_data(key)->s.cipher_ctx, key->u.s.vector, key->u.s.vector_len)) return STATUS_INTERNAL_ERROR; + return STATUS_SUCCESS; + } + TRACE("invalidating cipher handle\n"); + mbedtls_cipher_free(&key_data(key)->s.cipher_ctx); + key_data(key)->s.cipher_ctx.cipher_info = NULL; + return STATUS_SUCCESS; +} + +NTSTATUS init_cipher_handle(struct key *key, const mbedtls_operation_t operation) +{ + mbedtls_cipher_type_t cipher; + const mbedtls_cipher_info_t *info; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + if (key_data(key)->s.cipher_ctx.cipher_info != NULL) + { + if (mbedtls_cipher_get_operation(&key_data(key)->s.cipher_ctx) == operation) return STATUS_SUCCESS; + mbedtls_cipher_free(&key_data(key)->s.cipher_ctx); + key_data(key)->s.cipher_ctx.cipher_info = NULL; + } + cipher = key_get_cipher(key); + if (cipher == MBEDTLS_CIPHER_NONE) return STATUS_NOT_SUPPORTED; + + if ((info = mbedtls_cipher_info_from_type(cipher)) == NULL) return STATUS_INTERNAL_ERROR; + mbedtls_cipher_init(&key_data(key)->s.cipher_ctx); + if (mbedtls_cipher_setup(&key_data(key)->s.cipher_ctx, info)) + { + mbedtls_cipher_free(&key_data(key)->s.cipher_ctx); + return STATUS_INTERNAL_ERROR; + } + mbedtls_cipher_set_padding_mode(&key_data(key)->s.cipher_ctx, MBEDTLS_PADDING_NONE); + if (key->u.s.secret != NULL && key->u.s.secret_len > 0) mbedtls_cipher_setkey(&key_data(key)->s.cipher_ctx, key->u.s.secret, key->u.s.secret_len * 8, operation); + if (key->u.s.vector != NULL && key->u.s.vector_len > 0) mbedtls_cipher_set_iv(&key_data(key)->s.cipher_ctx, key->u.s.vector, key->u.s.vector_len); + + return STATUS_SUCCESS; +} + +NTSTATUS key_symmetric_set_auth_data(void *args) +{ + struct key_symmetric_set_auth_data_params *auth_params = args; + NTSTATUS status; + int ret; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + if ((status = init_cipher_handle(auth_params->key, auth_params->encrypt ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT))) return status; + + ret = mbedtls_cipher_update_ad(&key_data(auth_params->key)->s.cipher_ctx, auth_params->auth_data, auth_params->len); + if (ret) return STATUS_INTERNAL_ERROR; + return STATUS_SUCCESS; +} + +NTSTATUS key_symmetric_get_tag(void *args) +{ + struct key_symmetric_get_tag_params *tag_params = args; + mbedtls_operation_t operation; + int ret; + NTSTATUS status; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + operation = mbedtls_cipher_get_operation(&key_data(tag_params->key)->s.cipher_ctx); + if ((status = init_cipher_handle(tag_params->key, operation))) return status; + + switch (operation) { + case MBEDTLS_ENCRYPT: + ret = mbedtls_cipher_write_tag(&key_data(tag_params->key)->s.cipher_ctx, tag_params->tag, tag_params->len); + if (ret) return STATUS_INTERNAL_ERROR; + break; + case MBEDTLS_DECRYPT: + ret = mbedtls_cipher_check_tag(&key_data(tag_params->key)->s.cipher_ctx, tag_params->tag, tag_params->len); + if (ret) + { + if (ret == MBEDTLS_ERR_CIPHER_AUTH_FAILED) + return STATUS_AUTH_TAG_MISMATCH; + else + return STATUS_INTERNAL_ERROR; + } + break; + default: + FIXME("AES operation %u not supported\n", operation); + return STATUS_INTERNAL_ERROR; + break; + } + + return status; +} + +NTSTATUS key_symmetric_encrypt_internal(void *args) +{ + const struct key_symmetric_encrypt_params *params = args; + NTSTATUS status; + int ret; + size_t output_len = 0; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + if ((status = init_cipher_handle(params->key, MBEDTLS_ENCRYPT))) return status; + + ret = mbedtls_cipher_update(&key_data(params->key)->s.cipher_ctx, + params->input, params->input_len, + params->output, &output_len); + if (!output_len) + ret = mbedtls_cipher_finish(&key_data(params->key)->s.cipher_ctx, + params->output, &output_len); + if (ret) return STATUS_INTERNAL_ERROR; + + if (params->output_len != output_len) return STATUS_INTERNAL_ERROR; + + return STATUS_SUCCESS; +} + +NTSTATUS key_symmetric_decrypt_internal(void *args) +{ + const struct key_symmetric_decrypt_params *params = args; + NTSTATUS status; + int ret; + size_t output_len = 0; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + if ((status = init_cipher_handle(params->key, MBEDTLS_DECRYPT))) return status; + + ret = mbedtls_cipher_update(&key_data(params->key)->s.cipher_ctx, + params->input, params->input_len, + params->output, &output_len); + if (!output_len) + ret = mbedtls_cipher_finish(&key_data(params->key)->s.cipher_ctx, + params->output, &output_len); + if (ret) return STATUS_INTERNAL_ERROR; + + if (params->output_len != output_len) return STATUS_INTERNAL_ERROR; + + return STATUS_SUCCESS; +} + +NTSTATUS key_symmetric_destroy(void *args) +{ + struct key *key = args; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + if (key_data(key)->s.cipher_ctx.cipher_info != NULL) + { + mbedtls_cipher_free(&key_data(key)->s.cipher_ctx); + key_data(key)->s.cipher_ctx.cipher_info = NULL; + } + return STATUS_SUCCESS; +} + +static mbedtls_pk_type_t key_get_type(enum alg_id alg_id) +{ + mbedtls_pk_type_t pk_type; + + switch (alg_id) + { + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + pk_type = MBEDTLS_PK_RSA; + break; + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + pk_type = MBEDTLS_PK_ECKEY_DH; + break; + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + pk_type = MBEDTLS_PK_ECDSA; + break; + default: + FIXME("algorithm %u not supported\n", alg_id); + pk_type = MBEDTLS_PK_NONE; + break; + } + return pk_type; +} + +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; + mbedtls_ecp_group_id curve; + mbedtls_ecp_keypair *keypair; + DWORD magic, size; + UCHAR *dst; + + switch (key->alg_id) + { + 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_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; + + default: + FIXME("algorithm %u not supported\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + + keypair = mbedtls_pk_ec(key_data(key)->a.pk.pubkey); + if (!keypair) return STATUS_INVALID_PARAMETER; + + curve = keypair->grp.id; + + if (curve != MBEDTLS_ECP_DP_SECP256R1 && curve != MBEDTLS_ECP_DP_SECP384R1) + { + FIXME("curve %u not supported\n", curve); + return STATUS_NOT_IMPLEMENTED; + } + + *ret_len = sizeof(*ecc_blob) + size + size; + if (len >= *ret_len && buf) + { + ecc_blob->dwMagic = magic; + ecc_blob->cbKey = size; + + dst = buf + sizeof(*ecc_blob); + mbedtls_mpi_write_binary(&keypair->Q.X, dst, size); + dst += size; + mbedtls_mpi_write_binary(&keypair->Q.Y, dst, size); + } + + 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; + mbedtls_ecp_group_id curve; + mbedtls_ecp_keypair *keypair; + DWORD magic, size; + UCHAR *dst; + + switch (key->alg_id) + { + 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_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; + + default: + FIXME("algorithm %u does not yet support exporting ecc blob\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + + keypair = mbedtls_pk_ec(key_data(key)->a.pk.privkey); + if (!keypair) return STATUS_INVALID_PARAMETER; + + curve = keypair->grp.id; + + if (curve != MBEDTLS_ECP_DP_SECP256R1 && curve != MBEDTLS_ECP_DP_SECP384R1) + { + FIXME("curve %u not supported\n", curve); + return STATUS_NOT_IMPLEMENTED; + } + + + *ret_len = sizeof(*ecc_blob) + size + size + size; + if (len >= *ret_len && buf) + { + ecc_blob->dwMagic = magic; + ecc_blob->cbKey = size; + + dst = buf + sizeof(*ecc_blob); + mbedtls_mpi_write_binary(&keypair->Q.X, dst, size); + dst += size; + mbedtls_mpi_write_binary(&keypair->Q.Y, dst, size); + dst += size; + mbedtls_mpi_write_binary(&keypair->d, dst, size); + } + if (len < *ret_len && buf) return STATUS_BUFFER_TOO_SMALL; + + return STATUS_SUCCESS; +} + +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; + mbedtls_rsa_context *rsa_context; + mbedtls_mpi m, e; + UCHAR *dst; + int ret; + size_t m_size, e_size; + + rsa_context = mbedtls_pk_rsa(key_data(key)->a.pk.pubkey); + if (!rsa_context) return STATUS_INVALID_PARAMETER; + + mbedtls_mpi_init(&m); + mbedtls_mpi_init(&e); + + ret = mbedtls_rsa_export(rsa_context, &m, NULL, NULL, NULL, &e); + if (ret) + { + mbedtls_mpi_free(&e); + mbedtls_mpi_free(&m); + return STATUS_INTERNAL_ERROR; + } + + m_size = mbedtls_mpi_size(&m); + e_size = mbedtls_mpi_size(&e); + + *ret_len = sizeof(*rsa_blob) + e_size + m_size; + if (len >= *ret_len && buf) + { + dst = buf + sizeof(*rsa_blob); + mbedtls_mpi_write_binary(&e, dst, e_size); + rsa_blob->cbPublicExp = e_size; + + dst += rsa_blob->cbPublicExp; + mbedtls_mpi_write_binary(&m, dst, m_size); + rsa_blob->cbModulus = m_size; + + rsa_blob->Magic = BCRYPT_RSAPUBLIC_MAGIC; + rsa_blob->BitLength = key->u.a.bitlen; + rsa_blob->cbPrime1 = 0; + rsa_blob->cbPrime2 = 0; + } + + mbedtls_mpi_free(&e); + mbedtls_mpi_free(&m); + if (len < *ret_len && buf) return STATUS_BUFFER_TOO_SMALL; + + 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; + mbedtls_rsa_context *rsa_context; + mbedtls_mpi m, e, d, p, q, u, e1, e2; + BOOL full = (flags & KEY_EXPORT_FLAG_RSA_FULL); + UCHAR *dst; + int ret; + size_t m_size, e_size, d_size, p_size, q_size, u_size, e1_size, e2_size; + + rsa_context = mbedtls_pk_rsa(key_data(key)->a.pk.privkey); + if (!rsa_context) return STATUS_INVALID_PARAMETER; + + mbedtls_mpi_init(&m); + mbedtls_mpi_init(&e); + mbedtls_mpi_init(&d); + mbedtls_mpi_init(&p); + mbedtls_mpi_init(&q); + if (full) + { + mbedtls_mpi_init(&u); + mbedtls_mpi_init(&e1); + mbedtls_mpi_init(&e2); + } + ret = mbedtls_rsa_export(rsa_context, &m, &p, &q, &d, &e); + if (ret) + { + mbedtls_mpi_free(&m); + mbedtls_mpi_free(&e); + mbedtls_mpi_free(&d); + mbedtls_mpi_free(&p); + mbedtls_mpi_free(&q); + if (full) + { + mbedtls_mpi_free(&u); + mbedtls_mpi_free(&e1); + mbedtls_mpi_free(&e2); + } + return STATUS_INTERNAL_ERROR; + } + + if (full) + { + ret = mbedtls_rsa_export_crt(rsa_context, &e1, &e2, &u); + if (ret) + { + mbedtls_mpi_free(&m); + mbedtls_mpi_free(&e); + mbedtls_mpi_free(&d); + mbedtls_mpi_free(&p); + mbedtls_mpi_free(&q); + mbedtls_mpi_free(&u); + mbedtls_mpi_free(&e1); + mbedtls_mpi_free(&e2); + return STATUS_INTERNAL_ERROR; + } + } + + m_size = mbedtls_mpi_size(&m); + e_size = mbedtls_mpi_size(&e); + d_size = mbedtls_mpi_size(&d); + p_size = mbedtls_mpi_size(&p); + q_size = mbedtls_mpi_size(&q); + + if (full) + { + u_size = mbedtls_mpi_size(&u); + e1_size = mbedtls_mpi_size(&e1); + e2_size = mbedtls_mpi_size(&e2); + } + *ret_len = sizeof(*rsa_blob) + e_size + m_size + p_size + q_size; + + if (full) *ret_len += e1_size + e2_size + u_size + d_size; + + if (len >= *ret_len && buf) + { + rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf; + rsa_blob->Magic = full ? BCRYPT_RSAFULLPRIVATE_MAGIC : BCRYPT_RSAPRIVATE_MAGIC; + rsa_blob->BitLength = key->u.a.bitlen; + + dst = buf + sizeof(*rsa_blob); + mbedtls_mpi_write_binary(&e, dst, e_size); + rsa_blob->cbPublicExp = e_size; + + dst += rsa_blob->cbPublicExp; + mbedtls_mpi_write_binary(&m, dst, m_size); + rsa_blob->cbModulus = m_size; + + dst += rsa_blob->cbModulus; + mbedtls_mpi_write_binary(&p, dst, p_size); + rsa_blob->cbPrime1 = p_size; + + dst += rsa_blob->cbPrime1; + mbedtls_mpi_write_binary(&q, dst, q_size); + rsa_blob->cbPrime2 = q_size; + + if (full) + { + dst += rsa_blob->cbPrime2; + mbedtls_mpi_write_binary(&e1, dst, e1_size); + + dst += e1_size; + mbedtls_mpi_write_binary(&e2, dst, e2_size); + + dst += e2_size; + mbedtls_mpi_write_binary(&u, dst, u_size); + + dst += u_size; + mbedtls_mpi_write_binary(&d, dst, d_size); + } + } + + mbedtls_mpi_free(&m); + mbedtls_mpi_free(&e); + mbedtls_mpi_free(&d); + mbedtls_mpi_free(&p); + mbedtls_mpi_free(&q); + if (full) + { + mbedtls_mpi_free(&u); + mbedtls_mpi_free(&e1); + mbedtls_mpi_free(&e2); + } + if (len < *ret_len && buf) return STATUS_BUFFER_TOO_SMALL; + + return STATUS_SUCCESS; +} + +static NTSTATUS key_export_params(struct key *key, mbedtls_dhm_context *dh_key, UCHAR *buf, ULONG len, ULONG *ret_len) +{ + BCRYPT_DH_PARAMETER_HEADER *dh_params = (BCRYPT_DH_PARAMETER_HEADER *)buf; + size_t size = key->u.a.bitlen / 8; + UCHAR *dst; + + if (!dh_key) return STATUS_INVALID_PARAMETER; + + *ret_len = sizeof(*dh_params) + size + size; + + if (len >= *ret_len && buf) + { + dh_params->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC; + dh_params->cbLength = *ret_len; + dh_params->cbKeyLength = key->u.a.bitlen / 8; + + dst = buf + sizeof(*dh_params); + mbedtls_mpi_write_binary(&dh_key->P, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dh_key->G, dst, size); + } + if (len < *ret_len && buf) return STATUS_BUFFER_TOO_SMALL; + + return STATUS_SUCCESS; +} + +static NTSTATUS key_export_dh_public(struct key *key, mbedtls_dhm_context *dh_key, UCHAR *buf, ULONG len, ULONG *ret_len) +{ + BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf; + size_t size = key->u.a.bitlen / 8; + UCHAR *dst; + + if (!dh_key) return STATUS_INVALID_PARAMETER; + + *ret_len = sizeof(*dh_blob) + size + size + size; + + if (len >= *ret_len && buf) + { + dh_blob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC; + dh_blob->cbKey = key->u.a.bitlen / 8; + + dst = buf + sizeof(*dh_blob); + mbedtls_mpi_write_binary(&dh_key->P, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dh_key->G, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dh_key->GX, dst, size); + } + if (len < *ret_len && buf) return STATUS_BUFFER_TOO_SMALL; + + return STATUS_SUCCESS; +} + +static NTSTATUS key_export_dh(struct key *key, mbedtls_dhm_context *dh_key, UCHAR *buf, ULONG len, ULONG *ret_len) +{ + BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf; + size_t size = key->u.a.bitlen / 8; + UCHAR *dst; + + if (!dh_key) return STATUS_INVALID_PARAMETER; + *ret_len = sizeof(*dh_blob) + size + size + size + size; + + if (len >= *ret_len && buf) + { + dh_blob->dwMagic = BCRYPT_DH_PRIVATE_MAGIC; + dh_blob->cbKey = key->u.a.bitlen / 8; + + dst = buf + sizeof(*dh_blob); + mbedtls_mpi_write_binary(&dh_key->P, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dh_key->G, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dh_key->GX, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dh_key->X, dst, size); + } + if (len < *ret_len && buf) return STATUS_BUFFER_TOO_SMALL; + + return STATUS_SUCCESS; +} + +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ +static NTSTATUS key_export_dsa_public(struct key *key, mbedtls_dsa_context *dsa_key, UCHAR *buf, ULONG len, ULONG *ret_len) +{ + BCRYPT_DSA_KEY_BLOB *dsa_blob = (BCRYPT_DSA_KEY_BLOB *)buf; + size_t q_size; + 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_key) return STATUS_INVALID_PARAMETER; + + q_size = mbedtls_mpi_size(&dsa_key->Q); + + if (q_size > sizeof(dsa_blob->q)) return STATUS_INVALID_PARAMETER; + + *ret_len = sizeof(*dsa_blob) + size + size + size; + if (len >= *ret_len && buf) + { + dst = buf + sizeof(*dsa_blob);; + mbedtls_mpi_write_binary(&dsa_key->P, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dsa_key->G, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dsa_key->Y, dst, size); + + dst = dsa_blob->q; + mbedtls_mpi_write_binary(&dsa_key->Q, dst, sizeof(dsa_blob->q)); + + dsa_blob->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC; + dsa_blob->cbKey = size; + memset(dsa_blob->Count, 0, sizeof(dsa_blob->Count)); /* FIXME */ + memset(dsa_blob->Seed, 0, sizeof(dsa_blob->Seed)); /* FIXME */ + } + if (len < *ret_len && buf) return STATUS_BUFFER_TOO_SMALL; + + 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, mbedtls_dsa_context *dsa_key, UCHAR *buf, ULONG len, ULONG *ret_len) +{ + BLOBHEADER *hdr = (BLOBHEADER *)buf; + DSSPUBKEY *dsskey; + size_t q_size; + 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_key) return STATUS_INVALID_PARAMETER; + + q_size = mbedtls_mpi_size(&dsa_key->Q); + if (q_size > Q_SIZE) return STATUS_INVALID_PARAMETER; + + *ret_len = sizeof(*hdr) + sizeof(*dsskey) + sizeof(key->u.a.dss_seed) + + size + Q_SIZE + size + size; + if (len >= *ret_len && buf) + { + 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); + mbedtls_mpi_write_binary(&dsa_key->P, dst, size); + reverse_bytes(dst, size); + dst += size; + + mbedtls_mpi_write_binary(&dsa_key->Q, dst, Q_SIZE); + reverse_bytes(dst, Q_SIZE); + dst += Q_SIZE; + + mbedtls_mpi_write_binary(&dsa_key->G, dst, size); + reverse_bytes(dst, size); + dst += size; + + mbedtls_mpi_write_binary(&dsa_key->Y, dst, size); + reverse_bytes(dst, size); + dst += size; + + memcpy(dst, &key->u.a.dss_seed, sizeof(key->u.a.dss_seed)); + } + if (len < *ret_len && buf) return STATUS_BUFFER_TOO_SMALL; + + return STATUS_SUCCESS; +} + +static NTSTATUS key_export_dsa(struct key *key, mbedtls_dsa_context *dsa_key, UCHAR *buf, ULONG len, ULONG *ret_len) +{ + BCRYPT_DSA_KEY_BLOB *dsa_blob = (BCRYPT_DSA_KEY_BLOB *)buf; + size_t q_size; + 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_key) return STATUS_INVALID_PARAMETER; + + q_size = mbedtls_mpi_size(&dsa_key->Q); + if (q_size > sizeof(dsa_blob->q)) return STATUS_INVALID_PARAMETER; + + *ret_len = sizeof(*dsa_blob) + size + size + size + size; + if (len >= *ret_len && buf) + { + dst = buf + sizeof(*dsa_blob);; + mbedtls_mpi_write_binary(&dsa_key->P, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dsa_key->G, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dsa_key->Y, dst, size); + dst += size; + mbedtls_mpi_write_binary(&dsa_key->X, dst, size); + + dst = dsa_blob->q; + mbedtls_mpi_write_binary(&dsa_key->Q, dst, sizeof(dsa_blob->q)); + + dsa_blob->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC; + dsa_blob->cbKey = size; + memset(dsa_blob->Count, 0, sizeof(dsa_blob->Count)); /* FIXME */ + memset(dsa_blob->Seed, 0, sizeof(dsa_blob->Seed)); /* FIXME */ + } + if (len < *ret_len && buf) return STATUS_BUFFER_TOO_SMALL; + + return STATUS_SUCCESS; +} + +static NTSTATUS key_export_dsa_capi(struct key *key, mbedtls_dsa_context *dsa_key, UCHAR *buf, ULONG len, ULONG *ret_len) +{ + BLOBHEADER *hdr; + DSSPUBKEY *pubkey; + size_t q_size, x_size; + ULONG size = key->u.a.bitlen / 8; + UCHAR *dst; + + if (!dsa_key) return STATUS_INVALID_PARAMETER; + + q_size = mbedtls_mpi_size(&dsa_key->Q); + x_size = mbedtls_mpi_size(&dsa_key->X); + + if (q_size > 21 || x_size > 21) + { + ERR("can't export key in this format\n"); + return STATUS_NOT_SUPPORTED; + } + + *ret_len = sizeof(*hdr) + sizeof(*pubkey) + sizeof(key->u.a.dss_seed) + + size + Q_SIZE + size + Q_SIZE; + if (len >= *ret_len && buf) + { + hdr = (BLOBHEADER *)buf; + hdr->bType = PRIVATEKEYBLOB; + hdr->bVersion = 2; + hdr->reserved = 0; + hdr->aiKeyAlg = CALG_DSS_SIGN; + + pubkey = (DSSPUBKEY *)(hdr + 1); + pubkey->magic = MAGIC_DSS2; + pubkey->bitlen = key->u.a.bitlen; + + dst = (UCHAR *)(pubkey + 1); + mbedtls_mpi_write_binary(&dsa_key->P, dst, size); + reverse_bytes(dst, size); + dst += size; + + mbedtls_mpi_write_binary(&dsa_key->Q, dst, Q_SIZE); + reverse_bytes(dst, Q_SIZE); + dst += Q_SIZE; + + mbedtls_mpi_write_binary(&dsa_key->G, dst, size); + reverse_bytes(dst, size); + dst += size; + + mbedtls_mpi_write_binary(&dsa_key->X, dst, Q_SIZE); + reverse_bytes(dst, Q_SIZE); + dst += Q_SIZE; + + memcpy(dst, &key->u.a.dss_seed, sizeof(key->u.a.dss_seed)); + } + if (len < *ret_len && buf) return STATUS_BUFFER_TOO_SMALL; + + return STATUS_SUCCESS; +} +#endif + +NTSTATUS key_asymmetric_export(void *args) +{ + const struct key_asymmetric_export_params *params = args; + struct key *key = params->key; + unsigned flags = params->flags; + + if (!(key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE; + + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + 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); + +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + case ALG_ID_DSA: + if (flags & KEY_EXPORT_FLAG_PUBLIC) + { + if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2) + return key_export_dsa_capi_public(key, &key_data(key)->a.dsa.pubkey, params->buf, params->len, params->ret_len); + return key_export_dsa_public(key, &key_data(key)->a.dsa.pubkey, params->buf, params->len, params->ret_len); + } + /* if private key is not set return invalid parameter */ + if (mbedtls_mpi_cmp_int(&key_data(key)->a.dsa.privkey.X, 0) == 0) return STATUS_INVALID_PARAMETER; + if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2) + return key_export_dsa_capi(key, &key_data(key)->a.dsa.privkey, params->buf, params->len, params->ret_len); + return key_export_dsa(key, &key_data(key)->a.dsa.privkey, params->buf, params->len, params->ret_len); +#endif + + case ALG_ID_DH: + if (flags & KEY_EXPORT_FLAG_DH_PARAMETERS) + return key_export_params(key, &key_data(key)->a.dh.pubkey, params->buf, params->len, params->ret_len); + if (flags & KEY_EXPORT_FLAG_PUBLIC) + return key_export_dh_public(key, &key_data(key)->a.dh.pubkey, params->buf, params->len, params->ret_len); + return key_export_dh(key, &key_data(key)->a.dh.privkey, params->buf, params->len, params->ret_len); + default: + FIXME("algorithm %u not yet supported\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } +} + +static NTSTATUS key_import_ecc_public(struct key *key, UCHAR *buf, ULONG len) +{ + BCRYPT_ECCKEY_BLOB *ecc_blob; + mbedtls_ecp_group_id curve; + const mbedtls_ecp_keypair *keypair; + int ret; + size_t plen; + + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDSA_P256: + curve = MBEDTLS_ECP_DP_SECP256R1; + break; + + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P384: + curve = MBEDTLS_ECP_DP_SECP384R1; + break; + + default: + FIXME("algorithm %u not yet supported\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + + keypair = mbedtls_pk_ec(key_data(key)->a.pk.pubkey); + if (!keypair) return STATUS_INVALID_PARAMETER; + + ret = mbedtls_ecp_group_load((struct mbedtls_ecp_group *)&keypair->grp, curve); + if (ret) return STATUS_INTERNAL_ERROR; + + ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf; + plen = mbedtls_mpi_size(&keypair->grp.P); + if (plen != ecc_blob->cbKey) return STATUS_INTERNAL_ERROR; + + mbedtls_mpi_read_binary((struct mbedtls_mpi *)&keypair->Q.X, buf + sizeof(*ecc_blob), ecc_blob->cbKey); + mbedtls_mpi_read_binary((struct mbedtls_mpi *)&keypair->Q.Y, buf + sizeof(*ecc_blob) + ecc_blob->cbKey, ecc_blob->cbKey); + mbedtls_mpi_lset((struct mbedtls_mpi *)&keypair->Q.Z, 1); + ret = mbedtls_ecp_check_pubkey(&keypair->grp, &keypair->Q); + + if (ret) return STATUS_INTERNAL_ERROR; + + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_ecc(struct key *key, UCHAR *buf, ULONG len) +{ + BCRYPT_ECCKEY_BLOB *ecc_blob; + mbedtls_ecp_group_id curve; + const mbedtls_ecp_keypair *keypair; + int ret; + size_t plen; + + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDSA_P256: + curve = MBEDTLS_ECP_DP_SECP256R1; + break; + + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P384: + curve = MBEDTLS_ECP_DP_SECP384R1; + break; + + default: + FIXME("algorithm %u not yet supported\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + + keypair = mbedtls_pk_ec(key_data(key)->a.pk.privkey); + if (!keypair) return STATUS_INVALID_PARAMETER; + + ret = mbedtls_ecp_group_load((struct mbedtls_ecp_group *)&keypair->grp, curve); + + if (ret) return STATUS_INTERNAL_ERROR; + + ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf; + plen = mbedtls_mpi_size(&keypair->grp.P); + if (plen != ecc_blob->cbKey) return STATUS_INTERNAL_ERROR; + + mbedtls_mpi_read_binary((struct mbedtls_mpi *)&keypair->Q.X, buf + sizeof(*ecc_blob), ecc_blob->cbKey); + mbedtls_mpi_read_binary((struct mbedtls_mpi *)&keypair->Q.Y, buf + sizeof(*ecc_blob) + ecc_blob->cbKey, ecc_blob->cbKey); + mbedtls_mpi_lset((struct mbedtls_mpi *)&keypair->Q.Z, 1); + mbedtls_mpi_read_binary((struct mbedtls_mpi *)&keypair->d, buf + sizeof(*ecc_blob) + 2 * ecc_blob->cbKey, ecc_blob->cbKey); + ret = mbedtls_ecp_check_pubkey(&keypair->grp, &keypair->Q); + + if (ret) return STATUS_INTERNAL_ERROR; + + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_rsa_public(struct key *key, UCHAR *buf, ULONG len) +{ + BCRYPT_RSAKEY_BLOB *rsa_blob; + mbedtls_rsa_context *rsa_context; + int ret; + + rsa_context = mbedtls_pk_rsa(key_data(key)->a.pk.pubkey); + if (!rsa_context) return STATUS_INVALID_PARAMETER; + + rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf; + ret = mbedtls_rsa_import_raw(rsa_context, buf + sizeof(*rsa_blob) + rsa_blob->cbPublicExp, rsa_blob->cbModulus, NULL, 0, NULL, 0, NULL, 0, + buf + sizeof(*rsa_blob), rsa_blob->cbPublicExp); + if (ret) return STATUS_INTERNAL_ERROR; + ret = mbedtls_rsa_complete(rsa_context); + if (ret) return STATUS_INTERNAL_ERROR; + ret = mbedtls_rsa_check_pubkey(rsa_context); + if (ret) return STATUS_INTERNAL_ERROR; + + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_rsa(struct key *key, UCHAR *buf, ULONG len) +{ + BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf; + mbedtls_rsa_context *rsa_context; + int ret; + + rsa_context = mbedtls_pk_rsa(key_data(key)->a.pk.privkey); + if (!rsa_context) return STATUS_INVALID_PARAMETER; + + ret = mbedtls_rsa_import_raw(rsa_context, buf + sizeof(*rsa_blob) + rsa_blob->cbPublicExp, rsa_blob->cbModulus, + buf + sizeof(*rsa_blob) + rsa_blob->cbPublicExp + rsa_blob->cbModulus, rsa_blob->cbPrime1, + buf + sizeof(*rsa_blob) + rsa_blob->cbPublicExp + rsa_blob->cbModulus + rsa_blob->cbPrime1, rsa_blob->cbPrime2, + NULL, 0, buf + sizeof(*rsa_blob), rsa_blob->cbPublicExp); + if (ret) return STATUS_INTERNAL_ERROR; + ret = mbedtls_rsa_complete(rsa_context); + if (ret) return STATUS_INTERNAL_ERROR; + ret = mbedtls_rsa_check_privkey(rsa_context); + if (ret) return STATUS_INTERNAL_ERROR; + ret = mbedtls_rsa_check_pubkey(rsa_context); + if (ret) return STATUS_INTERNAL_ERROR; + ret = mbedtls_rsa_check_pub_priv(rsa_context, rsa_context); + if (ret) return STATUS_INTERNAL_ERROR; + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dh_params(struct key *key, mbedtls_dhm_context *dh_key, UCHAR *buf, ULONG len) +{ + BCRYPT_DH_PARAMETER_HEADER *dh_params = (BCRYPT_DH_PARAMETER_HEADER *)buf; + UCHAR *p; + size_t key_len = dh_params->cbKeyLength; + + if (!dh_key) return STATUS_INVALID_PARAMETER; + + if (dh_params->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC && dh_params->dwMagic != BCRYPT_DH_PUBLIC_MAGIC && dh_params->dwMagic != BCRYPT_DH_PRIVATE_MAGIC) + return STATUS_INVALID_PARAMETER; + if (len < 2 * key_len) return STATUS_INVALID_PARAMETER; + + p = (UCHAR *)buf + sizeof(*dh_params); + + mbedtls_mpi_read_binary(&dh_key->P, p, key_len); + p += key_len; + mbedtls_mpi_read_binary(&dh_key->G, p, key_len); + + key->u.a.bitlen = dh_params->cbKeyLength * 8; + dh_key->len = key_len; + + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dh_public(struct key *key, mbedtls_dhm_context *dh_key, UCHAR *buf, ULONG len) +{ + BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf; + UCHAR *p; + size_t key_len = dh_blob->cbKey; + + if (!dh_key) return STATUS_INVALID_PARAMETER; + + if (dh_blob->dwMagic != BCRYPT_DH_PUBLIC_MAGIC && dh_blob->dwMagic != BCRYPT_DH_PRIVATE_MAGIC) + return STATUS_INVALID_PARAMETER; + if (len < 3 * key_len) return STATUS_INVALID_PARAMETER; + + p = (UCHAR *)buf + sizeof(*dh_blob); + mbedtls_mpi_read_binary(&dh_key->P, p, key_len); + p += key_len; + mbedtls_mpi_read_binary(&dh_key->G, p, key_len); + p += key_len; + mbedtls_mpi_read_binary(&dh_key->GX, p, key_len); + + key->u.a.bitlen = dh_blob->cbKey * 8; + dh_key->len = key_len; + + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dh(struct key *key, mbedtls_dhm_context *dh_key, UCHAR *buf, ULONG len) +{ + BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf; + UCHAR *p; + size_t key_len = dh_blob->cbKey; + + if (!dh_key) return STATUS_INVALID_PARAMETER; + + if (dh_blob->dwMagic != BCRYPT_DH_PRIVATE_MAGIC) return STATUS_INVALID_PARAMETER; + if (len < 4 * key_len) return STATUS_INVALID_PARAMETER; + + p = (UCHAR *)buf + sizeof(*dh_blob); + mbedtls_mpi_read_binary(&dh_key->P, p, key_len); + p += key_len; + mbedtls_mpi_read_binary(&dh_key->G, p, key_len); + p += key_len; + mbedtls_mpi_read_binary(&dh_key->GX, p, key_len); + p += key_len; + mbedtls_mpi_read_binary(&dh_key->X, p, key_len); + + key->u.a.bitlen = dh_blob->cbKey * 8; + dh_key->len = key_len; + + return STATUS_SUCCESS; +} + +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ +static NTSTATUS key_import_dsa_public(struct key *key, mbedtls_dsa_context *dsa_key, UCHAR *buf, ULONG len) +{ + BCRYPT_DSA_KEY_BLOB *dsa_blob; + mbedtls_mpi P, Q, G, Y; + UCHAR *p_data, *q_data, *g_data, *y_data; + int ret; + size_t p_size, q_size, g_size, y_size; + + if (!dsa_key) return STATUS_INVALID_PARAMETER; + + dsa_blob = (BCRYPT_DSA_KEY_BLOB *)buf; + p_data = buf + sizeof(*dsa_blob); + p_size = dsa_blob->cbKey; + q_data = dsa_blob->q; + q_size = sizeof(dsa_blob->q); + g_data = buf + sizeof(*dsa_blob) + dsa_blob->cbKey; + g_size = dsa_blob->cbKey; + y_data = buf + sizeof(*dsa_blob) + dsa_blob->cbKey * 2; + y_size = dsa_blob->cbKey; + + mbedtls_mpi_init(&P); + mbedtls_mpi_init(&Q); + mbedtls_mpi_init(&G); + mbedtls_mpi_init(&Y); + + mbedtls_mpi_read_binary(&P, p_data, p_size); + mbedtls_mpi_read_binary(&Q, q_data, q_size); + mbedtls_mpi_read_binary(&G, g_data, g_size); + mbedtls_mpi_read_binary(&Y, y_data, y_size); + + ret = mbedtls_dsa_check_pqg(&P, &Q, &G); + ret = mbedtls_dsa_set_group(dsa_key, &P, &Q, &G); + if (ret) + { + mbedtls_mpi_free(&P); + mbedtls_mpi_free(&Q); + mbedtls_mpi_free(&G); + mbedtls_mpi_free(&Y); + return STATUS_INTERNAL_ERROR; + } + + mbedtls_mpi_copy(&dsa_key->Y, &Y); + + ret = mbedtls_dsa_check_pubkey(dsa_key); + + mbedtls_mpi_free(&P); + mbedtls_mpi_free(&Q); + mbedtls_mpi_free(&G); + mbedtls_mpi_free(&Y); + + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dsa(struct key *key, mbedtls_dsa_context *dsa_key, UCHAR *buf, ULONG len) +{ + BCRYPT_DSA_KEY_BLOB *dsa_blob; + mbedtls_mpi P, Q, G, Y, X; + UCHAR *p_data, *q_data, *g_data, *y_data, *x_data; + int ret, p_size, q_size, g_size, y_size, x_size; + + if (!dsa_key) return STATUS_INVALID_PARAMETER; + + dsa_blob = (BCRYPT_DSA_KEY_BLOB *)buf; + p_data = buf + sizeof(*dsa_blob); + p_size = dsa_blob->cbKey; + q_data = dsa_blob->q; + q_size = sizeof(dsa_blob->q); + g_data = buf + sizeof(*dsa_blob) + dsa_blob->cbKey; + g_size = dsa_blob->cbKey; + y_data = buf + sizeof(*dsa_blob) + dsa_blob->cbKey * 2; + y_size = dsa_blob->cbKey; + x_data = buf + sizeof(*dsa_blob) + dsa_blob->cbKey * 3; + x_size = dsa_blob->cbKey; + + mbedtls_mpi_init(&P); + mbedtls_mpi_init(&Q); + mbedtls_mpi_init(&G); + mbedtls_mpi_init(&Y); + mbedtls_mpi_init(&X); + + mbedtls_mpi_read_binary(&P, p_data, p_size); + mbedtls_mpi_read_binary(&Q, q_data, q_size); + mbedtls_mpi_read_binary(&G, g_data, g_size); + mbedtls_mpi_read_binary(&Y, y_data, y_size); + mbedtls_mpi_read_binary(&X, x_data, x_size); + + ret = mbedtls_dsa_check_pqg(&P, &Q, &G); + ret = mbedtls_dsa_set_group(dsa_key, &P, &Q, &G); + if (ret) + { + mbedtls_mpi_free(&P); + mbedtls_mpi_free(&Q); + mbedtls_mpi_free(&G); + mbedtls_mpi_free(&Y); + return STATUS_INTERNAL_ERROR; + } + + mbedtls_mpi_copy(&dsa_key->Y, &Y); + mbedtls_mpi_copy(&dsa_key->X, &X); + + ret = mbedtls_dsa_check_privkey(dsa_key); + + mbedtls_mpi_free(&P); + mbedtls_mpi_free(&Q); + mbedtls_mpi_free(&G); + mbedtls_mpi_free(&Y); + mbedtls_mpi_free(&X); + + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dsa_capi_public(struct key *key, mbedtls_dsa_context *dsa_key, UCHAR *buf, ULONG len) +{ + BLOBHEADER *hdr = (BLOBHEADER *)buf; + DSSPUBKEY *pubkey; + mbedtls_mpi P, Q, G, Y; + unsigned char *data, p_data[128], q_data[20], g_data[128], y_data[128]; + int i, ret, size, p_size, q_size, g_size, y_size; + + if (!dsa_key) return STATUS_INVALID_PARAMETER; + + pubkey = (DSSPUBKEY *)(buf+sizeof(*hdr)); + size = pubkey->bitlen / 8; + if (size > sizeof(p_data)) + { + FIXME("size %u not supported\n", size); + return STATUS_NOT_SUPPORTED; + } + data = buf + sizeof(*hdr) + sizeof(*pubkey); + + p_size = size; + for (i = 0; i < p_size; i++) p_data[i] = data[p_size - i - 1]; + data += p_size; + + q_size = sizeof(q_data); + for (i = 0; i < q_size; i++) q_data[i] = data[q_size - i - 1]; + data += q_size; + + g_size = size; + for (i = 0; i < g_size; i++) g_data[i] = data[g_size - i - 1]; + data += g_size; + + y_size = sizeof(y_data); + for (i = 0; i < y_size; i++) y_data[i] = data[y_size - i - 1]; + data += y_size; + + mbedtls_mpi_init(&P); + mbedtls_mpi_init(&Q); + mbedtls_mpi_init(&G); + mbedtls_mpi_init(&Y); + + mbedtls_mpi_read_binary(&P, p_data, p_size); + mbedtls_mpi_read_binary(&Q, q_data, q_size); + mbedtls_mpi_read_binary(&G, g_data, g_size); + mbedtls_mpi_read_binary(&Y, y_data, y_size); + + + ret = mbedtls_dsa_check_pqg(&P, &Q, &G); + ret = mbedtls_dsa_set_group(dsa_key, &P, &Q, &G); + if (ret) + { + mbedtls_mpi_free(&P); + mbedtls_mpi_free(&Q); + mbedtls_mpi_free(&G); + mbedtls_mpi_free(&Y); + return STATUS_INTERNAL_ERROR; + } + + mbedtls_mpi_copy(&dsa_key->Y, &Y); + + ret = mbedtls_dsa_check_pubkey(dsa_key); + + memcpy(&key->u.a.dss_seed, data, sizeof(key->u.a.dss_seed)); + + mbedtls_mpi_free(&P); + mbedtls_mpi_free(&Q); + mbedtls_mpi_free(&G); + mbedtls_mpi_free(&Y); + + return STATUS_SUCCESS; +} + +static NTSTATUS key_import_dsa_capi(struct key *key, mbedtls_dsa_context *dsa_key, UCHAR *buf, ULONG len) +{ + BLOBHEADER *hdr = (BLOBHEADER *)buf; + DSSPUBKEY *pubkey; + mbedtls_mpi P, Q, G, X; + unsigned char *data, p_data[128], q_data[20], g_data[128], x_data[20]; + int i, ret, size, p_size, q_size, g_size, x_size; + + if (!dsa_key) return STATUS_INVALID_PARAMETER; + + pubkey = (DSSPUBKEY *)(buf+sizeof(*hdr)); + if ((size = pubkey->bitlen / 8) > sizeof(p_data)) + { + FIXME("size %u not supported\n", size); + return STATUS_NOT_SUPPORTED; + } + data = buf + sizeof(*hdr) + sizeof(*pubkey); + + p_size = size; + for (i = 0; i < p_size; i++) p_data[i] = data[p_size - i - 1]; + data += p_size; + + q_size = sizeof(q_data); + for (i = 0; i < q_size; i++) q_data[i] = data[q_size - i - 1]; + data += q_size; + + g_size = size; + for (i = 0; i < g_size; i++) g_data[i] = data[g_size - i - 1]; + data += g_size; + + x_size = sizeof(x_data); + for (i = 0; i < x_size; i++) x_data[i] = data[x_size - i - 1]; + data += x_size; + + mbedtls_mpi_init(&P); + mbedtls_mpi_init(&Q); + mbedtls_mpi_init(&G); + mbedtls_mpi_init(&X); + + mbedtls_mpi_read_binary(&P, p_data, p_size); + mbedtls_mpi_read_binary(&Q, q_data, q_size); + mbedtls_mpi_read_binary(&G, g_data, g_size); + mbedtls_mpi_read_binary(&X, x_data, x_size); + + ret = mbedtls_dsa_check_pqg(&P, &Q, &G); + ret = mbedtls_dsa_set_group(dsa_key, &P, &Q, &G); + if (ret) + { + mbedtls_mpi_free(&P); + mbedtls_mpi_free(&Q); + mbedtls_mpi_free(&G); + mbedtls_mpi_free(&X); + return STATUS_INTERNAL_ERROR; + } + + mbedtls_mpi_copy(&dsa_key->X, &X); + + ret = mbedtls_dsa_pubkey_from_privkey(dsa_key); + ret = mbedtls_dsa_check_privkey(dsa_key); + + memcpy(&key->u.a.dss_seed, data, sizeof(key->u.a.dss_seed)); + + mbedtls_mpi_free(&P); + mbedtls_mpi_free(&Q); + mbedtls_mpi_free(&G); + mbedtls_mpi_free(&X); + + return STATUS_SUCCESS; +} +#endif + +static NTSTATUS init_pk_key(mbedtls_pk_context *key, mbedtls_pk_type_t pk_type, BOOL reinit) +{ + const mbedtls_pk_info_t *info; + int ret; + + if (pk_type == MBEDTLS_PK_NONE) return STATUS_NOT_SUPPORTED; + info = mbedtls_pk_info_from_type(pk_type); + if (info == NULL) return STATUS_NOT_SUPPORTED; + if (reinit) mbedtls_pk_free(key); + mbedtls_pk_init(key); + ret = mbedtls_pk_setup(key, info); + if (ret) return STATUS_INVALID_PARAMETER; + return STATUS_SUCCESS; +} + +static void init_dh_key(mbedtls_dhm_context *key, BOOL reinit) +{ + if (reinit) mbedtls_dhm_free(key); + mbedtls_dhm_init(key); +} + +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ +static void init_dsa_key(mbedtls_dsa_context *key, int len, BOOL reinit) +{ + if (reinit) mbedtls_dsa_free(key); + mbedtls_dsa_init(key); + key->len = len; +} +#endif + +NTSTATUS key_asymmetric_import(void *args) +{ + const struct key_asymmetric_import_params *params = args; + struct key *key = params->key; + NTSTATUS status = STATUS_SUCCESS; + int ret; + mbedtls_pk_type_t pk_type; + UCHAR *buf; + size_t olen; + BOOL bPrivate = (params->flags & KEY_IMPORT_FLAG_PUBLIC) == 0; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + + pk_type = key_get_type(key->alg_id); + if (bPrivate) + { + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + status = init_pk_key(&key_data(key)->a.pk.privkey, pk_type, TRUE); + status = key_import_ecc(key, params->buf, params->len); + break; + + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + status = init_pk_key(&key_data(key)->a.pk.privkey, pk_type, TRUE); + status = key_import_rsa(key, params->buf, params->len); + break; + +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + case ALG_ID_DSA: + init_dsa_key(&key_data(key)->a.dsa.privkey, (key->u.a.bitlen + 7) / 8, TRUE); + if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2) + ret = key_import_dsa_capi(key, &key_data(key)->a.dsa.privkey, params->buf, params->len); + else + ret = key_import_dsa(key, &key_data(key)->a.dsa.privkey, params->buf, params->len); + if (ret) status = STATUS_INTERNAL_ERROR; + break; +#endif + case ALG_ID_DH: + init_dh_key(&key_data(key)->a.dh.privkey, TRUE); + if (params->flags & KEY_IMPORT_FLAG_DH_PARAMETERS) + status = key_import_dh_params(key, &key_data(key)->a.dh.privkey, params->buf, params->len); + else + status = key_import_dh(key, &key_data(key)->a.dh.privkey, params->buf, params->len); + break; + + default: + FIXME("algorithm %u not yet supported\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + + if (status) return status; + } + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + status = init_pk_key(&key_data(key)->a.pk.pubkey, pk_type, TRUE); + status = key_import_ecc_public(key, params->buf, params->len); + break; + + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + status = init_pk_key(&key_data(key)->a.pk.pubkey, pk_type, TRUE); + status = key_import_rsa_public(key, params->buf, params->len); + if (status) break; + if (bPrivate) + { + ret = mbedtls_rsa_check_pub_priv(mbedtls_pk_rsa(key_data(key)->a.pk.pubkey), mbedtls_pk_rsa(key_data(key)->a.pk.privkey)); + if (ret) status = STATUS_INTERNAL_ERROR; + } + break; + +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + case ALG_ID_DSA: + init_dsa_key(&key_data(key)->a.dsa.pubkey, (key->u.a.bitlen + 7) / 8, TRUE); + if (key->u.a.flags & KEY_FLAG_LEGACY_DSA_V2) + { + if (bPrivate) + { + ret = key_import_dsa_capi(key, &key_data(key)->a.dsa.pubkey, params->buf, params->len); + mbedtls_mpi_lset(&key_data(key)->a.dsa.pubkey.X, 0); + } + else + ret = key_import_dsa_capi_public(key, &key_data(key)->a.dsa.pubkey, params->buf, params->len); + } + else + ret = key_import_dsa_public(key, &key_data(key)->a.dsa.pubkey, params->buf, params->len); + if (ret) status = STATUS_INTERNAL_ERROR; + break; +#endif + case ALG_ID_DH: + init_dh_key(&key_data(key)->a.dh.pubkey, TRUE); + if (params->flags & KEY_IMPORT_FLAG_DH_PARAMETERS) + { + status = key_import_dh_params(key, &key_data(key)->a.dh.pubkey, params->buf, params->len); + if (status) break; + buf = malloc(1024); + if (!buf) return STATUS_NO_MEMORY; + ret = mbedtls_dhm_make_params(&key_data(key)->a.dh.pubkey, (key->u.a.bitlen + 7) / 8, buf, &olen, bcrypt_rng, NULL); + free(buf); + if (ret) status = STATUS_INTERNAL_ERROR; + } + else + status = key_import_dh_public(key, &key_data(key)->a.dh.pubkey, params->buf, params->len); + break; + + default: + FIXME("algorithm %u not yet supported\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + + return status; +} + +#define MBEDTLS_DH_GEN_PRIME +#ifdef MBEDTLS_DH_GEN_PRIME +static int dh_gen_prime(mbedtls_dhm_context *dh, int nbits, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret; + mbedtls_mpi Q, P1; + mbedtls_mpi_init(&Q); + mbedtls_mpi_init(&P1); + ret = mbedtls_mpi_read_string(&dh->G, 10, "4"); + ret = mbedtls_mpi_gen_prime(&dh->P, nbits, MBEDTLS_MPI_GEN_PRIME_FLAG_DH, f_rng, p_rng); + // Verifying that Q = (P-1)/2 is prime + ret = mbedtls_mpi_sub_int(&P1, &dh->P, 1); + ret = mbedtls_mpi_div_int(&Q, NULL, &P1, 2); + ret = mbedtls_mpi_is_prime_ext(&Q, 50, f_rng, p_rng); + mbedtls_mpi_free(&Q); + mbedtls_mpi_free(&P1); + return ret; +} +#endif /* MBEDTLS_DH_GEN_PRIME */ + +NTSTATUS key_asymmetric_generate(void *args) +{ + struct key *key = args; + mbedtls_pk_type_t pk_type; + mbedtls_ecp_group_id grp_id; + int ret; + NTSTATUS status; + UCHAR *buf; + ULONG len; + size_t olen; + BCRYPT_DSA_KEY_BLOB *dsa_blob; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) + return STATUS_INTERNAL_ERROR; +#endif + + switch (key->alg_id) + { + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + pk_type = key_get_type(key->alg_id); + status = init_pk_key(&key_data(key)->a.pk.privkey, pk_type, FALSE); + ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(key_data(key)->a.pk.privkey), bcrypt_rng, NULL, key->u.a.bitlen, 65537); + ret = mbedtls_rsa_complete(mbedtls_pk_rsa(key_data(key)->a.pk.privkey)); + ret = mbedtls_rsa_check_privkey(mbedtls_pk_rsa(key_data(key)->a.pk.privkey)); + ret = mbedtls_rsa_check_pubkey(mbedtls_pk_rsa(key_data(key)->a.pk.privkey)); + if (ret) mbedtls_pk_free(&key_data(key)->a.pk.privkey); + break; + case ALG_ID_ECDH_P256: + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P384: + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDSA_P256: + grp_id = MBEDTLS_ECP_DP_SECP256R1; + break; + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P384: + grp_id = MBEDTLS_ECP_DP_SECP384R1; + break; + default: + grp_id = MBEDTLS_ECP_DP_NONE; + } + pk_type = key_get_type(key->alg_id); + status = init_pk_key(&key_data(key)->a.pk.privkey, pk_type, FALSE); + ret = mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(key_data(key)->a.pk.privkey), bcrypt_rng, NULL); + if (ret) mbedtls_pk_free(&key_data(key)->a.pk.privkey); + break; + case ALG_ID_DH: + init_dh_key(&key_data(key)->a.dh.privkey, FALSE); +#ifdef MBEDTLS_DH_GEN_PRIME + ret = dh_gen_prime(&key_data(key)->a.dh.privkey, key->u.a.bitlen, bcrypt_rng, NULL); +#endif + buf = malloc(1024); + if (!buf) return STATUS_NO_MEMORY; + ret = mbedtls_dhm_make_params(&key_data(key)->a.dh.privkey, (key->u.a.bitlen + 7) / 8, buf, &olen, bcrypt_rng, NULL); + free(buf); + if (ret) mbedtls_dhm_free(&key_data(key)->a.dh.privkey); + break; + +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + case ALG_ID_DSA: + ret = 0; + init_dsa_key(&key_data(key)->a.dsa.privkey, (key->u.a.bitlen + 7) / 8, FALSE); + ret = mbedtls_dsa_genkey(&key_data(key)->a.dsa.privkey, sizeof(dsa_blob->q), (key->u.a.bitlen + 7) / 8, bcrypt_rng, NULL); + ret = mbedtls_dsa_check_pqg(&key_data(key)->a.dsa.privkey.P, &key_data(key)->a.dsa.privkey.Q, &key_data(key)->a.dsa.privkey.G); + ret = mbedtls_dsa_check_pubkey(&key_data(key)->a.dsa.privkey); + ret = mbedtls_dsa_check_privkey(&key_data(key)->a.dsa.privkey); + if (ret) mbedtls_dsa_free(&key_data(key)->a.dsa.privkey); + break; +#endif + default: + FIXME("unsupported alg_id %d\n", key->alg_id); + ret = MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + break; + } + + if (ret) return STATUS_INTERNAL_ERROR; + + buf = malloc(1024); + if (!buf) return STATUS_NO_MEMORY; + + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + status = init_pk_key(&key_data(key)->a.pk.pubkey, pk_type, FALSE); + status = key_export_ecc(key, buf, 1024, &len); + status = key_import_ecc_public(key, buf, len); + if (status) mbedtls_pk_free(&key_data(key)->a.pk.pubkey); + break; + + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + status = init_pk_key(&key_data(key)->a.pk.pubkey, pk_type, FALSE); + status = key_export_rsa(key, 0, buf, 1024, &len); + status = key_import_rsa_public(key, buf, len); + if (status) mbedtls_pk_free(&key_data(key)->a.pk.pubkey); + break; + +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + case ALG_ID_DSA: + init_dsa_key(&key_data(key)->a.dsa.pubkey, (key->u.a.bitlen + 7) / 8, FALSE); + status = key_export_dsa(key, &key_data(key)->a.dsa.privkey, buf, 1024, &len); + status = key_import_dsa_public(key, &key_data(key)->a.dsa.pubkey, buf, len); + if (status) mbedtls_dsa_free(&key_data(key)->a.dsa.pubkey); + break; +#endif + case ALG_ID_DH: + init_dh_key(&key_data(key)->a.dh.pubkey, FALSE); + status = key_export_dh(key, &key_data(key)->a.dh.privkey, buf, 1024, &len); + status = key_import_dh_public(key, &key_data(key)->a.dh.pubkey, buf, len); + if (status) mbedtls_dhm_free(&key_data(key)->a.dh.pubkey); + break; + + default: + FIXME("algorithm %u not yet supported\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + free(buf); + + if (status) return status; + + return STATUS_SUCCESS; +} + +static mbedtls_md_type_t get_digest_from_id(const WCHAR *alg_id) +{ + if (!wcscmp(alg_id, BCRYPT_SHA1_ALGORITHM)) return MBEDTLS_MD_SHA1; + if (!wcscmp(alg_id, BCRYPT_SHA256_ALGORITHM)) return MBEDTLS_MD_SHA256; + if (!wcscmp(alg_id, BCRYPT_SHA384_ALGORITHM)) return MBEDTLS_MD_SHA384; + if (!wcscmp(alg_id, BCRYPT_SHA512_ALGORITHM)) return MBEDTLS_MD_SHA512; + if (!wcscmp(alg_id, BCRYPT_MD2_ALGORITHM)) return MBEDTLS_MD_MD2; + if (!wcscmp(alg_id, BCRYPT_MD5_ALGORITHM)) return MBEDTLS_MD_MD5; + return MBEDTLS_MD_NONE; +} + +NTSTATUS key_asymmetric_verify(void *args) +{ + const struct key_asymmetric_verify_params *params = args; + struct key *key = params->key; + mbedtls_md_type_t hash_alg = MBEDTLS_MD_NONE; + mbedtls_pk_type_t pk_type = key_get_type(key->alg_id); + mbedtls_pk_rsassa_pss_options pss_options; + UCHAR *out_signature; + ULONG out_signature_len; + UCHAR *p, *start; + size_t len; + int ret; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + + switch (key->alg_id) + { + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + { + if (params->flags) FIXME("flags %x not supported\n", params->flags); + + /* only the hash size must match, not the actual hash function */ + switch (params->hash_len) + { + case 20: + hash_alg = MBEDTLS_MD_SHA1; + break; + case 32: + hash_alg = MBEDTLS_MD_SHA256; + break; + case 48: + hash_alg = MBEDTLS_MD_SHA384; + break; + + default: + FIXME("hash size %u not yet supported\n", params->hash_len); + return STATUS_INVALID_SIGNATURE; + } + break; + } + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + { + if (params->flags & BCRYPT_PAD_PKCS1) + { + BCRYPT_PKCS1_PADDING_INFO *info = params->padding; + + if (!info) return STATUS_INVALID_PARAMETER; + if (!info->pszAlgId) + { + hash_alg = MBEDTLS_MD_NONE; + } + else + { + if ((hash_alg = get_digest_from_id(info->pszAlgId)) == MBEDTLS_MD_NONE) + { + FIXME("hash algorithm %s not supported\n", debugstr_w(info->pszAlgId)); + return STATUS_NOT_SUPPORTED; + } + } + } + else if (params->flags & BCRYPT_PAD_PSS) + { + BCRYPT_PSS_PADDING_INFO *info = params->padding; + + if (!info) return STATUS_INVALID_PARAMETER; + if (!info->pszAlgId) return STATUS_INVALID_SIGNATURE; + if ((hash_alg = get_digest_from_id(info->pszAlgId)) == MBEDTLS_MD_NONE) + { + FIXME("hash algorithm %s not supported\n", debugstr_w(info->pszAlgId)); + return STATUS_NOT_SUPPORTED; + } + pss_options.mgf1_hash_id = hash_alg; + pss_options.expected_salt_len = info->cbSalt; + pk_type = MBEDTLS_PK_RSASSA_PSS; + } + else + return STATUS_INVALID_PARAMETER; + break; + } +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + case ALG_ID_DSA: + { + if (params->flags) FIXME("flags %x not supported\n", params->flags); + if (params->hash_len != 20) + { + FIXME("hash size %u not supported\n", params->hash_len); + return STATUS_INVALID_PARAMETER; + } + hash_alg = MBEDTLS_MD_SHA1; + break; + } +#endif + default: + FIXME("algorithm %u not yet supported\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + + if (key->alg_id == ALG_ID_ECDSA_P256 || key->alg_id == ALG_ID_ECDSA_P384 +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + || key->alg_id == ALG_ID_DSA +#endif + ) + { + len = params->signature_len / 2; + out_signature = malloc(params->signature_len + 6); + if (!out_signature) return STATUS_NO_MEMORY; + p = out_signature + params->signature_len + 6; + start = out_signature; + ret = mbedtls_asn1_write_raw_buffer(&p, start, params->signature + len, len); + ret = mbedtls_asn1_write_len(&p, start, len); + ret = mbedtls_asn1_write_tag(&p, start, MBEDTLS_ASN1_INTEGER); + ret = mbedtls_asn1_write_raw_buffer(&p, start, params->signature, len); + ret = mbedtls_asn1_write_len(&p, start, len); + ret = mbedtls_asn1_write_tag(&p, start, MBEDTLS_ASN1_INTEGER); + ret = mbedtls_asn1_write_len(&p, start, params->signature_len + 4); + ret = mbedtls_asn1_write_tag(&p, start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + out_signature_len = params->signature_len + 6; + } + else + { + out_signature = params->signature; + out_signature_len = params->signature_len; + } + +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + if (key->alg_id == ALG_ID_DSA) + ret = mbedtls_dsa_verify(&key_data(params->key)->a.dsa.pubkey, params->hash, params->hash_len, out_signature, out_signature_len); + else +#endif + ret = mbedtls_pk_verify_ext(pk_type, (params->flags & BCRYPT_PAD_PSS) == 0 ? NULL : &pss_options, &key_data(params->key)->a.pk.pubkey, + hash_alg, params->hash, params->hash_len, out_signature, out_signature_len); + if (out_signature != params->signature) free(out_signature); + if (ret) return STATUS_INVALID_SIGNATURE; + + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_sign(void *args) +{ + const struct key_asymmetric_sign_params *params = args; + struct key *key = params->key; + size_t sig_len, len, expected_len; + mbedtls_md_type_t hash_alg = MBEDTLS_MD_NONE; + unsigned int flags = params->flags; + UCHAR *p, *end, *output; + int ret; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + + if (key->alg_id == ALG_ID_ECDSA_P256 || key->alg_id == ALG_ID_ECDSA_P384) + { + /* With ECDSA, we find the digest algorithm from the hash length, and verify it */ + switch (params->input_len) + { + case 20: + hash_alg = MBEDTLS_MD_SHA1; + break; + case 32: + hash_alg = MBEDTLS_MD_SHA256; + break; + case 48: + hash_alg = MBEDTLS_MD_SHA384; + break; + case 64: + hash_alg = MBEDTLS_MD_SHA512; + break; + + default: + FIXME("hash size %u not yet supported\n", params->input_len); + return STATUS_INVALID_PARAMETER; + } + + if (flags == BCRYPT_PAD_PKCS1) + { + BCRYPT_PKCS1_PADDING_INFO *pad = params->padding; + if (pad && pad->pszAlgId && get_digest_from_id(pad->pszAlgId) != hash_alg) + { + WARN("incorrect hashing algorithm %s, expected %u\n", debugstr_w(pad->pszAlgId), hash_alg); + return STATUS_INVALID_PARAMETER; + } + } + } +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + else if (key->alg_id == ALG_ID_DSA) + { + if (flags) FIXME("flags %#x not supported\n", flags); + if (params->input_len != 20) + { + FIXME("hash size %u not supported\n", params->input_len); + return STATUS_INVALID_PARAMETER; + } + hash_alg = MBEDTLS_MD_SHA1; + } +#endif + else if (flags == BCRYPT_PAD_PKCS1) + { + BCRYPT_PKCS1_PADDING_INFO *pad = params->padding; + + if (!pad) + { + WARN("padding info not found\n"); + return STATUS_INVALID_PARAMETER; + } + if (!pad->pszAlgId) hash_alg = MBEDTLS_MD_NONE; + else if ((hash_alg = get_digest_from_id(pad->pszAlgId)) == MBEDTLS_MD_NONE) + { + FIXME("hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId)); + return STATUS_NOT_SUPPORTED; + } + } + else if (flags == BCRYPT_PAD_PSS) + { + BCRYPT_PSS_PADDING_INFO *pad = params->padding; + + if (!pad || !pad->pszAlgId) + { + WARN("padding info not found\n"); + return STATUS_INVALID_PARAMETER; + } + if (key->alg_id != ALG_ID_RSA && key->alg_id != ALG_ID_RSA_SIGN) + { + FIXME("BCRYPT_PAD_PSS not supported for key algorithm %u\n", key->alg_id); + return STATUS_NOT_SUPPORTED; + } + if ((hash_alg = get_digest_from_id(pad->pszAlgId)) == MBEDTLS_MD_NONE) + { + FIXME("hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId)); + return STATUS_NOT_SUPPORTED; + } + + mbedtls_rsa_set_padding(mbedtls_pk_rsa(key_data(key)->a.pk.privkey), MBEDTLS_RSA_PKCS_V21, hash_alg); + } + else if (!flags) + { + WARN("invalid flags %#x\n", flags); + return STATUS_INVALID_PARAMETER; + } + else + { + FIXME("flags %#x not implemented\n", flags); + return STATUS_NOT_IMPLEMENTED; + } + + if (!params->output) + { + *params->ret_len = key->u.a.bitlen / 8; + return STATUS_SUCCESS; + } + + output = malloc(1024); + if (!output) return STATUS_NO_MEMORY; +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + if (key->alg_id == ALG_ID_DSA) + ret = mbedtls_dsa_sign(&key_data(params->key)->a.dsa.privkey, params->input, params->input_len, output, &sig_len, + bcrypt_rng, NULL); + else +#endif + ret = mbedtls_pk_sign(&key_data(params->key)->a.pk.privkey, hash_alg, params->input, params->input_len, output, &sig_len, + bcrypt_rng, NULL); + if (ret) + { + free(output); + return STATUS_INTERNAL_ERROR; + } + if ((key->alg_id == ALG_ID_ECDSA_P256 || key->alg_id == ALG_ID_ECDSA_P384 +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + || key->alg_id == ALG_ID_DSA +#endif + )) + { + p = output; + end = output + sig_len; + ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER); + expected_len = (len / 2) * 2; + if (!ret && expected_len < len) p += len - expected_len; + if (!ret) sig_len = expected_len; + memcpy(params->output, p, expected_len); + p += expected_len; + ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER); + expected_len = (len / 2) * 2; + if (!ret && expected_len < len) p += len - expected_len; + if (!ret) memcpy(params->output + sig_len, p, expected_len); + if (!ret) sig_len += expected_len; + } + else + if (sig_len <= params->output_len) memcpy(params->output, output, sig_len); + free(output); + + *params->ret_len = sig_len; + if (sig_len > params->output_len) return STATUS_BUFFER_TOO_SMALL; + if (ret) return STATUS_INTERNAL_ERROR; + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_destroy(void *args) +{ + struct key *key = args; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + + switch (key->alg_id) + { + case ALG_ID_ECDSA_P256: + case ALG_ID_ECDSA_P384: + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + { + mbedtls_pk_free(&key_data(key)->a.pk.privkey); + mbedtls_pk_free(&key_data(key)->a.pk.pubkey); + break; + } + case ALG_ID_DH: + { + mbedtls_dhm_free(&key_data(key)->a.dh.privkey); + mbedtls_dhm_free(&key_data(key)->a.dh.pubkey); + break; + } +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + case ALG_ID_DSA: + { + mbedtls_dsa_free(&key_data(key)->a.dsa.privkey); + mbedtls_dsa_free(&key_data(key)->a.dsa.pubkey); + break; + } +#endif + default: + FIXME("algorithm %u not yet supported\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_duplicate(void *args) +{ + const struct key_asymmetric_duplicate_params *params = args; + mbedtls_pk_type_t pk_type; + unsigned char *buf; + ULONG len; + NTSTATUS status; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + + buf = malloc(1024); + if (!buf) return STATUS_NO_MEMORY; + + switch (params->key_orig->alg_id) + { + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + { + pk_type = key_get_type(params->key_orig->alg_id); + init_pk_key(&key_data(params->key_copy)->a.pk.privkey, pk_type, TRUE); + init_pk_key(&key_data(params->key_copy)->a.pk.pubkey, pk_type, TRUE); + status = key_export_rsa(params->key_orig, KEY_EXPORT_FLAG_RSA_FULL, buf, 1024, &len); + status = key_import_rsa(params->key_copy, buf, len); + status = key_export_rsa_public(params->key_orig, buf, 1024, &len); + if (status) + { + mbedtls_pk_free(&key_data(params->key_copy)->a.pk.privkey); + mbedtls_pk_free(&key_data(params->key_copy)->a.pk.pubkey); + break; + } + status = key_import_rsa_public(params->key_copy, buf, len); + break; + } + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + { + pk_type = key_get_type(params->key_orig->alg_id); + init_pk_key(&key_data(params->key_copy)->a.pk.privkey, pk_type, TRUE); + init_pk_key(&key_data(params->key_copy)->a.pk.pubkey, pk_type, TRUE); + status = key_export_ecc(params->key_orig, buf, 1024, &len); + status = key_import_ecc(params->key_copy, buf, len); + status = key_export_ecc_public(params->key_orig, buf, 1024, &len); + if (status) + { + mbedtls_pk_free(&key_data(params->key_copy)->a.pk.privkey); + mbedtls_pk_free(&key_data(params->key_copy)->a.pk.pubkey); + break; + } + status = key_import_ecc_public(params->key_copy, buf, len); + break; + } +#ifdef MBEDTLS_DSA_C /* DSA is not supported by mbedtls */ + case ALG_ID_DSA: + { + init_dsa_key(&key_data(params->key_copy)->a.dsa.privkey, (params->key_orig->u.a.bitlen + 7) / 8, TRUE); + init_dsa_key(&key_data(params->key_copy)->a.dsa.pubkey, (params->key_orig->u.a.bitlen + 7) / 8, TRUE); + status = key_export_dsa(params->key_orig, &key_data(params->key_orig)->a.dsa.privkey, buf, 1024, &len); + status = key_import_dsa(params->key_copy, &key_data(params->key_copy)->a.dsa.privkey, buf, len); + status = key_export_dsa_public(params->key_orig, &key_data(params->key_orig)->a.dsa.pubkey, buf, 1024, &len); + if (status) + { + mbedtls_dsa_free(&key_data(params->key_copy)->a.dsa.privkey); + mbedtls_dsa_free(&key_data(params->key_copy)->a.dsa.pubkey); + break; + } + status = key_import_dsa_public(params->key_copy, &key_data(params->key_copy)->a.dsa.pubkey, buf, len); + break; + } +#endif + case ALG_ID_DH: + { + init_dh_key(&key_data(params->key_copy)->a.dh.privkey, TRUE); + init_dh_key(&key_data(params->key_copy)->a.dh.pubkey, TRUE); + status = key_export_dh(params->key_orig, &key_data(params->key_orig)->a.dh.privkey, buf, 1024, &len); + status = key_import_dh(params->key_copy, &key_data(params->key_copy)->a.dh.privkey, buf, len); + status = key_export_dh_public(params->key_orig, &key_data(params->key_orig)->a.dh.pubkey, buf, 1024, &len); + if (status) + { + mbedtls_dhm_free(&key_data(params->key_copy)->a.dh.privkey); + mbedtls_dhm_free(&key_data(params->key_copy)->a.dh.pubkey); + break; + } + status = key_import_dh_public(params->key_copy, &key_data(params->key_copy)->a.dh.pubkey, buf, len); + break; + } + default: + FIXME("unsupported algorithm %u\n", params->key_orig->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + + free(buf); + if (!status) return STATUS_SUCCESS; + + return STATUS_INTERNAL_ERROR; +} + +NTSTATUS key_asymmetric_decrypt(void *args) +{ + const struct key_asymmetric_decrypt_params *params = args; + size_t olen; + int ret; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + + if (!(params->key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE; + + if (params->key->alg_id == ALG_ID_RSA && params->flags & BCRYPT_PAD_OAEP) + { + BCRYPT_OAEP_PADDING_INFO *pad = params->padding; + mbedtls_md_type_t dig; + + if (!pad || !pad->pszAlgId) + { + WARN("padding info not found\n"); + return STATUS_INVALID_PARAMETER; + } + if ((dig = get_digest_from_id(pad->pszAlgId)) == MBEDTLS_MD_NONE) + { + FIXME("hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId)); + return STATUS_NOT_SUPPORTED; + } + + mbedtls_rsa_set_padding(mbedtls_pk_rsa(key_data(params->key)->a.pk.privkey), MBEDTLS_RSA_PKCS_V21, dig); + } + ret = mbedtls_pk_decrypt(&key_data(params->key)->a.pk.privkey, params->input, params->input_len, params->output, &olen, params->output_len, bcrypt_rng, NULL); + + if (ret) return STATUS_INTERNAL_ERROR; + + *params->ret_len = olen; + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_encrypt(void *args) +{ + const struct key_asymmetric_encrypt_params *params = args; + size_t olen; + int ret; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + + if (!(params->key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE; + + if (params->key->alg_id == ALG_ID_RSA && params->flags & BCRYPT_PAD_OAEP) + { + BCRYPT_OAEP_PADDING_INFO *pad = params->padding; + mbedtls_md_type_t dig; + + if (!pad || !pad->pszAlgId || !pad->pbLabel) + { + WARN("padding info not found\n"); + return STATUS_INVALID_PARAMETER; + } + if ((dig = get_digest_from_id(pad->pszAlgId)) == MBEDTLS_MD_NONE) + { + FIXME("hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId)); + return STATUS_NOT_SUPPORTED; + } + + mbedtls_rsa_set_padding(mbedtls_pk_rsa(key_data(params->key)->a.pk.pubkey), MBEDTLS_RSA_PKCS_V21, dig); + } + + ret = mbedtls_pk_encrypt(&key_data(params->key)->a.pk.pubkey, params->input, params->input_len, params->output, &olen, params->output_len, + bcrypt_rng, NULL); + + if (ret) return STATUS_INTERNAL_ERROR; + + *params->ret_len = olen; + return STATUS_SUCCESS; +} + +NTSTATUS key_asymmetric_derive_key(void *args) +{ + const struct key_asymmetric_derive_key_params *params = args; + int ret; + +#ifndef __REACTOS__ + if (!libmbedtls_handle) return STATUS_INTERNAL_ERROR; +#endif + + switch (params->privkey->alg_id) + { + case ALG_ID_ECDH_P256: + case ALG_ID_ECDH_P384: + { + mbedtls_ecdh_context ecdh; + mbedtls_ecp_keypair *ecp_keypair_pr; + mbedtls_ecp_keypair *ecp_keypair_pu; + + mbedtls_ecdh_init(&ecdh); + ecp_keypair_pr = mbedtls_pk_ec(key_data(params->privkey)->a.pk.privkey); + ecp_keypair_pu = mbedtls_pk_ec(key_data(params->pubkey)->a.pk.pubkey); + if (!ecp_keypair_pr || !ecp_keypair_pu) return STATUS_INTERNAL_ERROR; + mbedtls_ecp_group_load(&ecdh.grp, ecp_keypair_pr->grp.id); + mbedtls_mpi_copy(&ecdh.d, &ecp_keypair_pr->d); + mbedtls_ecp_copy(&ecdh.Qp, &ecp_keypair_pu->Q); + + *params->ret_len = mbedtls_mpi_size(&ecdh.grp.P); + if (params->output) + { + if (params->output_len < *params->ret_len) + { + mbedtls_ecdh_free(&ecdh); + return STATUS_BUFFER_TOO_SMALL; + } + ret = mbedtls_ecdh_compute_shared(&ecdh.grp, &ecdh.z, &ecdh.Qp, &ecdh.d, bcrypt_rng, NULL); + if (ret) + { + FIXME("mbedtls_ecdh_compute_shared failed: %d\n", ret); + mbedtls_ecdh_free(&ecdh); + return STATUS_UNSUCCESSFUL; + } + mbedtls_mpi_write_binary(&ecdh.z, params->output, *params->ret_len); + } + mbedtls_ecdh_free(&ecdh); + break; + } + case ALG_ID_DH: + { + mbedtls_dhm_context *dhm; + mbedtls_dhm_context *dhm_pub; + size_t secret_len; + + dhm = &key_data(params->privkey)->a.dh.privkey; + dhm_pub = &key_data(params->pubkey)->a.dh.pubkey; + + mbedtls_mpi_copy(&dhm->GY, &dhm_pub->GX); + + *params->ret_len = mbedtls_mpi_size(&dhm->P); + if (params->output) + { + if (params->output_len < *params->ret_len) return STATUS_BUFFER_TOO_SMALL; + ret = mbedtls_dhm_calc_secret(dhm, params->output, params->output_len, &secret_len, bcrypt_rng, NULL); + if (ret) + { + FIXME("mbedtls_dhm_calc_secret failed with %d\n", ret); + return STATUS_UNSUCCESSFUL; + } + *params->ret_len = secret_len; + } + break; + } + default: + FIXME("unsupported algorithm %u\n", params->privkey->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + + return STATUS_SUCCESS; +} + +#endif /* SONAME_LIBMBEDTLS */ diff --git a/dll/win32/bcrypt/precomp.h b/dll/win32/bcrypt/precomp.h new file mode 100644 index 00000000000..77536cd2b39 --- /dev/null +++ b/dll/win32/bcrypt/precomp.h @@ -0,0 +1,22 @@ + +#ifndef _BCRYPT_PRECOMP_H +#define _BCRYPT_PRECOMP_H + +#include +#include + +#include +#define WIN32_NO_STATUS +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bcrypt_internal.h" + +#endif /* !_BCRYPT_PRECOMP_H */ diff --git a/media/doc/WINESYNC.txt b/media/doc/WINESYNC.txt index d6f668ec2b2..c2d99b4ed9e 100644 --- a/media/doc/WINESYNC.txt +++ b/media/doc/WINESYNC.txt @@ -58,7 +58,7 @@ dll/win32/atl80 # Synced to Wine-10.0 dll/win32/atl100 # Synced to Wine-10.0 dll/win32/avifil32 # Synced to Wine-10.0 dll/win32/avrt # Synced to Wine-10.0 -dll/win32/bcrypt # Synced to WineStaging-1.9.23 (+ winetest synced to 8d8936cbb6fea3cac862e059e814527f5361f48b, a.k.a 20161206-BJ) +dll/win32/bcrypt # Synced to WineStaging-10.6 (+ winetest synced to d9148e48a3257ae80361f02f0bb7a2d607ef18ec) dll/win32/bcryptprimitives # Synced to Wine-10.0 dll/win32/bluetoothapis # Synced to Wine-10.0 dll/win32/cabinet # Synced to Wine-10.0 diff --git a/modules/rostests/winetests/bcrypt/bcrypt.c b/modules/rostests/winetests/bcrypt/bcrypt.c index 42a20be4b85..de672943e5c 100644 --- a/modules/rostests/winetests/bcrypt/bcrypt.c +++ b/modules/rostests/winetests/bcrypt/bcrypt.c @@ -23,6 +23,7 @@ #define WIN32_NO_STATUS #include #include +#include #include "wine/test.h" @@ -36,6 +37,29 @@ static NTSTATUS (WINAPI *pBCryptFinishHash)(BCRYPT_HASH_HANDLE, PUCHAR, ULONG, U static NTSTATUS (WINAPI *pBCryptDestroyHash)(BCRYPT_HASH_HANDLE); static NTSTATUS (WINAPI *pBCryptGenRandom)(BCRYPT_ALG_HANDLE, PUCHAR, ULONG, ULONG); static NTSTATUS (WINAPI *pBCryptGetProperty)(BCRYPT_HANDLE, LPCWSTR, PUCHAR, ULONG, ULONG *, ULONG); +static NTSTATUS (WINAPI *pBCryptKeyDerivation)(BCRYPT_KEY_HANDLE, + BCryptBufferDesc *, UCHAR *, ULONG, ULONG *, ULONG); +static NTSTATUS (WINAPI *pBCryptDeriveKeyPBKDF2)(BCRYPT_ALG_HANDLE, PUCHAR, ULONG, PUCHAR, ULONG, ULONGLONG, PUCHAR, ULONG, ULONG); +static NTSTATUS (WINAPI *pBCryptSetProperty)(BCRYPT_HANDLE, LPCWSTR, PUCHAR, ULONG, ULONG); +static NTSTATUS (WINAPI *pBCryptGenerateSymmetricKey)(BCRYPT_ALG_HANDLE, BCRYPT_KEY_HANDLE *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG); +static NTSTATUS (WINAPI *pBCryptEncrypt)(BCRYPT_KEY_HANDLE, PUCHAR, ULONG, VOID *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG *, ULONG); +static NTSTATUS (WINAPI *pBCryptDuplicateKey)(BCRYPT_KEY_HANDLE, BCRYPT_KEY_HANDLE *, PUCHAR, ULONG, ULONG); +static NTSTATUS (WINAPI *pBCryptDestroyKey)(BCRYPT_KEY_HANDLE); +static NTSTATUS (WINAPI *pBCryptDecrypt)(BCRYPT_KEY_HANDLE, PUCHAR, ULONG, VOID *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG *, ULONG); +static NTSTATUS (WINAPI *pBCryptImportKey)(BCRYPT_ALG_HANDLE, BCRYPT_KEY_HANDLE, LPCWSTR, BCRYPT_KEY_HANDLE *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG); +static NTSTATUS (WINAPI *pBCryptExportKey)(BCRYPT_KEY_HANDLE, BCRYPT_KEY_HANDLE, LPCWSTR, PUCHAR, ULONG, ULONG *, ULONG); +static NTSTATUS (WINAPI *pBCryptImportKeyPair)(BCRYPT_ALG_HANDLE, BCRYPT_KEY_HANDLE, LPCWSTR, BCRYPT_KEY_HANDLE *, UCHAR *, ULONG, ULONG); +static NTSTATUS (WINAPI *pBCryptVerifySignature)(BCRYPT_KEY_HANDLE, void *, UCHAR *, ULONG, UCHAR *, ULONG, ULONG); +static NTSTATUS (WINAPI *pBCryptGenerateKeyPair)(BCRYPT_ALG_HANDLE, BCRYPT_KEY_HANDLE *, ULONG, ULONG); +static NTSTATUS (WINAPI *pBCryptFinalizeKeyPair)(BCRYPT_KEY_HANDLE, ULONG); +static NTSTATUS (WINAPI *pBCryptSignHash)(BCRYPT_KEY_HANDLE, void *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG *, ULONG); +static NTSTATUS (WINAPI *pBCryptSecretAgreement)(BCRYPT_KEY_HANDLE, BCRYPT_KEY_HANDLE, BCRYPT_SECRET_HANDLE *, ULONG); +static NTSTATUS (WINAPI *pBCryptDeriveKey)(BCRYPT_SECRET_HANDLE, LPCWSTR, BCryptBufferDesc*, PUCHAR, ULONG, ULONG *, ULONG); +static NTSTATUS (WINAPI *pBCryptDestroySecret)(BCRYPT_SECRET_HANDLE); +static NTSTATUS (WINAPI *pBCryptEnumAlgorithms)(ULONG, ULONG *, BCRYPT_ALGORITHM_IDENTIFIER **, ULONG); +static void (WINAPI *pBCryptFreeBuffer)(void *); +static NTSTATUS (WINAPI *pBCryptDeriveKeyCapi)(BCRYPT_HASH_HANDLE, BCRYPT_ALG_HANDLE, PUCHAR, ULONG, ULONG); +static NTSTATUS (WINAPI *pBCryptEnumContextFunctions)(ULONG, const WCHAR *, ULONG, ULONG *, CRYPT_CONTEXT_FUNCTIONS **); static void test_BCryptGenRandom(void) { @@ -65,6 +89,18 @@ static void test_BCryptGenRandom(void) ret = pBCryptGenRandom(NULL, buffer, 8, BCRYPT_USE_SYSTEM_PREFERRED_RNG); ok(ret == STATUS_SUCCESS, "Expected success, got 0x%x\n", ret); ok(memcmp(buffer, buffer + 8, 8), "Expected a random number, got 0\n"); + + /* Test pseudo handle, which was introduced at the same time as BCryptHash */ + if (pBCryptHash) + { + ret = pBCryptGenRandom(BCRYPT_RNG_ALG_HANDLE, buffer, sizeof(buffer), 0); + ok(ret == STATUS_SUCCESS, "Expected success, got 0x%x\n", ret); + + ret = pBCryptCloseAlgorithmProvider(BCRYPT_RNG_ALG_HANDLE, 0); + ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + } + else win_skip("BCryptGenRandom pseudo handles are not available\n"); + } static void test_BCryptGetFipsAlgorithmMode(void) @@ -102,7 +138,7 @@ static void test_BCryptGetFipsAlgorithmMode(void) else { expected = FALSE; -todo_wine + todo_wine ok(0, "Neither XP or Vista key is present\n"); } RegCloseKey(hkey); @@ -126,11 +162,40 @@ static void format_hash(const UCHAR *bytes, ULONG size, char *buf) return; } -static int strcmp_wa(const WCHAR *strw, const char *stra) +#define test_object_length(a) _test_object_length(__LINE__,a) +static void _test_object_length(unsigned line, void *handle) { - WCHAR buf[512]; - MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, sizeof(buf)/sizeof(buf[0])); - return lstrcmpW(strw, buf); + NTSTATUS status; + ULONG len, size; + + len = size = 0xdeadbeef; + status = pBCryptGetProperty(NULL, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok_(__FILE__,line)(status == STATUS_INVALID_HANDLE, "BCryptGetProperty failed: 0x%x\n", status); + + len = size = 0xdeadbeef; + status = pBCryptGetProperty(handle, NULL, (UCHAR *)&len, sizeof(len), &size, 0); + ok_(__FILE__,line)(status == STATUS_INVALID_PARAMETER, "BCryptGetProperty failed: 0x%x\n", status); + + len = size = 0xdeadbeef; + status = pBCryptGetProperty(handle, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), NULL, 0); + ok_(__FILE__,line)(status == STATUS_INVALID_PARAMETER, "BCryptGetProperty failed: 0x%x\n", status); + + len = size = 0xdeadbeef; + status = pBCryptGetProperty(handle, BCRYPT_OBJECT_LENGTH, NULL, sizeof(len), &size, 0); + ok_(__FILE__,line)(status == STATUS_SUCCESS, "BCryptGetProperty failed: 0x%x\n", status); + ok_(__FILE__,line)(size == sizeof(len), "got %u\n", size); + + len = size = 0xdeadbeef; + status = pBCryptGetProperty(handle, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, 0, &size, 0); + ok_(__FILE__,line)(status == STATUS_BUFFER_TOO_SMALL, "BCryptGetProperty failed: 0x%x\n", status); + ok_(__FILE__,line)(len == 0xdeadbeef, "got %u\n", len); + ok_(__FILE__,line)(size == sizeof(len), "got %u\n", size); + + len = size = 0xdeadbeef; + status = pBCryptGetProperty(handle, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok_(__FILE__,line)(status == STATUS_SUCCESS, "BCryptGetProperty failed: 0x%x\n", status); + ok_(__FILE__,line)(len != 0xdeadbeef, "len not set\n"); + ok_(__FILE__,line)(size == sizeof(len), "got %u\n", size); } #define test_hash_length(a,b) _test_hash_length(__LINE__,a,b) @@ -140,13 +205,13 @@ static void _test_hash_length(unsigned line, void *handle, ULONG exlen) NTSTATUS status; status = pBCryptGetProperty(handle, BCRYPT_HASH_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); - ok_(__FILE__,line)(status == STATUS_SUCCESS, "BCryptGetProperty failed: %08x\n", status); + ok_(__FILE__,line)(status == STATUS_SUCCESS, "BCryptGetProperty failed: 0x%08x\n", status); ok_(__FILE__,line)(size == sizeof(len), "got %u\n", size); ok_(__FILE__,line)(len == exlen, "len = %u, expected %u\n", len, exlen); } #define test_alg_name(a,b) _test_alg_name(__LINE__,a,b) -static void _test_alg_name(unsigned line, void *handle, const char *exname) +static void _test_alg_name(unsigned line, void *handle, const WCHAR *exname) { ULONG size = 0xdeadbeef; UCHAR buf[256]; @@ -154,545 +219,243 @@ static void _test_alg_name(unsigned line, void *handle, const char *exname) NTSTATUS status; status = pBCryptGetProperty(handle, BCRYPT_ALGORITHM_NAME, buf, sizeof(buf), &size, 0); - ok_(__FILE__,line)(status == STATUS_SUCCESS, "BCryptGetProperty failed: %08x\n", status); - ok_(__FILE__,line)(size == (strlen(exname)+1)*sizeof(WCHAR), "got %u\n", size); - ok_(__FILE__,line)(!strcmp_wa(name, exname), "alg name = %s, expected %s\n", wine_dbgstr_w(name), exname); + ok_(__FILE__,line)(status == STATUS_SUCCESS, "BCryptGetProperty failed: 0x%08x\n", status); + ok_(__FILE__,line)(size == (lstrlenW(exname) + 1) * sizeof(WCHAR), "got %u\n", size); + ok_(__FILE__,line)(!lstrcmpW(name, exname), "alg name = %s, expected %s\n", wine_dbgstr_w(name), + wine_dbgstr_w(exname)); } -static void test_sha1(void) +struct hash_test +{ + const WCHAR *alg; + unsigned hash_size; + const char *hash; + const char *hash2; + const char *hmac_hash; + const char *hmac_hash2; +}; + +static void test_hash(const struct hash_test *test) { - static const char expected[] = "961fa64958818f767707072755d7018dcd278e94"; - static const char expected_hmac[] = "2472cf65d0e090618d769d3e46f0d9446cf212da"; BCRYPT_ALG_HANDLE alg; BCRYPT_HASH_HANDLE hash; - UCHAR buf[512], buf_hmac[1024], sha1[20], sha1_hmac[20]; - ULONG size, len; - char str[41]; + UCHAR buf[512], buf_hmac[1024], hash_buf[128], hmac_hash[128]; + char str[512]; NTSTATUS ret; + ULONG len; alg = NULL; - ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ret = pBCryptOpenAlgorithmProvider(&alg, test->alg, MS_PRIMITIVE_PROVIDER, 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); ok(alg != NULL, "alg not set\n"); - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(NULL, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_INVALID_HANDLE, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, NULL, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), NULL, 0); - ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, NULL, sizeof(len), &size, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(size == sizeof(len), "got %u\n", size); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, 0, &size, 0); - ok(ret == STATUS_BUFFER_TOO_SMALL, "got %08x\n", ret); - ok(len == 0xdeadbeef, "got %u\n", len); - ok(size == sizeof(len), "got %u\n", size); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len , sizeof(len), &size, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(len != 0xdeadbeef, "len not set\n"); - ok(size == sizeof(len), "got %u\n", size); - - test_hash_length(alg, 20); - test_alg_name(alg, "SHA1"); + test_object_length(alg); + test_hash_length(alg, test->hash_size); + test_alg_name(alg, test->alg); hash = NULL; len = sizeof(buf); ret = pBCryptCreateHash(alg, &hash, buf, len, NULL, 0, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); ok(hash != NULL, "hash not set\n"); ret = pBCryptHashData(hash, NULL, 0, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); - test_hash_length(hash, 20); - test_alg_name(hash, "SHA1"); + test_hash_length(hash, test->hash_size); + test_alg_name(hash, test->alg); - memset(sha1, 0, sizeof(sha1)); - ret = pBCryptFinishHash(hash, sha1, sizeof(sha1), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - format_hash( sha1, sizeof(sha1), str ); - ok(!strcmp(str, expected), "got %s\n", str); + memset(hash_buf, 0, sizeof(hash_buf)); + ret = pBCryptFinishHash(hash, hash_buf, test->hash_size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + format_hash( hash_buf, test->hash_size, str ); + ok(!strcmp(str, test->hash), "got %s\n", str); ret = pBCryptDestroyHash(hash); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + + hash = NULL; + len = sizeof(buf); + ret = pBCryptCreateHash(alg, &hash, buf, len, NULL, 0, BCRYPT_HASH_REUSABLE_FLAG); + ok(ret == STATUS_SUCCESS || broken(ret == STATUS_INVALID_PARAMETER) /* < win8 */, "got 0x%08x\n", ret); + if (ret == STATUS_SUCCESS) + { + ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + + memset(hash_buf, 0, sizeof(hash_buf)); + ret = pBCryptFinishHash(hash, hash_buf, test->hash_size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + format_hash( hash_buf, test->hash_size, str ); + ok(!strcmp(str, test->hash), "got %s\n", str); + + /* reuse it */ + ret = pBCryptHashData(hash, (UCHAR *)"tset", sizeof("tset"), 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + + memset(hash_buf, 0, sizeof(hash_buf)); + ret = pBCryptFinishHash(hash, hash_buf, test->hash_size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + format_hash( hash_buf, test->hash_size, str ); + ok(!strcmp(str, test->hash2), "got %s\n", str); + + ret = pBCryptDestroyHash(hash); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + } ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); alg = NULL; - ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ret = pBCryptOpenAlgorithmProvider(&alg, test->alg, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); ok(alg != NULL, "alg not set\n"); hash = NULL; len = sizeof(buf_hmac); ret = pBCryptCreateHash(alg, &hash, buf_hmac, len, (UCHAR *)"key", sizeof("key"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); ok(hash != NULL, "hash not set\n"); ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); - test_hash_length(hash, 20); - test_alg_name(hash, "SHA1"); + test_hash_length(hash, test->hash_size); + test_alg_name(hash, test->alg); - memset(sha1_hmac, 0, sizeof(sha1_hmac)); - ret = pBCryptFinishHash(hash, sha1_hmac, sizeof(sha1_hmac), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - format_hash( sha1_hmac, sizeof(sha1_hmac), str ); - ok(!strcmp(str, expected_hmac), "got %s\n", str); + memset(hmac_hash, 0, sizeof(hmac_hash)); + ret = pBCryptFinishHash(hash, hmac_hash, test->hash_size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + format_hash( hmac_hash, test->hash_size, str ); + ok(!strcmp(str, test->hmac_hash), "got %s\n", str); ret = pBCryptDestroyHash(hash); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); -} - -static void test_sha256(void) -{ - static const char expected[] = - "ceb73749c899693706ede1e30c9929b3fd5dd926163831c2fb8bd41e6efb1126"; - static const char expected_hmac[] = - "34c1aa473a4468a91d06e7cdbc75bc4f93b830ccfc2a47ffd74e8e6ed29e4c72"; - BCRYPT_ALG_HANDLE alg; - BCRYPT_HASH_HANDLE hash; - UCHAR buf[512], buf_hmac[1024], sha256[32], sha256_hmac[32]; - ULONG size, len; - char str[65]; - NTSTATUS ret; - - alg = NULL; - ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA256_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(alg != NULL, "alg not set\n"); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(NULL, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_INVALID_HANDLE, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, NULL, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), NULL, 0); - ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, NULL, sizeof(len), &size, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(size == sizeof(len), "got %u\n", size); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, 0, &size, 0); - ok(ret == STATUS_BUFFER_TOO_SMALL, "got %08x\n", ret); - ok(len == 0xdeadbeef, "got %u\n", len); - ok(size == sizeof(len), "got %u\n", size); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len , sizeof(len), &size, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(len != 0xdeadbeef, "len not set\n"); - ok(size == sizeof(len), "got %u\n", size); - - test_hash_length(alg, 32); - test_alg_name(alg, "SHA256"); - - hash = NULL; - len = sizeof(buf); - ret = pBCryptCreateHash(alg, &hash, buf, len, NULL, 0, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(hash != NULL, "hash not set\n"); - - ret = pBCryptHashData(hash, NULL, 0, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - test_hash_length(hash, 32); - test_alg_name(hash, "SHA256"); - - memset(sha256, 0, sizeof(sha256)); - ret = pBCryptFinishHash(hash, sha256, sizeof(sha256), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - format_hash( sha256, sizeof(sha256), str ); - ok(!strcmp(str, expected), "got %s\n", str); - - ret = pBCryptDestroyHash(hash); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - alg = NULL; - ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA256_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(alg != NULL, "alg not set\n"); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); hash = NULL; len = sizeof(buf_hmac); - ret = pBCryptCreateHash(alg, &hash, buf_hmac, len, (UCHAR *)"key", sizeof("key"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(hash != NULL, "hash not set\n"); + ret = pBCryptCreateHash(alg, &hash, buf_hmac, len, (UCHAR *)"key", sizeof("key"), BCRYPT_HASH_REUSABLE_FLAG); + ok(ret == STATUS_SUCCESS || broken(ret == STATUS_INVALID_PARAMETER) /* < win8 */, "got 0x%x\n", ret); + if (ret == STATUS_SUCCESS) + { + ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); - ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + memset(hmac_hash, 0, sizeof(hmac_hash)); + ret = pBCryptFinishHash(hash, hmac_hash, test->hash_size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + format_hash( hmac_hash, test->hash_size, str ); + ok(!strcmp(str, test->hmac_hash), "got %s\n", str); - test_hash_length(hash, 32); - test_alg_name(hash, "SHA256"); + /* reuse it */ + ret = pBCryptHashData(hash, (UCHAR *)"tset", sizeof("tset"), 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); - memset(sha256_hmac, 0, sizeof(sha256_hmac)); - ret = pBCryptFinishHash(hash, sha256_hmac, sizeof(sha256_hmac), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - format_hash( sha256_hmac, sizeof(sha256_hmac), str ); - ok(!strcmp(str, expected_hmac), "got %s\n", str); + memset(hmac_hash, 0, sizeof(hmac_hash)); + ret = pBCryptFinishHash(hash, hmac_hash, test->hash_size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + format_hash( hmac_hash, test->hash_size, str ); + ok(!strcmp(str, test->hmac_hash2), "got %s\n", str); + + ret = pBCryptDestroyHash(hash); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + } ret = pBCryptDestroyHash(hash); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%08x\n", ret); + + ret = pBCryptDestroyHash(NULL); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%08x\n", ret); ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + + if (pBCryptHash) + { + ret = pBCryptHash(BCRYPT_SHA1_ALG_HANDLE, NULL, 0, NULL, 0, hash_buf, 20); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + ret = pBCryptHash(BCRYPT_SHA1_ALG_HANDLE, NULL, 0, (UCHAR *)"test", sizeof("test"), NULL, 20); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%08x\n", ret); + ret = pBCryptHash(BCRYPT_SHA1_ALG_HANDLE, NULL, 0, (UCHAR *)"test", sizeof("test"), hash_buf, 21); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%08x\n", ret); + ret = pBCryptHash(BCRYPT_SHA1_ALG_HANDLE, NULL, 0, (UCHAR *)"test", sizeof("test"), hash_buf, 20); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + + ret = pBCryptCreateHash(BCRYPT_SHA1_ALG_HANDLE, &hash, NULL, 0, NULL, 0, 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + ret = pBCryptFinishHash(hash, hash_buf, 21, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + ret = pBCryptFinishHash(hash, hash_buf, 20, 0); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + ret = pBCryptDestroyHash(hash); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + } } -static void test_sha384(void) +static void test_hashes(void) { - static const char expected[] = - "62b21e90c9022b101671ba1f808f8631a8149f0f12904055839a35c1ca78ae5363eed1e743a692d70e0504b0cfd12ef9"; - static const char expected_hmac[] = - "4b3e6d6ff2da121790ab7e7b9247583e3a7eed2db5bd4dabc680303b1608f37dfdc836d96a704c03283bc05b4f6c5eb8"; - BCRYPT_ALG_HANDLE alg; - BCRYPT_HASH_HANDLE hash; - UCHAR buf[512], buf_hmac[1024], sha384[48], sha384_hmac[48]; - ULONG size, len; - char str[97]; - NTSTATUS ret; - - alg = NULL; - ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA384_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(alg != NULL, "alg not set\n"); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(NULL, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_INVALID_HANDLE, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, NULL, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), NULL, 0); - ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, NULL, sizeof(len), &size, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(size == sizeof(len), "got %u\n", size); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, 0, &size, 0); - ok(ret == STATUS_BUFFER_TOO_SMALL, "got %08x\n", ret); - ok(len == 0xdeadbeef, "got %u\n", len); - ok(size == sizeof(len), "got %u\n", size); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len , sizeof(len), &size, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(len != 0xdeadbeef, "len not set\n"); - ok(size == sizeof(len), "got %u\n", size); - - test_hash_length(alg, 48); - test_alg_name(alg, "SHA384"); - - hash = NULL; - len = sizeof(buf); - ret = pBCryptCreateHash(alg, &hash, buf, len, NULL, 0, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(hash != NULL, "hash not set\n"); - - ret = pBCryptHashData(hash, NULL, 0, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - test_hash_length(hash, 48); - test_alg_name(hash, "SHA384"); - - memset(sha384, 0, sizeof(sha384)); - ret = pBCryptFinishHash(hash, sha384, sizeof(sha384), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - format_hash( sha384, sizeof(sha384), str ); - ok(!strcmp(str, expected), "got %s\n", str); - - ret = pBCryptDestroyHash(hash); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - alg = NULL; - ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA384_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(alg != NULL, "alg not set\n"); - - hash = NULL; - len = sizeof(buf_hmac); - ret = pBCryptCreateHash(alg, &hash, buf_hmac, len, (UCHAR *)"key", sizeof("key"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(hash != NULL, "hash not set\n"); - - ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - test_hash_length(hash, 48); - test_alg_name(hash, "SHA384"); - - memset(sha384_hmac, 0, sizeof(sha384_hmac)); - ret = pBCryptFinishHash(hash, sha384_hmac, sizeof(sha384_hmac), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - format_hash( sha384_hmac, sizeof(sha384_hmac), str ); - ok(!strcmp(str, expected_hmac), "got %s\n", str); - - ret = pBCryptDestroyHash(hash); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); -} - -static void test_sha512(void) -{ - static const char expected[] = - "d55ced17163bf5386f2cd9ff21d6fd7fe576a915065c24744d09cfae4ec84ee1e" - "f6ef11bfbc5acce3639bab725b50a1fe2c204f8c820d6d7db0df0ecbc49c5ca"; - static const char expected_hmac[] = - "415fb6b10018ca03b38a1b1399c42ac0be5e8aceddb9a73103f5e543bf2d888f2" - "eecf91373941f9315dd730a77937fa92444450fbece86f409d9cb5ec48c6513"; - BCRYPT_ALG_HANDLE alg; - BCRYPT_HASH_HANDLE hash; - UCHAR buf[512], buf_hmac[1024], sha512[64], sha512_hmac[64]; - ULONG size, len; - char str[129]; - NTSTATUS ret; - - alg = NULL; - ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA512_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(alg != NULL, "alg not set\n"); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(NULL, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_INVALID_HANDLE, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, NULL, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), NULL, 0); - ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, NULL, sizeof(len), &size, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(size == sizeof(len), "got %u\n", size); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, 0, &size, 0); - ok(ret == STATUS_BUFFER_TOO_SMALL, "got %08x\n", ret); - ok(len == 0xdeadbeef, "got %u\n", len); - ok(size == sizeof(len), "got %u\n", size); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len , sizeof(len), &size, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(len != 0xdeadbeef, "len not set\n"); - ok(size == sizeof(len), "got %u\n", size); - - test_hash_length(alg, 64); - test_alg_name(alg, "SHA512"); - - hash = NULL; - len = sizeof(buf); - ret = pBCryptCreateHash(alg, &hash, buf, len, NULL, 0, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(hash != NULL, "hash not set\n"); - - ret = pBCryptHashData(hash, NULL, 0, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - test_hash_length(hash, 64); - test_alg_name(hash, "SHA512"); - - memset(sha512, 0, sizeof(sha512)); - ret = pBCryptFinishHash(hash, sha512, sizeof(sha512), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - format_hash( sha512, sizeof(sha512), str ); - ok(!strcmp(str, expected), "got %s\n", str); - - ret = pBCryptDestroyHash(hash); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - alg = NULL; - ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA512_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(alg != NULL, "alg not set\n"); - - hash = NULL; - len = sizeof(buf_hmac); - ret = pBCryptCreateHash(alg, &hash, buf_hmac, len, (UCHAR *)"key", sizeof("key"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(hash != NULL, "hash not set\n"); - - ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - test_hash_length(hash, 64); - test_alg_name(hash, "SHA512"); - - memset(sha512_hmac, 0, sizeof(sha512_hmac)); - ret = pBCryptFinishHash(hash, sha512_hmac, sizeof(sha512_hmac), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - format_hash( sha512_hmac, sizeof(sha512_hmac), str ); - ok(!strcmp(str, expected_hmac), "got %s\n", str); - - ret = pBCryptDestroyHash(hash); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - -} - -static void test_md5(void) -{ - static const char expected[] = - "e2a3e68d23ce348b8f68b3079de3d4c9"; - static const char expected_hmac[] = - "7bda029b93fa8d817fcc9e13d6bdf092"; - BCRYPT_ALG_HANDLE alg; - BCRYPT_HASH_HANDLE hash; - UCHAR buf[512], buf_hmac[1024], md5[16], md5_hmac[16]; - ULONG size, len; - char str[65]; - NTSTATUS ret; - - alg = NULL; - ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_MD5_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(alg != NULL, "alg not set\n"); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(NULL, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_INVALID_HANDLE, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, NULL, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), NULL, 0); - ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, NULL, sizeof(len), &size, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(size == sizeof(len), "got %u\n", size); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, 0, &size, 0); - ok(ret == STATUS_BUFFER_TOO_SMALL, "got %08x\n", ret); - ok(len == 0xdeadbeef, "got %u\n", len); - ok(size == sizeof(len), "got %u\n", size); - - len = size = 0xdeadbeef; - ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len , sizeof(len), &size, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(len != 0xdeadbeef, "len not set\n"); - ok(size == sizeof(len), "got %u\n", size); - - test_hash_length(alg, 16); - test_alg_name(alg, "MD5"); - - hash = NULL; - len = sizeof(buf); - ret = pBCryptCreateHash(alg, &hash, buf, len, NULL, 0, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(hash != NULL, "hash not set\n"); - - ret = pBCryptHashData(hash, NULL, 0, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - test_hash_length(hash, 16); - test_alg_name(hash, "MD5"); - - memset(md5, 0, sizeof(md5)); - ret = pBCryptFinishHash(hash, md5, sizeof(md5), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - format_hash( md5, sizeof(md5), str ); - ok(!strcmp(str, expected), "got %s\n", str); - - ret = pBCryptDestroyHash(hash); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - alg = NULL; - ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_MD5_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(alg != NULL, "alg not set\n"); - - hash = NULL; - len = sizeof(buf_hmac); - ret = pBCryptCreateHash(alg, &hash, buf_hmac, len, (UCHAR *)"key", sizeof("key"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - ok(hash != NULL, "hash not set\n"); - - ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - test_hash_length(hash, 16); - test_alg_name(hash, "MD5"); - - memset(md5_hmac, 0, sizeof(md5_hmac)); - ret = pBCryptFinishHash(hash, md5_hmac, sizeof(md5_hmac), 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - format_hash( md5_hmac, sizeof(md5_hmac), str ); - ok(!strcmp(str, expected_hmac), "got %s\n", str); - - ret = pBCryptDestroyHash(hash); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); - - ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + static const struct hash_test tests[] = + { + { L"SHA1", 20, + "961fa64958818f767707072755d7018dcd278e94", + "9314f62ff64197143c91fc86de37e9ae776a3fb8", + "2472cf65d0e090618d769d3e46f0d9446cf212da", + "b2d2ba8cfd714d474cf0d9622cc5d15e1f53d53f", + }, + { L"SHA256", 32, + "ceb73749c899693706ede1e30c9929b3fd5dd926163831c2fb8bd41e6efb1126", + "ea0938c118a7b15954f41b85195f2b42aec3a9429c63f593cfa65c137ffaa986", + "34c1aa473a4468a91d06e7cdbc75bc4f93b830ccfc2a47ffd74e8e6ed29e4c72", + "55feb7052060bd99e33f36eb0982c7f4856eb6a84fbefe19a1afd9faafc3af6f", + }, + { L"SHA384", 48, + "62b21e90c9022b101671ba1f808f8631a8149f0f12904055839a35c1ca78ae53" + "63eed1e743a692d70e0504b0cfd12ef9", + "724db7c0bbc51ef1ac3fc793083fc54c0e5c423faec9b11378c01c236b19aaaf" + "a45177ad055feaf003968cc40ece44c7", + "4b3e6d6ff2da121790ab7e7b9247583e3a7eed2db5bd4dabc680303b1608f37d" + "fdc836d96a704c03283bc05b4f6c5eb8", + "03e1818e5c165a0e54619e513acb06c393e1a6cb0ddbb4036b5f29617b334642" + "e6e0be8b214d8508595b17a8c4b4e7db", + }, + { L"SHA512", 64, + "d55ced17163bf5386f2cd9ff21d6fd7fe576a915065c24744d09cfae4ec84ee1" + "ef6ef11bfbc5acce3639bab725b50a1fe2c204f8c820d6d7db0df0ecbc49c5ca", + "7752d707b54d2b00e7d1c09120d189475b0fd2e31ebb988cf0a01fc8492ddc0b" + "3ca9c9ca61d9d7d1fb65ca7665e87f043c1d5bc9f786f8345e951c2d91ac594f", + "415fb6b10018ca03b38a1b1399c42ac0be5e8aceddb9a73103f5e543bf2d888f" + "2eecf91373941f9315dd730a77937fa92444450fbece86f409d9cb5ec48c6513", + "1487bcecba46ae677622fa499e4cb2f0fdf92f6f3427cba76382d537a06e49c3" + "3e70a2fc1fc730092bf21128c3704cc6387f6dfbf7e2f9f315bbb894505a1205", + }, + { L"MD2", 16, + "1bb33606ba908912a84221109d29cd7e", + "b9a6ad9323b17e2d0cd389dddd6ef78a", + "7f05b0638d77f4a27f3a9c4d353cd648", + "05980873e6bfdd05dd7b30078de7e42a", + }, + { L"MD4", 16, + "74b5db93c0b41e36ca7074338fc0b637", + "a14a9ff2059a8c28f47b01e6bc48a1bf", + "bc2e8ac4d8248ed21b8d26227a30ea3a", + "b609db0eb4b8669db74f2c20099701e4", + }, + { L"MD5", 16, + "e2a3e68d23ce348b8f68b3079de3d4c9", + "bcdd7ca574342aa9db0e212348eacb16", + "7bda029b93fa8d817fcc9e13d6bdf092", + "dd636ab8e9592c5088e57c37d44c5bb3", + } + }; + unsigned i; + + for(i = 0; i < ARRAY_SIZE(tests); i++) + test_hash(tests+i); } static void test_BcryptHash(void) @@ -706,36 +469,129 @@ static void test_BcryptHash(void) char str[65]; NTSTATUS ret; + if (!pBCryptHash) /* < Win10 */ + { + win_skip("BCryptHash is not available\n"); + return; + } + alg = NULL; ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_MD5_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); ok(alg != NULL, "alg not set\n"); test_hash_length(alg, 16); - test_alg_name(alg, "MD5"); + test_alg_name(alg, L"MD5"); memset(md5, 0, sizeof(md5)); ret = pBCryptHash(alg, NULL, 0, (UCHAR *)"test", sizeof("test"), md5, sizeof(md5)); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); format_hash( md5, sizeof(md5), str ); ok(!strcmp(str, expected), "got %s\n", str); ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); alg = NULL; memset(md5_hmac, 0, sizeof(md5_hmac)); ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_MD5_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); ok(alg != NULL, "alg not set\n"); ret = pBCryptHash(alg, (UCHAR *)"key", sizeof("key"), (UCHAR *)"test", sizeof("test"), md5_hmac, sizeof(md5_hmac)); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); format_hash( md5_hmac, sizeof(md5_hmac), str ); ok(!strcmp(str, expected_hmac), "got %s\n", str); ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + + if (pBCryptHash) + { + memset(md5, 0, sizeof(md5)); + ret = pBCryptHash(BCRYPT_MD5_ALG_HANDLE, NULL, 0, (UCHAR *)"test", sizeof("test"), md5, sizeof(md5)); + ok(ret == STATUS_SUCCESS, "got 0x%08x\n", ret); + format_hash( md5, sizeof(md5), str ); + ok(!strcmp(str, expected), "got %s\n", str); + } +} + +/* test vectors from RFC 6070 */ +static UCHAR password[] = "password"; +static UCHAR salt[] = "salt"; +static UCHAR long_password[] = "passwordPASSWORDpassword"; +static UCHAR long_salt[] = "saltSALTsaltSALTsaltSALTsaltSALTsalt"; +static UCHAR password_NUL[] = "pass\0word"; +static UCHAR salt_NUL[] = "sa\0lt"; + +static UCHAR dk1[] = "0c60c80f961f0e71f3a9b524af6012062fe037a6"; +static UCHAR dk2[] = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"; +static UCHAR dk3[] = "4b007901b765489abead49d926f721d065a429c1"; +static UCHAR dk4[] = "364dd6bc200ec7d197f1b85f4a61769010717124"; +static UCHAR dk5[] = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"; +static UCHAR dk6[] = "56fa6aa75548099dcc37d7f03425e0c3"; +static UCHAR dk7[] = "8754c32c64b0f524fc50c00f788135de"; + +static const struct +{ + ULONG pwd_len; + ULONG salt_len; + ULONGLONG iterations; + ULONG dk_len; + UCHAR *pwd; + UCHAR *salt; + const UCHAR *dk; +} rfc6070[] = +{ + { 8, 4, 1, 20, password, salt, dk1 }, + { 8, 4, 2, 20, password, salt, dk2 }, + { 8, 4, 4096, 20, password, salt, dk3 }, + { 8, 4, 1000000, 20, password, salt, dk4 }, + { 24, 36, 4096, 25, long_password, long_salt, dk5 }, + { 9, 5, 4096, 16, password_NUL, salt_NUL, dk6 }, + { 8, 0, 1, 16, password, NULL, dk7 } +}; + +static void test_BcryptDeriveKeyPBKDF2(void) +{ + BCRYPT_ALG_HANDLE alg; + UCHAR buf[25]; + char str[51]; + NTSTATUS ret; + ULONG i; + + alg = NULL; + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER, + BCRYPT_ALG_HANDLE_HMAC_FLAG); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(alg != NULL, "alg not set\n"); + + test_hash_length(alg, 20); + test_alg_name(alg, L"SHA1"); + + ret = pBCryptDeriveKeyPBKDF2(alg, rfc6070[0].pwd, rfc6070[0].pwd_len, rfc6070[0].salt, rfc6070[0].salt_len, + 0, buf, rfc6070[0].dk_len, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + for (i = 0; i < ARRAY_SIZE(rfc6070); i++) + { + memset(buf, 0, sizeof(buf)); + ret = pBCryptDeriveKeyPBKDF2(alg, rfc6070[i].pwd, rfc6070[i].pwd_len, rfc6070[i].salt, rfc6070[i].salt_len, + rfc6070[i].iterations, buf, rfc6070[i].dk_len, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + format_hash(buf, rfc6070[i].dk_len, str); + ok(!memcmp(str, rfc6070[i].dk, rfc6070[i].dk_len), "got %s\n", str); + } + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + if (pBCryptHash) + { + ret = pBCryptDeriveKeyPBKDF2(BCRYPT_HMAC_SHA1_ALG_HANDLE, rfc6070[0].pwd, rfc6070[0].pwd_len, rfc6070[0].salt, + rfc6070[0].salt_len, 1, buf, rfc6070[0].dk_len, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + } } static void test_rng(void) @@ -747,22 +603,3924 @@ static void test_rng(void) alg = NULL; ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_RNG_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); ok(alg != NULL, "alg not set\n"); len = size = 0xdeadbeef; ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); - ok(ret == STATUS_NOT_SUPPORTED, "got %08x\n", ret); + ok(ret == STATUS_NOT_SUPPORTED, "got 0x%x\n", ret); - test_alg_name(alg, "RNG"); + len = size = 0xdeadbeef; + ret = pBCryptGetProperty(alg, BCRYPT_HASH_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_NOT_SUPPORTED, "got 0x%x\n", ret); + + test_alg_name(alg, L"RNG"); memset(buf, 0, 16); ret = pBCryptGenRandom(alg, buf, 8, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); ok(memcmp(buf, buf + 8, 8), "got zeroes\n"); ret = pBCryptCloseAlgorithmProvider(alg, 0); - ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); +} + +static void test_aes(void) +{ + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; + BCRYPT_ALG_HANDLE alg; + ULONG size, len; + UCHAR mode[64]; + NTSTATUS ret; + + alg = NULL; + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(alg != NULL, "alg not set\n"); + + len = size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(len, "expected non-zero len\n"); + ok(size == sizeof(len), "got %u\n", size); + + len = size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(len == 16, "got %u\n", len); + ok(size == sizeof(len), "got %u\n", size); + + size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, 0, &size, 0); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 64, "got %u\n", size); + + size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, sizeof(mode) - 1, &size, 0); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 64, "got %u\n", size); + + size = 0; + memset(mode, 0, sizeof(mode)); + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, sizeof(mode), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!lstrcmpW((const WCHAR *)mode, BCRYPT_CHAIN_MODE_CBC), "got %s\n", wine_dbgstr_w((const WCHAR *)mode)); + ok(size == 64, "got %u\n", size); + + size = 0; + memset(&key_lengths, 0, sizeof(key_lengths)); + ret = pBCryptGetProperty(alg, BCRYPT_KEY_LENGTHS, (UCHAR*)&key_lengths, sizeof(key_lengths), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(key_lengths), "got %u\n", size); + ok(key_lengths.dwMinLength == 128, "Expected 128, got %u\n", key_lengths.dwMinLength); + ok(key_lengths.dwMaxLength == 256, "Expected 256, got %u\n", key_lengths.dwMaxLength); + ok(key_lengths.dwIncrement == 64, "Expected 64, got %u\n", key_lengths.dwIncrement); + + ret = pBCryptSetProperty(alg, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_GCM, 0, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + size = 0; + memset(mode, 0, sizeof(mode)); + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, sizeof(mode), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!lstrcmpW((const WCHAR *)mode, BCRYPT_CHAIN_MODE_GCM), "got %s\n", wine_dbgstr_w((const WCHAR *)mode)); + ok(size == 64, "got %u\n", size); + + ret = pBCryptSetProperty(alg, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_CFB, 0, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + size = 0; + memset(mode, 0, sizeof(mode)); + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, sizeof(mode), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!lstrcmpW((const WCHAR *)mode, BCRYPT_CHAIN_MODE_CFB), "got %s\n", wine_dbgstr_w((const WCHAR *)mode)); + ok(size == 64, "got %u\n", size); + + test_alg_name(alg, L"AES"); + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + if (pBCryptHash) + { + ret = pBCryptSetProperty(BCRYPT_AES_CBC_ALG_HANDLE, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_GCM, 0, 0); + ok(ret == STATUS_ACCESS_DENIED, "got 0x%x\n", ret); + + size = 0; + memset(mode, 0, sizeof(mode)); + ret = pBCryptGetProperty(BCRYPT_AES_CBC_ALG_HANDLE, BCRYPT_CHAINING_MODE, mode, sizeof(mode), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!lstrcmpW((const WCHAR *)mode, BCRYPT_CHAIN_MODE_CBC), "got %s\n", wine_dbgstr_w((const WCHAR *)mode)); + ok(size == 64, "got %u\n", size); + } +} + +static void test_3des(void) +{ + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; + BCRYPT_ALG_HANDLE alg; + ULONG size, len; + UCHAR mode[64]; + NTSTATUS ret; + + alg = NULL; + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_3DES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(alg != NULL, "alg not set\n"); + + len = size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(len, "expected non-zero len\n"); + ok(size == sizeof(len), "got %u\n", size); + + len = size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(len == 8, "got %u\n", len); + ok(size == sizeof(len), "got %u\n", size); + + size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, 0, &size, 0); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 64, "got %u\n", size); + + size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, sizeof(mode) - 1, &size, 0); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 64, "got %u\n", size); + + size = 0; + memset(mode, 0, sizeof(mode)); + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, sizeof(mode), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!lstrcmpW((const WCHAR *)mode, BCRYPT_CHAIN_MODE_CBC), "got %s\n", wine_dbgstr_w((const WCHAR *)mode)); + ok(size == 64, "got %u\n", size); + + size = 0; + memset(&key_lengths, 0, sizeof(key_lengths)); + ret = pBCryptGetProperty(alg, BCRYPT_KEY_LENGTHS, (UCHAR*)&key_lengths, sizeof(key_lengths), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(key_lengths), "got %u\n", size); + ok(key_lengths.dwMinLength == 192, "Expected 192, got %u\n", key_lengths.dwMinLength); + ok(key_lengths.dwMaxLength == 192, "Expected 192, got %u\n", key_lengths.dwMaxLength); + ok(key_lengths.dwIncrement == 0, "Expected 0, got %u\n", key_lengths.dwIncrement); + + memcpy(mode, BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM)); + ret = pBCryptSetProperty(alg, BCRYPT_CHAINING_MODE, mode, 0, 0); + ok(ret == STATUS_NOT_SUPPORTED, "got 0x%x\n", ret); + + test_alg_name(alg, L"3DES"); + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); +} + +static void test_BCryptGenerateSymmetricKey(void) +{ + static UCHAR secret[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; + static UCHAR iv[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; + static UCHAR data[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; + static UCHAR expected[] = + {0xc6,0xa1,0x3b,0x37,0x87,0x8f,0x5b,0x82,0x6f,0x4f,0x81,0x62,0xa1,0xc8,0xd8,0x79}; + static UCHAR expected2[] = + {0xb5,0x8a,0x10,0x64,0xd8,0xac,0xa9,0x9b,0xd9,0xb0,0x40,0x5b,0x85,0x45,0xf5,0xbb}; + static UCHAR expected3[] = + {0xe3,0x7c,0xd3,0x63,0xdd,0x7c,0x87,0xa0,0x9a,0xff,0x0e,0x3e,0x60,0xe0,0x9c,0x82}; + BCRYPT_ALG_HANDLE aes; + BCRYPT_KEY_HANDLE key, key2; + UCHAR *buf, ciphertext[16], plaintext[16], ivbuf[16], mode[64]; + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; + ULONG size, len, len2, i; + NTSTATUS ret; + DWORD keylen; + + ret = pBCryptOpenAlgorithmProvider(&aes, BCRYPT_AES_ALGORITHM, NULL, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + len = size = 0xdeadbeef; + ret = pBCryptGetProperty(aes, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + len2 = len; + + key = (void *)0xdeadbeef; + ret = pBCryptGenerateSymmetricKey(NULL, &key, NULL, 0, secret, sizeof(secret), 0); + ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + ok(key == (void *)0xdeadbeef, "got %p\n", key); + + key = NULL; + buf = calloc(1, len); + + key = (BCRYPT_KEY_HANDLE)0xdeadbeef; + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret, 1, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + ok(key == (HANDLE)0xdeadbeef, "got unexpected key %p.\n", key); + + key = (BCRYPT_KEY_HANDLE)0xdeadbeef; + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret, sizeof(secret) + 1, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + ok(key == (HANDLE)0xdeadbeef, "got unexpected key %p.\n", key); + + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret, sizeof(secret), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(key != NULL, "key not set\n"); + + keylen = 0; + ret = pBCryptGetProperty(key, BCRYPT_KEY_STRENGTH, (UCHAR *)&keylen, sizeof(keylen), &size, 0); + ok(!ret, "got 0x%x\n", ret); + ok(size == sizeof(keylen), "got %u\n", size); + ok(keylen == 128, "got %u\n", keylen); + + ret = pBCryptSetProperty(aes, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_CBC, + sizeof(BCRYPT_CHAIN_MODE_CBC), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + todo_wine + { + keylen = 512; + ret = pBCryptSetProperty(aes, BCRYPT_KEY_LENGTH, (UCHAR *)&keylen, sizeof(keylen), 0); + ok(ret == STATUS_NOT_SUPPORTED, "got 0x%x\n", ret); + } + + size = 0; + memset(mode, 0, sizeof(mode)); + ret = pBCryptGetProperty(key, BCRYPT_CHAINING_MODE, mode, sizeof(mode), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!lstrcmpW((const WCHAR *)mode, BCRYPT_CHAIN_MODE_CBC), "got %s\n", wine_dbgstr_w((const WCHAR *)mode)); + ok(size == 64, "got %u\n", size); + + ret = pBCryptSetProperty(key, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_ECB, 0, 0); + ok(ret == STATUS_SUCCESS || broken(ret == STATUS_NOT_SUPPORTED) /* < Win 8 */, "got 0x%x\n", ret); + if (ret == STATUS_SUCCESS) + { + size = 0; + memset(mode, 0, sizeof(mode)); + ret = pBCryptGetProperty(key, BCRYPT_CHAINING_MODE, mode, sizeof(mode), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!lstrcmpW((const WCHAR *)mode, BCRYPT_CHAIN_MODE_ECB), "got %s\n", wine_dbgstr_w((const WCHAR *)mode)); + ok(size == 64, "got %u\n", size); + } + + ret = pBCryptSetProperty(key, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_CBC, + sizeof(BCRYPT_CHAIN_MODE_CBC), 0); + ok(ret == STATUS_SUCCESS || broken(ret == STATUS_NOT_SUPPORTED) /* < Win 8 */, "got 0x%x\n", ret); + + size = 0xdeadbeef; + ret = pBCryptEncrypt(key, NULL, 0, NULL, NULL, 0, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!size, "got %u\n", size); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected, sizeof(expected)), "wrong data\n"); + for (i = 0; i < 16; i++) + ok(ciphertext[i] == expected[i], "%u: %02x != %02x\n", i, ciphertext[i], expected[i]); + ok(!memcmp(ivbuf, ciphertext, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 16, NULL, NULL, 0, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected2, sizeof(expected2)), "wrong data\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ++ivbuf[0]; + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected3, sizeof(expected3)), "wrong data\n"); + for (i = 0; i < 16; i++) + ok(ciphertext[i] == expected3[i], "%u: %02x != %02x\n", i, ciphertext[i], expected3[i]); + ok(!memcmp(ivbuf, ciphertext, sizeof(iv)), "wrong iv data.\n"); + + key2 = (void *)0xdeadbeef; + ret = pBCryptDuplicateKey(NULL, &key2, NULL, 0, 0); + ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + ok(key2 == (void *)0xdeadbeef, "got %p\n", key2); + + if (0) /* crashes on some Windows versions */ + { + ret = pBCryptDuplicateKey(key, NULL, NULL, 0, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + } + + key2 = (void *)0xdeadbeef; + ret = pBCryptDuplicateKey(key, &key2, NULL, 0, 0); + ok(ret == STATUS_SUCCESS || broken(ret == STATUS_INVALID_PARAMETER), "got 0x%x\n", ret); + + if (ret == STATUS_SUCCESS) + { + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key2, data, 16, NULL, ivbuf, 16, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected, sizeof(expected)), "wrong data\n"); + for (i = 0; i < 16; i++) + ok(ciphertext[i] == expected[i], "%u: %02x != %02x\n", i, ciphertext[i], expected[i]); + ok(!memcmp(ivbuf, ciphertext, sizeof(iv)), "wrong iv data.\n"); + + ret = pBCryptDestroyKey(key2); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + } + + size = 0xdeadbeef; + ret = pBCryptDecrypt(key, NULL, 0, NULL, NULL, 0, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!size, "got %u\n", size); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext, 16, NULL, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext, 16, NULL, ivbuf, 16, plaintext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(plaintext, data, sizeof(data)), "wrong data\n"); + ok(!memcmp(ivbuf, ciphertext, sizeof(iv)), "wrong iv data.\n"); + + memset(mode, 0, sizeof(mode)); + ret = pBCryptGetProperty(key, BCRYPT_CHAINING_MODE, mode, sizeof(mode), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!lstrcmpW((const WCHAR *)mode, BCRYPT_CHAIN_MODE_CBC), "wrong mode\n"); + + len = 0; + size = 0; + ret = pBCryptGetProperty(key, BCRYPT_BLOCK_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(len == 16, "got %u\n", len); + ok(size == sizeof(len), "got %u\n", size); + + size = 0; + memset(&key_lengths, 0, sizeof(key_lengths)); + ret = pBCryptGetProperty(aes, BCRYPT_KEY_LENGTHS, (UCHAR*)&key_lengths, sizeof(key_lengths), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(key_lengths), "got %u\n", size); + ok(key_lengths.dwMinLength == 128, "Expected 128, got %u\n", key_lengths.dwMinLength); + ok(key_lengths.dwMaxLength == 256, "Expected 256, got %u\n", key_lengths.dwMaxLength); + ok(key_lengths.dwIncrement == 64, "Expected 64, got %u\n", key_lengths.dwIncrement); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + if (pBCryptHash) + { + ret = pBCryptGenerateSymmetricKey(BCRYPT_AES_CBC_ALG_HANDLE, &key, buf, len2, secret, sizeof(secret), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + } + + free(buf); + ret = pBCryptCloseAlgorithmProvider(aes, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); +} + +#define RACE_TEST_COUNT 200 +static LONG encrypt_race_start_barrier; + +static DWORD WINAPI encrypt_race_thread(void *parameter) +{ + static UCHAR nonce[] = + {0x11,0x20,0x30,0x40,0x50,0x60,0x10,0x20,0x30,0x40,0x50,0x60}; + static UCHAR auth_data[] = + {0x61,0x50,0x40,0x30,0x20,0x10,0x60,0x50,0x40,0x30,0x20,0x10}; + static UCHAR data2[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10}; + static UCHAR expected4[] = + {0xb2,0x27,0x19,0x09,0xc7,0x89,0xdc,0x52,0x24,0x83,0x3a,0x55,0x34,0x76,0x2c,0xbf, + 0x15,0xa1,0xcb,0x40,0x78,0x11,0xba,0xbc,0xa4,0x76,0x69,0x7c,0x75,0x4f,0x11,0xba}; + static UCHAR expected_tag3[] = + {0xef,0xee,0x75,0x99,0xb8,0x12,0xe9,0xf0,0xb4,0xcc,0x65,0x11,0x67,0x60,0x2d,0xe6}; + + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO auth_info; + BCRYPT_KEY_HANDLE key = parameter; + UCHAR ciphertext[48], tag[16]; + unsigned int i, test; + NTSTATUS ret; + ULONG size; + + memset(&auth_info, 0, sizeof(auth_info)); + auth_info.cbSize = sizeof(auth_info); + auth_info.dwInfoVersion = 1; + auth_info.pbNonce = nonce; + auth_info.cbNonce = sizeof(nonce); + auth_info.pbTag = tag; + auth_info.cbTag = sizeof(tag); + auth_info.pbAuthData = auth_data; + auth_info.cbAuthData = sizeof(auth_data); + + InterlockedIncrement(&encrypt_race_start_barrier); + while (InterlockedCompareExchange(&encrypt_race_start_barrier, 3, 2) != 2) + ; + + for (test = 0; test < RACE_TEST_COUNT; ++test) + { + size = 0; + memset(ciphertext, 0xff, sizeof(ciphertext)); + memset(tag, 0xff, sizeof(tag)); + ret = pBCryptEncrypt(key, data2, 32, &auth_info, NULL, 0, ciphertext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ciphertext, expected4, sizeof(expected4)), "wrong data\n"); + ok(!memcmp(tag, expected_tag3, sizeof(expected_tag3)), "wrong tag\n"); + for (i = 0; i < 32; i++) + ok(ciphertext[i] == expected4[i], "%u: %02x != %02x\n", i, ciphertext[i], expected4[i]); + for (i = 0; i < 16; i++) + ok(tag[i] == expected_tag3[i], "%u: %02x != %02x\n", i, tag[i], expected_tag3[i]); + } + + return 0; +} + +static void test_BCryptEncrypt(void) +{ + static UCHAR nonce[] = + {0x10,0x20,0x30,0x40,0x50,0x60,0x10,0x20,0x30,0x40,0x50,0x60}; + static UCHAR auth_data[] = + {0x60,0x50,0x40,0x30,0x20,0x10,0x60,0x50,0x40,0x30,0x20,0x10}; + static UCHAR secret[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; + static UCHAR secret256[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00}; + static UCHAR iv[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; + static UCHAR data[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10}; + static UCHAR data2[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10}; + static UCHAR expected[] = + {0xc6,0xa1,0x3b,0x37,0x87,0x8f,0x5b,0x82,0x6f,0x4f,0x81,0x62,0xa1,0xc8,0xd8,0x79}; + static UCHAR expected2[] = + {0xc6,0xa1,0x3b,0x37,0x87,0x8f,0x5b,0x82,0x6f,0x4f,0x81,0x62,0xa1,0xc8,0xd8,0x79, + 0x28,0x73,0x3d,0xef,0x84,0x8f,0xb0,0xa6,0x5d,0x1a,0x51,0xb7,0xec,0x8f,0xea,0xe9}; + static UCHAR expected3[] = + {0xc6,0xa1,0x3b,0x37,0x87,0x8f,0x5b,0x82,0x6f,0x4f,0x81,0x62,0xa1,0xc8,0xd8,0x79, + 0xb1,0xa2,0x92,0x73,0xbe,0x2c,0x42,0x07,0xa5,0xac,0xe3,0x93,0x39,0x8c,0xb6,0xfb, + 0x87,0x5d,0xea,0xa3,0x7e,0x0f,0xde,0xfa,0xd9,0xec,0x6c,0x4e,0x3c,0x76,0x86,0xe4}; + static UCHAR expected4[] = + {0xe1,0x82,0xc3,0xc0,0x24,0xfb,0x86,0x85,0xf3,0xf1,0x2b,0x7d,0x09,0xb4,0x73,0x67, + 0x86,0x64,0xc3,0xfe,0xa3,0x07,0x61,0xf8,0x16,0xc9,0x78,0x7f,0xe7,0xb1,0xc4,0x94}; + static UCHAR expected5[] = + {0x0a,0x94,0x0b,0xb5,0x41,0x6e,0xf0,0x45,0xf1,0xc3,0x94,0x58,0xc6,0x53,0xea,0x5a}; + static UCHAR expected6[] = + {0x0a,0x94,0x0b,0xb5,0x41,0x6e,0xf0,0x45,0xf1,0xc3,0x94,0x58,0xc6,0x53,0xea,0x5a, + 0x84,0x07,0x66,0xb7,0x49,0xc0,0x9b,0x49,0x74,0x28,0x8c,0x10,0xb9,0xc2,0x09,0x70}; + static UCHAR expected7[] = + {0x0a,0x94,0x0b,0xb5,0x41,0x6e,0xf0,0x45,0xf1,0xc3,0x94,0x58,0xc6,0x53,0xea,0x5a, + 0x95,0x4f,0x64,0xf2,0xe4,0xe8,0x6e,0x9e,0xee,0x82,0xd2,0x02,0x16,0x68,0x48,0x99, + 0x95,0x4f,0x64,0xf2,0xe4,0xe8,0x6e,0x9e,0xee,0x82,0xd2,0x02,0x16,0x68,0x48,0x99}; + static UCHAR expected8[] = + {0xb5,0x8a,0x10,0x64,0xd8,0xac,0xa9,0x9b,0xd9,0xb0,0x40,0x5b,0x85,0x45,0xf5,0xbb}; + static UCHAR expected9[] = + {0x0a,0x94,0x0b,0xb5,0x41,0x6e,0xf0,0x45,0xf1,0xc3,0x94,0x58,0xc6,0x53,0xea,0x5a}; + static UCHAR expected10[] = + {0x66,0xb8,0xbd,0xe5,0x90,0x6c,0xec,0xdf,0xfa,0x8a,0xb2,0xfd,0x92,0x84,0xeb,0xf0, + 0x95,0xc4,0xdf,0xa7,0x7a,0x62,0xe4,0xab,0xd4,0x0e,0x94,0x4e,0xd7,0x6e,0xa1,0x47, + 0x29,0x4b,0x37,0xfe,0x28,0x6d,0x5f,0x69,0x46,0x30,0x73,0xc0,0xaa,0x42,0xe4,0x46}; + static UCHAR expected11[] = + {0x0a,0x22,0xf7,0x96,0xe1,0xb9,0x3e,0x90,0x32,0xcf,0xf8,0x04,0x83,0x8a,0xdf,0xc3}; + static UCHAR expected12[] = + {0xb5,0x9d,0xe7,0x8e,0x0f,0x80,0x16,0xc3,0xf6,0x80,0xad,0xf8,0xac,0xef,0xf9,0x9f}; + static UCHAR expected13[] = + {0xc6,0x41,0x37,0xd8,0x6f,0x65,0x64,0x1d,0x2b,0xa0,0xad,0xee,0x67,0xba,0x9b,0x5f}; + static UCHAR expected14[] = + {0x0a,0x22,0xf7,0x96,0xe1,0xb9,0x3e,0x90,0x32,0xcf,0xf8,0x04,0x83,0x8a,0xdf,0xc3, + 0xa5,0xfa,0x54,0xd7,0x62,0x9d,0x8b,0x11,0x3d,0xd8,0xe2,0x95,0xc2,0x23,0x3f,0x29}; + static UCHAR expected15[] = + {0x0a,0x22,0xf7,0x96,0xe1,0xb9,0x3e,0x90,0x32,0xcf,0xf8,0x04,0x83,0x8a,0xdf,0xc3, + 0xa5,0xe5,0xcd,0x29,0x48,0x37,0x3f,0x92,0x6f,0x09,0x84,0x5a,0xb8,0x88,0xda,0xfd, + 0x65,0x00,0x20,0x9a,0xc6,0xdb,0xfa,0x82,0xdb,0xbc,0xf4,0x70,0x7d,0x88,0x86,0xb5}; + static UCHAR expected16[] = + {0x64,0xff,0xc6,0x97,0x14,0x5e,0x21,0xfa,0xe8,0x6e,0xba,0xc1,0x0c,0xb1,0x72,0x64, + 0x5d,0xf0,0x22,0x72,0xef,0x39,0xe3,0x2f,0x80,0x3e,0x53,0x00,0xc1,0xcf,0x3f,0xbb, + 0xea,0x30,0x01,0x78,0x3b,0xba,0x4d,0xca,0xa0,0x2e,0x83,0x5e,0x0e,0xe2,0x65,0xcb}; + static UCHAR expected_tag[] = + {0x89,0xb3,0x92,0x00,0x39,0x20,0x09,0xb4,0x6a,0xd6,0xaf,0xca,0x4b,0x5b,0xfd,0xd0}; + static UCHAR expected_tag2[] = + {0x9a,0x92,0x32,0x2c,0x61,0x2a,0xae,0xef,0x66,0x2a,0xfb,0x55,0xe9,0x48,0xdf,0xbd}; + static UCHAR expected_tag3[] = + {0x17,0x9d,0xc0,0x7a,0xf0,0xcf,0xaa,0xd5,0x1c,0x11,0xc4,0x4b,0xd6,0xa3,0x3e,0x77}; + static UCHAR expected_tag4[] = + {0x4c,0x42,0x83,0x9e,0x8d,0x40,0xf1,0x19,0xd6,0x2b,0x1c,0x66,0x03,0x2b,0x39,0x63}; + + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO auth_info; + UCHAR *buf, ciphertext[48], ivbuf[16], tag[16]; + BCRYPT_AUTH_TAG_LENGTHS_STRUCT tag_length; + ULONG size, len, i, test; + BCRYPT_ALG_HANDLE aes; + BCRYPT_KEY_HANDLE key; + HANDLE hthread; + NTSTATUS ret; + + ret = pBCryptOpenAlgorithmProvider(&aes, BCRYPT_AES_ALGORITHM, NULL, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + /****************** + * AES - CBC mode * + ******************/ + + len = 0xdeadbeef; + size = sizeof(len); + ret = pBCryptGetProperty(aes, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + key = NULL; + buf = calloc(1, len); + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret, sizeof(secret), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(key != NULL, "key not set\n"); + + /* input size is a multiple of block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected, sizeof(expected)), "wrong data\n"); + for (i = 0; i < 16; i++) + ok(ciphertext[i] == expected[i], "%u: %02x != %02x\n", i, ciphertext[i], expected[i]); + ok(!memcmp(ivbuf, ciphertext, sizeof(iv)), "wrong iv data.\n"); + + /* NULL initialization vector */ + size = 0; + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 16, NULL, NULL, 0, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected8, sizeof(expected8)), "wrong data\n"); + + /* all zero initialization vector */ + size = 0; + memset(ciphertext, 0, sizeof(ciphertext)); + memset(ivbuf, 0, sizeof(ivbuf)); + ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected9, sizeof(expected9)), "wrong data\n"); + for (i = 0; i < 16; i++) + ok(ciphertext[i] == expected9[i], "%u: %02x != %02x\n", i, ciphertext[i], expected9[i]); + ok(!memcmp(ivbuf, ciphertext, sizeof(iv)), "wrong iv data.\n"); + + /* input size is not a multiple of block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data, 17, NULL, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_INVALID_BUFFER_SIZE, "got 0x%x\n", ret); + ok(size == 17, "got %u\n", size); + + /* input size is not a multiple of block size, block padding set */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data, 17, NULL, ivbuf, 16, NULL, 0, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 17, NULL, ivbuf, 16, ciphertext, 32, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ciphertext, expected2, sizeof(expected2)), "wrong data\n"); + for (i = 0; i < 32; i++) + ok(ciphertext[i] == expected2[i], "%u: %02x != %02x\n", i, ciphertext[i], expected2[i]); + ok(!memcmp(ivbuf, ciphertext + 32 - 16, sizeof(iv)), "wrong iv data.\n"); + + /* input size is a multiple of block size, block padding set */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data2, 32, NULL, ivbuf, 16, NULL, 0, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data2, 32, NULL, ivbuf, 16, ciphertext, 48, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + ok(!memcmp(ciphertext, expected3, sizeof(expected3)), "wrong data\n"); + for (i = 0; i < 48; i++) + ok(ciphertext[i] == expected3[i], "%u: %02x != %02x\n", i, ciphertext[i], expected3[i]); + ok(!memcmp(ivbuf, ciphertext + 48 - 16, sizeof(iv)), "wrong iv data.\n"); + + /* output size too small */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 17, NULL, ivbuf, 16, ciphertext, 31, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data2, 32, NULL, ivbuf, 16, ciphertext, 32, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + free(buf); + + /* 256 bit key */ + buf = calloc(1, len); + + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret256, sizeof(secret256), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + /* Key generations succeeds if the key size exceeds maximum and uses maximum key length + * from secret. */ + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret256, sizeof(secret256) + 1, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data2, 32, NULL, ivbuf, 16, NULL, 0, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data2, 32, NULL, ivbuf, 16, ciphertext, 48, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + ok(!memcmp(ciphertext, expected10, sizeof(expected10)), "wrong data\n"); + for (i = 0; i < 48; i++) + ok(ciphertext[i] == expected10[i], "%u: %02x != %02x\n", i, ciphertext[i], expected10[i]); + ok(!memcmp(ivbuf, ciphertext + 48 - 16, sizeof(iv)), "wrong iv data.\n"); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + free(buf); + + /****************** + * AES - GCM mode * + ******************/ + + size = 0; + ret = pBCryptGetProperty(aes, BCRYPT_AUTH_TAG_LENGTH, NULL, 0, &size, 0); + ok(ret == STATUS_NOT_SUPPORTED, "got 0x%x\n", ret); + + ret = pBCryptSetProperty(aes, BCRYPT_CHAINING_MODE, (UCHAR*)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + size = 0; + ret = pBCryptGetProperty(aes, BCRYPT_AUTH_TAG_LENGTH, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(tag_length), "got %u\n", size); + + size = 0; + memset(&tag_length, 0, sizeof(tag_length)); + ret = pBCryptGetProperty(aes, BCRYPT_AUTH_TAG_LENGTH, (UCHAR*)&tag_length, sizeof(tag_length), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(tag_length), "got %u\n", size); + ok(tag_length.dwMinLength == 12, "Expected 12, got %u\n", tag_length.dwMinLength); + ok(tag_length.dwMaxLength == 16, "Expected 16, got %u\n", tag_length.dwMaxLength); + ok(tag_length.dwIncrement == 1, "Expected 1, got %u\n", tag_length.dwIncrement); + + len = 0xdeadbeef; + size = sizeof(len); + ret = pBCryptGetProperty(aes, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + key = NULL; + buf = calloc(1, len); + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret, sizeof(secret), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(key != NULL, "key not set\n"); + + ret = pBCryptGetProperty(key, BCRYPT_AUTH_TAG_LENGTH, (UCHAR*)&tag_length, sizeof(tag_length), &size, 0); + ok(ret == STATUS_NOT_SUPPORTED, "got 0x%x\n", ret); + + memset(&auth_info, 0, sizeof(auth_info)); + auth_info.cbSize = sizeof(auth_info); + auth_info.dwInfoVersion = 1; + auth_info.pbNonce = nonce; + auth_info.cbNonce = sizeof(nonce); + auth_info.pbTag = tag; + auth_info.cbTag = sizeof(tag); + + /* input size is a multiple of block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0xff, sizeof(ciphertext)); + memset(tag, 0xff, sizeof(tag)); + ret = pBCryptEncrypt(key, data2, 32, &auth_info, ivbuf, 16, ciphertext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ciphertext, expected4, sizeof(expected4)), "wrong data\n"); + ok(!memcmp(tag, expected_tag, sizeof(expected_tag)), "wrong tag\n"); + for (i = 0; i < 32; i++) + ok(ciphertext[i] == expected4[i], "%u: %02x != %02x\n", i, ciphertext[i], expected4[i]); + for (i = 0; i < 16; i++) + ok(tag[i] == expected_tag[i], "%u: %02x != %02x\n", i, tag[i], expected_tag[i]); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + /* NULL initialization vector */ + size = 0; + memset(ciphertext, 0xff, sizeof(ciphertext)); + memset(tag, 0xff, sizeof(tag)); + ret = pBCryptEncrypt(key, data2, 32, &auth_info, NULL, 0, ciphertext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ciphertext, expected4, sizeof(expected4)), "wrong data\n"); + ok(!memcmp(tag, expected_tag, sizeof(expected_tag)), "wrong tag\n"); + for (i = 0; i < 32; i++) + ok(ciphertext[i] == expected4[i], "%u: %02x != %02x\n", i, ciphertext[i], expected4[i]); + for (i = 0; i < 16; i++) + ok(tag[i] == expected_tag[i], "%u: %02x != %02x\n", i, tag[i], expected_tag[i]); + + /* all zero initialization vector */ + size = 0; + memset(ciphertext, 0xff, sizeof(ciphertext)); + memset(tag, 0xff, sizeof(tag)); + memset(ivbuf, 0, sizeof(ivbuf)); + ret = pBCryptEncrypt(key, data2, 32, &auth_info, ivbuf, 16, ciphertext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ciphertext, expected4, sizeof(expected4)), "wrong data\n"); + ok(!memcmp(tag, expected_tag, sizeof(expected_tag)), "wrong tag\n"); + for (i = 0; i < 32; i++) + ok(ciphertext[i] == expected4[i], "%u: %02x != %02x\n", i, ciphertext[i], expected4[i]); + for (i = 0; i < 16; i++) + ok(tag[i] == expected_tag[i], "%u: %02x != %02x\n", i, tag[i], expected_tag[i]); + memset(ciphertext, 0, sizeof(iv)); + ok(!memcmp(ivbuf, ciphertext, sizeof(iv)), "wrong iv data.\n"); + + /* input size is not multiple of block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0xff, sizeof(ciphertext)); + memset(tag, 0xff, sizeof(tag)); + ret = pBCryptEncrypt(key, data2, 24, &auth_info, ivbuf, 16, ciphertext, 24, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 24, "got %u\n", size); + ok(!memcmp(ciphertext, expected4, 24), "wrong data\n"); + ok(!memcmp(tag, expected_tag2, sizeof(expected_tag2)), "wrong tag\n"); + for (i = 0; i < 24; i++) + ok(ciphertext[i] == expected4[i], "%u: %02x != %02x\n", i, ciphertext[i], expected4[i]); + for (i = 0; i < 16; i++) + ok(tag[i] == expected_tag2[i], "%u: %02x != %02x\n", i, tag[i], expected_tag2[i]); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + /* test with auth data */ + auth_info.pbAuthData = auth_data; + auth_info.cbAuthData = sizeof(auth_data); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0xff, sizeof(ciphertext)); + memset(tag, 0xff, sizeof(tag)); + ret = pBCryptEncrypt(key, data2, 32, &auth_info, ivbuf, 16, ciphertext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ciphertext, expected4, sizeof(expected4)), "wrong data\n"); + ok(!memcmp(tag, expected_tag3, sizeof(expected_tag3)), "wrong tag\n"); + for (i = 0; i < 32; i++) + ok(ciphertext[i] == expected4[i], "%u: %02x != %02x\n", i, ciphertext[i], expected4[i]); + for (i = 0; i < 16; i++) + ok(tag[i] == expected_tag3[i], "%u: %02x != %02x\n", i, tag[i], expected_tag3[i]); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + memset(tag, 0xff, sizeof(tag)); + ret = pBCryptEncrypt(key, data2, 0, &auth_info, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!size, "got %u\n", size); + for (i = 0; i < 16; i++) + ok(tag[i] == 0xff, "%u: %02x != %02x\n", i, tag[i], 0xff); + + memset(tag, 0xff, sizeof(tag)); + ret = pBCryptEncrypt(key, NULL, 0, &auth_info, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!size, "got %u\n", size); + ok(!memcmp(tag, expected_tag4, sizeof(expected_tag4)), "wrong tag\n"); + for (i = 0; i < 16; i++) + ok(tag[i] == expected_tag4[i], "%u: %02x != %02x\n", i, tag[i], expected_tag4[i]); + + /* test with padding */ + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data2, 32, &auth_info, ivbuf, 16, ciphertext, 32, &size, BCRYPT_BLOCK_PADDING); + todo_wine ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data2, 32, &auth_info, ivbuf, 16, ciphertext, 48, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + /* race test */ + + encrypt_race_start_barrier = 0; + hthread = CreateThread(NULL, 0, encrypt_race_thread, key, 0, NULL); + + while (InterlockedCompareExchange(&encrypt_race_start_barrier, 2, 1) != 1) + ; + + for (test = 0; test < RACE_TEST_COUNT; ++test) + { + size = 0; + memset(ciphertext, 0xff, sizeof(ciphertext)); + memset(tag, 0xff, sizeof(tag)); + ret = pBCryptEncrypt(key, data2, 32, &auth_info, NULL, 0, ciphertext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ciphertext, expected4, sizeof(expected4)), "wrong data\n"); + ok(!memcmp(tag, expected_tag3, sizeof(expected_tag2)), "wrong tag\n"); + for (i = 0; i < 32; i++) + ok(ciphertext[i] == expected4[i], "%u: %02x != %02x\n", i, ciphertext[i], expected4[i]); + for (i = 0; i < 16; i++) + ok(tag[i] == expected_tag3[i], "%u: %02x != %02x\n", i, tag[i], expected_tag3[i]); + } + + WaitForSingleObject(hthread, INFINITE); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + free(buf); + + /****************** + * AES - ECB mode * + ******************/ + + ret = pBCryptSetProperty(aes, BCRYPT_CHAINING_MODE, (UCHAR*)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + len = 0xdeadbeef; + size = sizeof(len); + ret = pBCryptGetProperty(aes, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + buf = calloc(1, len); + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret, sizeof(secret), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + /* initialization vector is not allowed */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, ciphertext, 16, &size, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + + /* input size is a multiple of block size */ + size = 0; + ret = pBCryptEncrypt(key, data, 16, NULL, NULL, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + + size = 0; + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 16, NULL, NULL, 16, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected5, sizeof(expected5)), "wrong data\n"); + for (i = 0; i < 16; i++) + ok(ciphertext[i] == expected5[i], "%u: %02x != %02x\n", i, ciphertext[i], expected5[i]); + + /* input size is not a multiple of block size */ + size = 0; + ret = pBCryptEncrypt(key, data, 17, NULL, NULL, 16, NULL, 0, &size, 0); + ok(ret == STATUS_INVALID_BUFFER_SIZE, "got 0x%x\n", ret); + ok(size == 17, "got %u\n", size); + + /* input size is not a multiple of block size, block padding set */ + size = 0; + ret = pBCryptEncrypt(key, data, 17, NULL, NULL, 16, NULL, 0, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + + size = 0; + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 17, NULL, NULL, 16, ciphertext, 32, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ciphertext, expected6, sizeof(expected6)), "wrong data\n"); + for (i = 0; i < 32; i++) + ok(ciphertext[i] == expected6[i], "%u: %02x != %02x\n", i, ciphertext[i], expected6[i]); + + /* input size is a multiple of block size, block padding set */ + size = 0; + ret = pBCryptEncrypt(key, data2, 32, NULL, NULL, 16, NULL, 0, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + + size = 0; + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data2, 32, NULL, NULL, 16, ciphertext, 48, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + ok(!memcmp(ciphertext, expected7, sizeof(expected7)), "wrong data\n"); + for (i = 0; i < 48; i++) + ok(ciphertext[i] == expected7[i], "%u: %02x != %02x\n", i, ciphertext[i], expected7[i]); + + /* output size too small */ + size = 0; + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 17, NULL, NULL, 16, ciphertext, 31, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + + size = 0; + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data2, 32, NULL, NULL, 16, ciphertext, 32, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + free(buf); + + /****************** + * AES - CFB mode * + ******************/ + + ret = pBCryptSetProperty(aes, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_CFB, sizeof(BCRYPT_CHAIN_MODE_CFB), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + key = NULL; + buf = calloc(1, len); + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret, sizeof(secret), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(key != NULL, "key not set\n"); + + /* input size is a multiple of block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected11, sizeof(expected11)), "wrong data\n"); + for (i = 0; i < 16; i++) + ok(ciphertext[i] == expected11[i], "%u: %02x != %02x\n", i, ciphertext[i], expected11[i]); + ok(!memcmp(ivbuf, ciphertext, sizeof(iv)), "wrong iv data.\n"); + + /* NULL initialization vector */ + size = 0; + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 16, NULL, NULL, 0, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected12, sizeof(expected12)), "wrong data\n"); + + /* all zero initialization vector */ + size = 0; + memset(ciphertext, 0, sizeof(ciphertext)); + memset(ivbuf, 0, sizeof(ivbuf)); + ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, ciphertext, 16, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(ciphertext, expected13, sizeof(expected13)), "wrong data\n"); + for (i = 0; i < 16; i++) + ok(ciphertext[i] == expected13[i], "%u: %02x != %02x\n", i, ciphertext[i], expected13[i]); + ok(!memcmp(ivbuf, ciphertext, sizeof(iv)), "wrong iv data.\n"); + + /* input size is not a multiple of block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data, 17, NULL, ivbuf, 16, NULL, 0, &size, 0); + todo_wine ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 17, "got %u\n", size); + + /* input size is not a multiple of block size, block padding set */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data, 17, NULL, ivbuf, 16, NULL, 0, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 17, NULL, ivbuf, 16, ciphertext, 32, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ciphertext, expected14, sizeof(expected14)), "wrong data\n"); + for (i = 0; i < 32; i++) + ok(ciphertext[i] == expected14[i], "%u: %02x != %02x\n", i, ciphertext[i], expected14[i]); + ok(!memcmp(ivbuf, ciphertext + 32 - 16, sizeof(iv)), "wrong iv data.\n"); + + /* input size is a multiple of block size, block padding set */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data2, 32, NULL, ivbuf, 16, NULL, 0, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data2, 32, NULL, ivbuf, 16, ciphertext, 48, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + ok(!memcmp(ciphertext, expected15, sizeof(expected15)), "wrong data\n"); + for (i = 0; i < 48; i++) + ok(ciphertext[i] == expected15[i], "%u: %02x != %02x\n", i, ciphertext[i], expected15[i]); + ok(!memcmp(ivbuf, ciphertext + 48 - 16, sizeof(iv)), "wrong iv data.\n"); + + /* output size too small */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data, 17, NULL, ivbuf, 16, ciphertext, 31, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data2, 32, NULL, ivbuf, 16, ciphertext, 32, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + free(buf); + + /* 256 bit key */ + buf = calloc(1, len); + + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret256, sizeof(secret256), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + /* Key generations succeeds if the key size exceeds maximum and uses maximum key length + * from secret. */ + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret256, sizeof(secret256) + 1, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptEncrypt(key, data2, 32, NULL, ivbuf, 16, NULL, 0, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(ciphertext, 0, sizeof(ciphertext)); + ret = pBCryptEncrypt(key, data2, 32, NULL, ivbuf, 16, ciphertext, 48, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + ok(!memcmp(ciphertext, expected16, sizeof(expected16)), "wrong data\n"); + for (i = 0; i < 48; i++) + ok(ciphertext[i] == expected16[i], "%u: %02x != %02x\n", i, ciphertext[i], expected16[i]); + ok(!memcmp(ivbuf, ciphertext + 48 - 16, sizeof(iv)), "wrong iv data.\n"); + + ret = pBCryptCloseAlgorithmProvider(aes, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); +} + +static void test_BCryptDecrypt(void) +{ + static UCHAR nonce[] = + {0x10,0x20,0x30,0x40,0x50,0x60,0x10,0x20,0x30,0x40,0x50,0x60}; + static UCHAR auth_data[] = + {0x60,0x50,0x40,0x30,0x20,0x10,0x60,0x50,0x40,0x30,0x20,0x10}; + static UCHAR secret[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; + static UCHAR iv[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; + static UCHAR expected[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; + static UCHAR expected2[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10}; + static UCHAR expected3[] = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10}; + static UCHAR expected4[] = + {0x28,0x73,0x3d,0xef,0x84,0x8f,0xb0,0xa6,0x5d,0x1a,0x51,0xb7,0xec,0x8f,0xea,0xe9, + 0x10,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f}; + static UCHAR expected5[] = + {0x29,0x73,0x3d,0xef,0x84,0x8f,0xb0,0xa6,0x5d,0x1a,0x51,0xb7,0xec,0x8f,0xea,0xe9, + 0x10,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f}; + static UCHAR ciphertext[32] = + {0xc6,0xa1,0x3b,0x37,0x87,0x8f,0x5b,0x82,0x6f,0x4f,0x81,0x62,0xa1,0xc8,0xd8,0x79, + 0x28,0x73,0x3d,0xef,0x84,0x8f,0xb0,0xa6,0x5d,0x1a,0x51,0xb7,0xec,0x8f,0xea,0xe9}; + static UCHAR ciphertext2[] = + {0xc6,0xa1,0x3b,0x37,0x87,0x8f,0x5b,0x82,0x6f,0x4f,0x81,0x62,0xa1,0xc8,0xd8,0x79, + 0x28,0x73,0x3d,0xef,0x84,0x8f,0xb0,0xa6,0x5d,0x1a,0x51,0xb7,0xec,0x8f,0xea,0xe9}; + static UCHAR ciphertext3[] = + {0xc6,0xa1,0x3b,0x37,0x87,0x8f,0x5b,0x82,0x6f,0x4f,0x81,0x62,0xa1,0xc8,0xd8,0x79, + 0xb1,0xa2,0x92,0x73,0xbe,0x2c,0x42,0x07,0xa5,0xac,0xe3,0x93,0x39,0x8c,0xb6,0xfb, + 0x87,0x5d,0xea,0xa3,0x7e,0x0f,0xde,0xfa,0xd9,0xec,0x6c,0x4e,0x3c,0x76,0x86,0xe4}; + static UCHAR ciphertext4[] = + {0xe1,0x82,0xc3,0xc0,0x24,0xfb,0x86,0x85,0xf3,0xf1,0x2b,0x7d,0x09,0xb4,0x73,0x67, + 0x86,0x64,0xc3,0xfe,0xa3,0x07,0x61,0xf8,0x16,0xc9,0x78,0x7f,0xe7,0xb1,0xc4,0x94}; + static UCHAR ciphertext5[] = + {0x0a,0x94,0x0b,0xb5,0x41,0x6e,0xf0,0x45,0xf1,0xc3,0x94,0x58,0xc6,0x53,0xea,0x5a, + 0x84,0x07,0x66,0xb7,0x49,0xc0,0x9b,0x49,0x74,0x28,0x8c,0x10,0xb9,0xc2,0x09,0x70}; + static UCHAR ciphertext6[] = + {0x0a,0x94,0x0b,0xb5,0x41,0x6e,0xf0,0x45,0xf1,0xc3,0x94,0x58,0xc6,0x53,0xea,0x5a, + 0x95,0x4f,0x64,0xf2,0xe4,0xe8,0x6e,0x9e,0xee,0x82,0xd2,0x02,0x16,0x68,0x48,0x99, + 0x95,0x4f,0x64,0xf2,0xe4,0xe8,0x6e,0x9e,0xee,0x82,0xd2,0x02,0x16,0x68,0x48,0x99}; + static UCHAR tag[] = + {0x89,0xb3,0x92,0x00,0x39,0x20,0x09,0xb4,0x6a,0xd6,0xaf,0xca,0x4b,0x5b,0xfd,0xd0}; + static UCHAR tag2[] = + {0x17,0x9d,0xc0,0x7a,0xf0,0xcf,0xaa,0xd5,0x1c,0x11,0xc4,0x4b,0xd6,0xa3,0x3e,0x77}; + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO auth_info; + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; + BCRYPT_AUTH_TAG_LENGTHS_STRUCT tag_lengths; + BCRYPT_ALG_HANDLE aes; + BCRYPT_KEY_HANDLE key; + UCHAR *buf, plaintext[48], ivbuf[16]; + ULONG size, len; + NTSTATUS ret; + + ret = pBCryptOpenAlgorithmProvider(&aes, BCRYPT_AES_ALGORITHM, NULL, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + size = 0; + memset(&key_lengths, 0, sizeof(key_lengths)); + ret = pBCryptGetProperty(aes, BCRYPT_KEY_LENGTHS, (UCHAR*)&key_lengths, sizeof(key_lengths), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(key_lengths), "got %u\n", size); + ok(key_lengths.dwMinLength == 128, "Expected 128, got %u\n", key_lengths.dwMinLength); + ok(key_lengths.dwMaxLength == 256, "Expected 256, got %u\n", key_lengths.dwMaxLength); + ok(key_lengths.dwIncrement == 64, "Expected 64, got %u\n", key_lengths.dwIncrement); + + /****************** + * AES - CBC mode * + ******************/ + + len = 0xdeadbeef; + size = sizeof(len); + ret = pBCryptGetProperty(aes, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + key = NULL; + buf = calloc(1, len); + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret, sizeof(secret), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(key != NULL, "key not set\n"); + + /* input size is a multiple of block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext, 32, NULL, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext, 32, NULL, ivbuf, 16, plaintext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(plaintext, expected, sizeof(expected)), "wrong data\n"); + ok(!memcmp(ivbuf, ciphertext + size - sizeof(iv), sizeof(iv)), "wrong iv data.\n"); + + size = 0; + ++ivbuf[0]; + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext, 32, NULL, ivbuf, 16, plaintext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(plaintext, expected5, sizeof(expected)), "wrong data\n"); + ok(!memcmp(ivbuf, ciphertext + 32 - 16, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext, 32, NULL, NULL, 0, plaintext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(plaintext, expected4, sizeof(expected4)), "wrong data\n"); + + /* test with padding smaller than block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext2, 32, NULL, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext2, 32, NULL, ivbuf, 16, plaintext, 17, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 17, "got %u\n", size); + ok(!memcmp(plaintext, expected2, sizeof(expected2)), "wrong data\n"); + ok(!memcmp(ivbuf, ciphertext2 + 32 - sizeof(iv), sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext2, 32, NULL, ivbuf, 16, plaintext, 17, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 17, "got %u\n", size); + ok(!memcmp(plaintext, expected4, size), "wrong data\n"); + ok(!memcmp(ivbuf, ciphertext2 + 32 - 16, sizeof(iv)), "wrong iv data.\n"); + + /* test with padding of block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext3, 48, NULL, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext3, 48, NULL, ivbuf, 16, plaintext, 32, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(plaintext, expected3, sizeof(expected3)), "wrong data\n"); + ok(!memcmp(ivbuf, ciphertext3 + 48 - 16, sizeof(iv)), "wrong iv data.\n"); + + /* output size too small */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext, 32, NULL, ivbuf, 16, plaintext, 31, &size, 0); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext2, 32, NULL, ivbuf, 16, plaintext, 15, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext2, 32, NULL, ivbuf, 16, plaintext, 16, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 17, "got %u\n", size); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext3, 48, NULL, ivbuf, 16, plaintext, 31, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + + /* input size is not a multiple of block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext, 17, NULL, ivbuf, 16, NULL, 0, &size, 0); + ok(ret == STATUS_INVALID_BUFFER_SIZE, "got 0x%x\n", ret); + ok(size == 17 || broken(size == 0 /* Win < 7 */), "got %u\n", size); + + /* input size is not a multiple of block size, block padding set */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext, 17, NULL, ivbuf, 16, NULL, 0, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_INVALID_BUFFER_SIZE, "got 0x%x\n", ret); + ok(size == 17 || broken(size == 0 /* Win < 7 */), "got %u\n", size); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + free(buf); + + /****************** + * AES - GCM mode * + ******************/ + + ret = pBCryptSetProperty(aes, BCRYPT_CHAINING_MODE, (UCHAR*)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + key = NULL; + buf = calloc(1, len); + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret, sizeof(secret), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(key != NULL, "key not set\n"); + + ret = pBCryptGetProperty(key, BCRYPT_AUTH_TAG_LENGTH, (UCHAR*)&tag_lengths, sizeof(tag_lengths), &size, 0); + ok(ret == STATUS_NOT_SUPPORTED, "got 0x%x\n", ret); + + memset(&auth_info, 0, sizeof(auth_info)); + auth_info.cbSize = sizeof(auth_info); + auth_info.dwInfoVersion = 1; + auth_info.pbNonce = nonce; + auth_info.cbNonce = sizeof(nonce); + auth_info.pbTag = tag; + auth_info.cbTag = sizeof(tag); + + /* input size is a multiple of block size */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext4, 32, &auth_info, ivbuf, 16, plaintext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(plaintext, expected3, sizeof(expected3)), "wrong data\n"); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv.\n"); + + size = 0; + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext4, 32, &auth_info, NULL, 0, plaintext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(plaintext, expected3, sizeof(expected3)), "wrong data\n"); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ++ivbuf[0]; + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext4, 32, &auth_info, ivbuf, 16, plaintext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(plaintext, expected3, sizeof(expected3)), "wrong data\n"); + ok(!memcmp(ivbuf + 1, iv + 1, sizeof(iv) - 1), "wrong iv data.\n"); + ok(ivbuf[0] == iv[0] + 1, "wrong iv data.\n"); + + /* test with auth data */ + auth_info.pbAuthData = auth_data; + auth_info.cbAuthData = sizeof(auth_data); + auth_info.pbTag = tag2; + auth_info.cbTag = sizeof(tag2); + + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext4, 32, &auth_info, ivbuf, 16, plaintext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(plaintext, expected3, sizeof(expected3)), "wrong data\n"); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv.\n"); + + /* test with wrong tag */ + memcpy(ivbuf, iv, sizeof(iv)); + auth_info.pbTag = iv; /* wrong tag */ + ret = pBCryptDecrypt(key, ciphertext4, 32, &auth_info, ivbuf, 16, plaintext, 32, &size, 0); + ok(ret == STATUS_AUTH_TAG_MISMATCH, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(ivbuf, iv, sizeof(iv)), "wrong iv data.\n"); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + free(buf); + + /****************** + * AES - ECB mode * + ******************/ + + ret = pBCryptSetProperty(aes, BCRYPT_CHAINING_MODE, (UCHAR*)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + len = 0xdeadbeef; + size = sizeof(len); + ret = pBCryptGetProperty(aes, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + buf = calloc(1, len); + ret = pBCryptGenerateSymmetricKey(aes, &key, buf, len, secret, sizeof(secret), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + /* initialization vector is not allowed */ + size = 0; + memcpy(ivbuf, iv, sizeof(iv)); + ret = pBCryptDecrypt(key, ciphertext5, 32, NULL, ivbuf, 16, plaintext, 32, &size, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + + /* input size is a multiple of block size */ + size = 0; + ret = pBCryptDecrypt(key, ciphertext5, 32, NULL, NULL, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + + size = 0; + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext5, 32, NULL, NULL, 16, plaintext, 32, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(plaintext, expected, sizeof(expected)), "wrong data\n"); + + /* test with padding smaller than block size */ + size = 0; + ret = pBCryptDecrypt(key, ciphertext5, 32, NULL, NULL, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + + size = 0; + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext5, 32, NULL, NULL, 16, plaintext, 17, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 17, "got %u\n", size); + ok(!memcmp(plaintext, expected2, sizeof(expected2)), "wrong data\n"); + + /* test with padding of block size */ + size = 0; + ret = pBCryptDecrypt(key, ciphertext6, 48, NULL, NULL, 16, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + + size = 0; + memset(plaintext, 0, sizeof(plaintext)); + ret = pBCryptDecrypt(key, ciphertext6, 48, NULL, NULL, 16, plaintext, 32, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + ok(!memcmp(plaintext, expected3, sizeof(expected3)), "wrong data\n"); + + /* output size too small */ + size = 0; + ret = pBCryptDecrypt(key, ciphertext4, 32, NULL, NULL, 16, plaintext, 31, &size, 0); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + + size = 0; + ret = pBCryptDecrypt(key, ciphertext5, 32, NULL, NULL, 16, plaintext, 15, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 32, "got %u\n", size); + + size = 0; + ret = pBCryptDecrypt(key, ciphertext5, 32, NULL, NULL, 16, plaintext, 16, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 17, "got %u\n", size); + + size = 0; + ret = pBCryptDecrypt(key, ciphertext6, 48, NULL, NULL, 16, plaintext, 31, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == 48, "got %u\n", size); + + /* input size is not a multiple of block size */ + size = 0; + ret = pBCryptDecrypt(key, ciphertext4, 17, NULL, NULL, 16, NULL, 0, &size, 0); + ok(ret == STATUS_INVALID_BUFFER_SIZE, "got 0x%x\n", ret); + ok(size == 17 || broken(size == 0 /* Win < 7 */), "got %u\n", size); + + /* input size is not a multiple of block size, block padding set */ + size = 0; + ret = pBCryptDecrypt(key, ciphertext4, 17, NULL, NULL, 16, NULL, 0, &size, BCRYPT_BLOCK_PADDING); + ok(ret == STATUS_INVALID_BUFFER_SIZE, "got 0x%x\n", ret); + ok(size == 17 || broken(size == 0 /* Win < 7 */), "got %u\n", size); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + free(buf); + + ret = pBCryptDestroyKey(NULL); + ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + + ret = pBCryptCloseAlgorithmProvider(aes, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptCloseAlgorithmProvider(aes, 0); + ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + + ret = pBCryptCloseAlgorithmProvider(NULL, 0); + ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); +} + +static void test_key_import_export(void) +{ + static const UCHAR encrypted_blob[40] = {0x33,0x6e,0x51,0x10, + 0x25,0xba,0xdb,0xce,0xcb,0x25,0x00,0x85,0x51,0xc0,0xfa,0x21, + 0x66,0xdd,0x6d,0x67,0x46,0x76,0x0f,0x8a,0x44,0xe5,0x65,0x31, + 0xcb,0x02,0x52,0x9c,0x69,0x59,0x1a,0xec,0x67,0x27,0x11,0xaa}; + UCHAR buffer1[sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + 16]; + UCHAR buffer2[sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + 32], *buf; + UCHAR buffer3[32 + 8], buffer4[sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + 32]; + BCRYPT_KEY_DATA_BLOB_HEADER *key_data1 = (void*)buffer1; + BCRYPT_KEY_DATA_BLOB_HEADER *key_data2 = (void*)buffer2; + BCRYPT_ALG_HANDLE aes; + BCRYPT_KEY_HANDLE key, key2, key3; + NTSTATUS ret; + ULONG size; + + ret = pBCryptOpenAlgorithmProvider(&aes, BCRYPT_AES_ALGORITHM, NULL, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + key_data1->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC; + key_data1->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1; + key_data1->cbKeyData = 16; + memset(&key_data1[1], 0x11, 16); + + key = NULL; + ret = pBCryptImportKey(aes, NULL, BCRYPT_KEY_DATA_BLOB, &key, NULL, 0, buffer1, sizeof(buffer1), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(key != NULL, "key not set\n"); + + key_data2->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC; + key_data2->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1; + key_data2->cbKeyData = 32; + memset(&key_data2[1], 0x22, 32); + key2 = NULL; + ret = pBCryptImportKey(aes, NULL, BCRYPT_KEY_DATA_BLOB, &key2, NULL, 0, buffer2, sizeof(buffer2), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(key2 != NULL, "key not set\n"); + + size = 0; + ret = pBCryptExportKey(key2, key, BCRYPT_AES_WRAP_KEY_BLOB, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(buffer3), "got %u\n", size); + + ret = pBCryptExportKey(key2, key, BCRYPT_AES_WRAP_KEY_BLOB, buffer3, size, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(!memcmp(buffer3, encrypted_blob, sizeof(encrypted_blob)), "blobs didn't match\n"); + + key3 = NULL; + ret = pBCryptImportKey(aes, key, BCRYPT_AES_WRAP_KEY_BLOB, &key3, NULL, 0, buffer3, sizeof(buffer3), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(key3 != NULL, "key not set\n"); + + size = 0; + memset(buffer4, 0xff, sizeof(buffer4)); + ret = pBCryptExportKey(key3, NULL, BCRYPT_KEY_DATA_BLOB, buffer4, sizeof(buffer4), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(buffer2), "Got %u\n", size); + ok(!memcmp(buffer4, buffer2, sizeof(buffer2)), "Expected exported key to match imported key\n"); + + pBCryptDestroyKey(key3); + pBCryptDestroyKey(key2); + + size = 0; + ret = pBCryptExportKey(key, NULL, BCRYPT_KEY_DATA_BLOB, buffer1, 0, &size, 0); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size == sizeof(buffer1), "got %u\n", size); + + size = 0; + memset(buffer2, 0xff, sizeof(buffer2)); + ret = pBCryptExportKey(key, NULL, BCRYPT_KEY_DATA_BLOB, buffer2, sizeof(buffer2), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(buffer1), "Got %u\n", size); + ok(!memcmp(buffer1, buffer2, sizeof(buffer1)), "Expected exported key to match imported key\n"); + + /* opaque blob */ + size = 0; + ret = pBCryptExportKey(key, NULL, BCRYPT_OPAQUE_KEY_BLOB, buffer2, 0, &size, 0); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(size > 0, "got zero\n"); + + buf = malloc(size); + ret = pBCryptExportKey(key, NULL, BCRYPT_OPAQUE_KEY_BLOB, buf, size, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + key = NULL; + ret = pBCryptImportKey(aes, NULL, BCRYPT_OPAQUE_KEY_BLOB, &key, NULL, 0, buf, size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(key != NULL, "key not set\n"); + + ret = pBCryptDestroyKey(key); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + if (pBCryptHash) + { + ret = pBCryptImportKey(BCRYPT_AES_CBC_ALG_HANDLE, NULL, BCRYPT_OPAQUE_KEY_BLOB, &key, NULL, 0, buf, size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + pBCryptDestroyKey(key); + } + + free(buf); + ret = pBCryptCloseAlgorithmProvider(aes, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); +} + +static BYTE eccPrivkey[] = +{ + /* X */ + 0x26, 0xff, 0x0e, 0xf9, 0x71, 0x93, 0xf8, 0xed, 0x59, 0xfa, 0x24, 0xec, 0x18, 0x13, 0xfe, 0xf5, + 0x0b, 0x4a, 0xb1, 0x27, 0xb7, 0xab, 0x3e, 0x4f, 0xc5, 0x5a, 0x91, 0xa3, 0x6e, 0x21, 0x61, 0x65, + /* Y */ + 0x62, 0x7b, 0x8b, 0x30, 0x7a, 0x63, 0x4c, 0x1a, 0xf4, 0x54, 0x54, 0xbb, 0x75, 0x59, 0x68, 0x36, + 0xfe, 0x49, 0x95, 0x75, 0x9e, 0x20, 0x3e, 0x69, 0x58, 0xb9, 0x7a, 0x84, 0x03, 0x45, 0x5c, 0x10, + /* d */ + 0xb9, 0xcd, 0xbe, 0xd4, 0x75, 0x5d, 0x05, 0xe5, 0x83, 0x0c, 0xd3, 0x37, 0x34, 0x15, 0xe3, 0x2c, + 0xe5, 0x85, 0x15, 0xa9, 0xee, 0xba, 0x94, 0x03, 0x03, 0x0b, 0x86, 0xea, 0x85, 0x40, 0xbd, 0x35, +}; +static BYTE eccPubkey[] = +{ + /* X */ + 0x3b, 0x3c, 0x34, 0xc8, 0x3f, 0x15, 0xea, 0x02, 0x68, 0x46, 0x69, 0xdf, 0x0c, 0xa6, 0xee, 0x7a, + 0xd9, 0x82, 0x08, 0x9b, 0x37, 0x53, 0x42, 0xf3, 0x13, 0x63, 0xda, 0x65, 0x79, 0xe8, 0x04, 0x9e, + /* Y */ + 0x8c, 0x77, 0xc4, 0x33, 0x77, 0xd9, 0x5a, 0x7f, 0x60, 0x7b, 0x98, 0xce, 0xf3, 0x96, 0x56, 0xd6, + 0xb5, 0x8d, 0x87, 0x7a, 0x00, 0x2b, 0xf3, 0x70, 0xb3, 0x90, 0x73, 0xa0, 0x56, 0x06, 0x3b, 0x22, +}; +static BYTE certHash[] = +{ + 0x28, 0x19, 0x0f, 0x15, 0x6d, 0x75, 0xcc, 0xcf, 0x62, 0xf1, 0x5e, 0xe6, 0x8a, 0xc3, 0xf0, 0x5d, + 0x89, 0x28, 0x2d, 0x48, 0xd8, 0x73, 0x7c, 0x05, 0x05, 0x8e, 0xbc, 0xce, 0x28, 0xb7, 0xba, 0xc9, +}; +static BYTE certSignature[] = +{ + /* r */ + 0xd7, 0x29, 0xce, 0x5a, 0xef, 0x74, 0x85, 0xd1, 0x18, 0x5f, 0x6e, 0xf1, 0xba, 0x53, 0xd4, 0xcd, + 0xdd, 0xe0, 0x5d, 0xf1, 0x5e, 0x48, 0x51, 0xea, 0x63, 0xc0, 0xe8, 0xe2, 0xf6, 0xfa, 0x4c, 0xaf, + /* s */ + 0xe3, 0x94, 0x15, 0x3b, 0x6c, 0x71, 0x6e, 0x44, 0x22, 0xcb, 0xa0, 0x88, 0xcd, 0x0a, 0x5a, 0x50, + 0x29, 0x7c, 0x5c, 0xd6, 0x6c, 0xd2, 0xe0, 0x7f, 0xcd, 0x02, 0x92, 0x21, 0x4c, 0x2c, 0x92, 0xee, +}; + +static void test_ECDSA(void) +{ + BYTE buffer[sizeof(BCRYPT_ECCKEY_BLOB) + sizeof(eccPrivkey)]; + BCRYPT_ECCKEY_BLOB *ecckey = (void *)buffer; + BCRYPT_ALG_HANDLE alg; + BCRYPT_KEY_HANDLE key; + NTSTATUS status; + DWORD keylen; + ULONG size; + + status = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDSA_P256_ALGORITHM, NULL, 0); + ok(!status, "got 0x%x\n", status); + + ecckey->dwMagic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC; + memcpy(ecckey + 1, eccPubkey, sizeof(eccPubkey)); + + ecckey->cbKey = 2; + size = sizeof(BCRYPT_ECCKEY_BLOB) + sizeof(eccPubkey); + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &key, buffer, size, 0); + ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got 0x%x\n", status); + + ecckey->dwMagic = BCRYPT_ECDH_PUBLIC_P256_MAGIC; + ecckey->cbKey = 32; + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &key, buffer, size, 0); + ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got 0x%x\n", status); + + ecckey->dwMagic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC; + ecckey->cbKey = 32; + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_PUBLIC_KEY_BLOB, &key, buffer, size, 0); + ok(!status, "BCryptImportKeyPair failed: 0x%x\n", status); + pBCryptDestroyKey(key); + + if (pBCryptHash) + { + status = pBCryptImportKeyPair(BCRYPT_ECDSA_P256_ALG_HANDLE, NULL, BCRYPT_PUBLIC_KEY_BLOB, &key, buffer, + size, 0); + ok(!status, "BCryptImportKeyPair failed: 0x%x\n", status); + pBCryptDestroyKey(key); + } + + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &key, buffer, size, 0); + ok(!status, "BCryptImportKeyPair failed: 0x%x\n", status); + + keylen = 0; + status = pBCryptGetProperty(key, BCRYPT_KEY_STRENGTH, (UCHAR *)&keylen, sizeof(keylen), &size, 0); + ok(!status, "got 0x%x\n", status); + ok(size == sizeof(keylen), "got %u\n", size); + ok(keylen == 256, "got %u\n", keylen); + + memset(buffer, 0xcc, sizeof(buffer)); + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, buffer, sizeof(buffer), &size, 0); + ok(!status, "Got unexpected status 0x%x\n", status); + ok(ecckey->dwMagic == BCRYPT_ECDSA_PUBLIC_P256_MAGIC, "Got unexpected magic 0x%x.\n", ecckey->dwMagic); + ok(ecckey->cbKey == 32, "got %u\n", ecckey->cbKey); + ok(!memcmp(ecckey + 1, eccPubkey, sizeof(eccPubkey)), "Got unexpected key data.\n"); + + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPRIVATE_BLOB, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status 0x%x\n", status); + + status = pBCryptVerifySignature(key, NULL, certHash, sizeof(certHash) - 1, certSignature, sizeof(certSignature), 0); + ok(status == STATUS_INVALID_SIGNATURE, "Expected STATUS_INVALID_SIGNATURE, got 0x%x\n", status); + + status = pBCryptVerifySignature(key, NULL, certHash, sizeof(certHash), certSignature, sizeof(certSignature), 0); + ok(!status, "BCryptVerifySignature failed: 0x%x\n", status); + pBCryptDestroyKey(key); + + ecckey->dwMagic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC; + memcpy(ecckey + 1, eccPrivkey, sizeof(eccPrivkey)); + + ecckey->cbKey = 2; + size = sizeof(BCRYPT_ECCKEY_BLOB) + sizeof(eccPrivkey); + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &key, buffer, size, 0); + ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got 0x%x\n", status); + + ecckey->dwMagic = BCRYPT_ECDH_PRIVATE_P256_MAGIC; + ecckey->cbKey = 32; + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &key, buffer, size, 0); + ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got 0x%x\n", status); + + ecckey->dwMagic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC; + ecckey->cbKey = 32; + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &key, buffer, size, 0); + ok(!status, "BCryptImportKeyPair failed: 0x%x\n", status); + + memset( buffer, 0xcc, sizeof(buffer) ); + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, buffer, sizeof(buffer), &size, 0); + ok(!status, "Got unexpected status 0x%x\n", status); + ok(ecckey->dwMagic == BCRYPT_ECDSA_PUBLIC_P256_MAGIC, "got 0x%x\n", ecckey->dwMagic); + ok(ecckey->cbKey == 32, "got %u\n", ecckey->cbKey); + ok(!memcmp(ecckey + 1, eccPrivkey, sizeof(eccPubkey)), "Got unexpected key data.\n"); + + size = sizeof(BCRYPT_ECCKEY_BLOB) + sizeof(eccPrivkey); + memset( buffer, 0, sizeof(buffer) ); + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPRIVATE_BLOB, buffer, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ecckey = (BCRYPT_ECCKEY_BLOB *)buffer; + ok(ecckey->dwMagic == BCRYPT_ECDSA_PRIVATE_P256_MAGIC, "got 0x%x\n", ecckey->dwMagic); + ok(ecckey->cbKey == 32, "got %u\n", ecckey->cbKey); + ok(size == sizeof(*ecckey) + ecckey->cbKey * 3, "got %u\n", size); + + pBCryptDestroyKey(key); + pBCryptCloseAlgorithmProvider(alg, 0); +} + +static UCHAR rsaPublicBlob[] = +{ + 0x52, 0x53, 0x41, 0x31, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xad, 0x41, 0x09, 0xa2, 0x56, + 0x3a, 0x7b, 0x75, 0x4b, 0x72, 0x9b, 0x28, 0x72, 0x3b, 0xae, 0x9f, 0xd8, 0xa8, 0x25, 0x4a, 0x4c, + 0x19, 0xf5, 0xa6, 0xd0, 0x05, 0x1c, 0x59, 0x8f, 0xe3, 0xf3, 0x2d, 0x29, 0x47, 0xf8, 0x80, 0x25, + 0x25, 0x21, 0x58, 0xc2, 0xac, 0xa1, 0x9e, 0x93, 0x8e, 0x82, 0x6d, 0xd7, 0xf3, 0xe7, 0x8f, 0x0b, + 0xc0, 0x41, 0x85, 0x29, 0x3c, 0xf1, 0x0b, 0x2c, 0x5d, 0x49, 0xed, 0xb4, 0x30, 0x6e, 0x02, 0x15, + 0x4b, 0x9a, 0x08, 0x0d, 0xe1, 0x6f, 0xa8, 0xd3, 0x12, 0xab, 0x66, 0x48, 0x4d, 0xd9, 0x28, 0x03, + 0x6c, 0x9d, 0x44, 0x7a, 0xed, 0xc9, 0x43, 0x4f, 0x9d, 0x4e, 0x3c, 0x7d, 0x0e, 0xff, 0x07, 0x87, + 0xeb, 0xca, 0xca, 0x65, 0x6d, 0xbe, 0xc5, 0x31, 0x8b, 0xcc, 0x7e, 0x0a, 0x71, 0x4a, 0x4d, 0x9d, + 0x3d, 0xfd, 0x7a, 0x56, 0x32, 0x8a, 0x6c, 0x6d, 0x9d, 0x2a, 0xd9, 0x8e, 0x68, 0x89, 0x63, 0xc6, + 0x4f, 0x24, 0xd1, 0x2a, 0x72, 0x69, 0x08, 0x77, 0xa0, 0x7f, 0xfe, 0xc6, 0x33, 0x8d, 0xb4, 0x7d, + 0x73, 0x91, 0x13, 0x9c, 0x47, 0x53, 0x6a, 0x13, 0xdf, 0x19, 0xc7, 0xed, 0x48, 0x81, 0xed, 0xd8, + 0x1f, 0x11, 0x11, 0xbb, 0x41, 0x15, 0x5b, 0xa4, 0xf5, 0xc9, 0x2b, 0x48, 0x5e, 0xd8, 0x4b, 0x52, + 0x1f, 0xf7, 0x87, 0xf2, 0x68, 0x25, 0x28, 0x79, 0xee, 0x39, 0x41, 0xc9, 0x0e, 0xc8, 0xf9, 0xf2, + 0xd8, 0x24, 0x09, 0xb4, 0xd4, 0xb7, 0x90, 0xba, 0x26, 0xe8, 0x1d, 0xb4, 0xd7, 0x09, 0x00, 0xc4, + 0xa0, 0xb6, 0x14, 0xe8, 0x4c, 0x29, 0x60, 0x54, 0x2e, 0x01, 0xde, 0x54, 0x66, 0x40, 0x22, 0x50, + 0x27, 0xf1, 0xe7, 0x62, 0xa9, 0x00, 0x5a, 0x61, 0x2e, 0xfa, 0xfe, 0x16, 0xd8, 0xe0, 0xe7, 0x66, + 0x17, 0xda, 0xb8, 0x0c, 0xa6, 0x04, 0x8d, 0xf8, 0x21, 0x68, 0x39 +}; + +static UCHAR rsaHash[] = +{ + 0x96, 0x1f, 0xa6, 0x49, 0x58, 0x81, 0x8f, 0x76, 0x77, 0x07, 0x07, 0x27, 0x55, 0xd7, 0x01, 0x8d, + 0xcd, 0x27, 0x8e, 0x94 +}; + +static UCHAR rsaSignature[] = +{ + 0xa8, 0x3a, 0x9d, 0xaf, 0x92, 0x94, 0xa4, 0x4d, 0x34, 0xba, 0x41, 0x0c, 0xc1, 0x23, 0x91, 0xc7, + 0x91, 0xa8, 0xf8, 0xfc, 0x94, 0x87, 0x4d, 0x05, 0x85, 0x63, 0xe8, 0x7d, 0xea, 0x7f, 0x6b, 0x8d, + 0xbb, 0x9a, 0xd4, 0x46, 0xa6, 0xc0, 0xd6, 0xdc, 0x91, 0xba, 0xd3, 0x1a, 0xbf, 0xf4, 0x52, 0xa0, + 0xc7, 0x15, 0x87, 0xe9, 0x1e, 0x60, 0x49, 0x9c, 0xee, 0x5a, 0x9c, 0x6c, 0xbd, 0x7a, 0x3e, 0xc3, + 0x48, 0xb3, 0xee, 0xca, 0x68, 0x40, 0x9b, 0xa1, 0x4c, 0x6e, 0x20, 0xd6, 0xca, 0x6c, 0x72, 0xaf, + 0x2b, 0x6b, 0x62, 0x7c, 0x78, 0x06, 0x94, 0x4c, 0x02, 0xf3, 0x8d, 0x49, 0xe0, 0x11, 0xc4, 0x9b, + 0x62, 0x5b, 0xc2, 0xfd, 0x68, 0xf4, 0x07, 0x15, 0x71, 0x11, 0x4c, 0x35, 0x97, 0x5d, 0xc0, 0xe6, + 0x22, 0xc9, 0x8a, 0x7b, 0x96, 0xc9, 0xc3, 0xe4, 0x2b, 0x1e, 0x88, 0x17, 0x4f, 0x98, 0x9b, 0xf3, + 0x42, 0x23, 0x0c, 0xa0, 0xfa, 0x19, 0x03, 0x2a, 0xf7, 0x13, 0x2d, 0x27, 0xac, 0x9f, 0xaf, 0x2d, + 0xa3, 0xce, 0xf7, 0x63, 0xbb, 0x39, 0x9f, 0x72, 0x80, 0xdd, 0x6c, 0x73, 0x00, 0x85, 0x70, 0xf2, + 0xed, 0x50, 0xed, 0xa0, 0x74, 0x42, 0xd7, 0x22, 0x46, 0x24, 0xee, 0x67, 0xdf, 0xb5, 0x45, 0xe8, + 0x49, 0xf4, 0x9c, 0xe4, 0x00, 0x83, 0xf2, 0x27, 0x8e, 0xa2, 0xb1, 0xc3, 0xc2, 0x01, 0xd7, 0x59, + 0x2e, 0x4d, 0xac, 0x49, 0xa2, 0xc1, 0x8d, 0x88, 0x4b, 0xfe, 0x28, 0xe5, 0xac, 0xa6, 0x85, 0xc4, + 0x1f, 0xf8, 0xc5, 0xc5, 0x14, 0x4e, 0xa3, 0xcb, 0x17, 0xb7, 0x64, 0xb3, 0xc2, 0x12, 0xf8, 0xf8, + 0x36, 0x99, 0x1c, 0x91, 0x9b, 0xbd, 0xed, 0x55, 0x0f, 0xfd, 0x49, 0x85, 0xbb, 0x32, 0xad, 0x78, + 0xc1, 0x74, 0xe6, 0x7c, 0x18, 0x0f, 0x2b, 0x3b, 0xaa, 0xd1, 0x9d, 0x40, 0x71, 0x1d, 0x19, 0x53 +}; + +static UCHAR rsaPrivateBlob[] = +{ + 0x52, 0x53, 0x41, 0x32, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xa6, 0x8b, 0x46, 0x26, 0xb5, + 0xa9, 0x69, 0x83, 0x94, 0x66, 0xa7, 0xf3, 0x33, 0x95, 0x74, 0xe9, 0xeb, 0xc8, 0xcd, 0xd7, 0x81, + 0x9e, 0x45, 0x66, 0xb2, 0x48, 0x8b, 0x1f, 0xfe, 0xb3, 0x62, 0xc4, 0x0d, 0xa2, 0xf9, 0xf3, 0xe2, + 0xa6, 0x86, 0xd1, 0x1e, 0x8a, 0xbb, 0x1d, 0xa5, 0xc5, 0xe8, 0xa7, 0x50, 0x37, 0xfd, 0x69, 0x1f, + 0x6f, 0x99, 0x99, 0xca, 0x39, 0x13, 0xea, 0x5b, 0x6b, 0xe3, 0x91, 0xc0, 0xd2, 0x2c, 0x0b, 0x21, + 0xb1, 0xac, 0xa9, 0xe8, 0xa0, 0x6d, 0xa4, 0x1f, 0x1b, 0x34, 0xcb, 0x88, 0x7f, 0x2e, 0xeb, 0x7d, + 0x91, 0x38, 0x48, 0xce, 0x05, 0x73, 0x05, 0xdd, 0x22, 0x94, 0xc3, 0xdd, 0x1c, 0xfd, 0xc5, 0x41, + 0x2e, 0x94, 0xf9, 0xed, 0xe5, 0x92, 0x5f, 0x3f, 0x06, 0xf8, 0x49, 0x60, 0xb8, 0x92, 0x52, 0x6a, + 0x56, 0x6e, 0xd7, 0x04, 0x1a, 0xb5, 0xb5, 0x1c, 0x31, 0xd1, 0x1b, +}; + +static UCHAR rsaFullPrivateBlob[] = +{ + 0x52, 0x53, 0x41, 0x33, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xa6, 0x8b, 0x46, 0x26, 0xb5, + 0xa9, 0x69, 0x83, 0x94, 0x66, 0xa7, 0xf3, 0x33, 0x95, 0x74, 0xe9, 0xeb, 0xc8, 0xcd, 0xd7, 0x81, + 0x9e, 0x45, 0x66, 0xb2, 0x48, 0x8b, 0x1f, 0xfe, 0xb3, 0x62, 0xc4, 0x0d, 0xa2, 0xf9, 0xf3, 0xe2, + 0xa6, 0x86, 0xd1, 0x1e, 0x8a, 0xbb, 0x1d, 0xa5, 0xc5, 0xe8, 0xa7, 0x50, 0x37, 0xfd, 0x69, 0x1f, + 0x6f, 0x99, 0x99, 0xca, 0x39, 0x13, 0xea, 0x5b, 0x6b, 0xe3, 0x91, 0xc0, 0xd2, 0x2c, 0x0b, 0x21, + 0xb1, 0xac, 0xa9, 0xe8, 0xa0, 0x6d, 0xa4, 0x1f, 0x1b, 0x34, 0xcb, 0x88, 0x7f, 0x2e, 0xeb, 0x7d, + 0x91, 0x38, 0x48, 0xce, 0x05, 0x73, 0x05, 0xdd, 0x22, 0x94, 0xc3, 0xdd, 0x1c, 0xfd, 0xc5, 0x41, + 0x2e, 0x94, 0xf9, 0xed, 0xe5, 0x92, 0x5f, 0x3f, 0x06, 0xf8, 0x49, 0x60, 0xb8, 0x92, 0x52, 0x6a, + 0x56, 0x6e, 0xd7, 0x04, 0x1a, 0xb5, 0xb5, 0x1c, 0x31, 0xd1, 0x1b, 0xa3, 0xf3, 0xd1, 0x69, 0x61, + 0xab, 0xfe, 0xc1, 0xb6, 0x40, 0x7b, 0x19, 0xbb, 0x2d, 0x59, 0xf5, 0xda, 0x49, 0x32, 0x6f, 0x20, + 0x24, 0xd3, 0xb3, 0xec, 0x21, 0xec, 0x0c, 0xc7, 0x5b, 0xf9, 0x1b, 0xba, 0x6e, 0xe9, 0x61, 0xda, + 0x55, 0xc6, 0x72, 0xfd, 0x2d, 0x66, 0x3f, 0x3c, 0xcb, 0x49, 0xa9, 0xc5, 0x0d, 0x9b, 0x02, 0x36, + 0x7a, 0xee, 0x36, 0x09, 0x55, 0xe4, 0x03, 0xf2, 0xe3, 0xe6, 0x25, 0x14, 0x89, 0x7f, 0x2b, 0xfb, + 0x27, 0x0e, 0x8d, 0x37, 0x84, 0xfd, 0xad, 0x10, 0x79, 0x43, 0x4e, 0x38, 0x4a, 0xd4, 0x5e, 0xfa, + 0xda, 0x9f, 0x88, 0x21, 0x7c, 0xb4, 0x98, 0xb6, 0x6e, 0x1c, 0x24, 0x09, 0xe5, 0xe7, 0x22, 0x6f, + 0xd3, 0x84, 0xc0, 0xdc, 0x36, 0x09, 0xaf, 0x4b, 0x96, 0x8b, 0x5f, 0x47, 0xb3, 0x24, 0x80, 0xb5, + 0x64, 0x69, 0xad, 0x83, 0xd5, 0x09, 0xe7, 0xb9, 0xe4, 0x81, 0x6f, 0x1a, 0xe2, 0x6d, 0xf1, 0x5e, + 0x2b, 0xb3, 0x7a, 0xd0, 0x77, 0xef, 0x82, 0xcd, 0x55, 0x2e, 0xd5, 0xb1, 0xa7, 0x72, 0xec, 0x02, + 0x9d, 0xe2, 0xcc, 0x5a, 0xf1, 0x68, 0x30, 0xe5, 0xbc, 0x8d, 0xad, +}; + +static struct +{ + PUBLICKEYSTRUC header; + RSAPUBKEY rsapubkey; + BYTE modulus[512 / 8]; + BYTE prime1[512 / 16]; + BYTE prime2[512 / 16]; + BYTE exp1[512 / 16]; + BYTE exp2[512 / 16]; + BYTE coefficient[512 / 16]; + BYTE private_exp[512 / 8]; + +} rsaLegacyPrivateBlob = +{ + { PRIVATEKEYBLOB, CUR_BLOB_VERSION, 0, CALG_RSA_KEYX }, + { BCRYPT_RSAPRIVATE_MAGIC, 512, 0x10001 }, + { + 0x91, 0xe3, 0x6b, 0x5b, 0xea, 0x13, 0x39, 0xca, 0x99, 0x99, 0x6f, 0x1f, 0x69, 0xfd, 0x37, 0x50, + 0xa7, 0xe8, 0xc5, 0xa5, 0x1d, 0xbb, 0x8a, 0x1e, 0xd1, 0x86, 0xa6, 0xe2, 0xf3, 0xf9, 0xa2, 0x0d, + 0xc4, 0x62, 0xb3, 0xfe, 0x1f, 0x8b, 0x48, 0xb2, 0x66, 0x45, 0x9e, 0x81, 0xd7, 0xcd, 0xc8, 0xeb, + 0xe9, 0x74, 0x95, 0x33, 0xf3, 0xa7, 0x66, 0x94, 0x83, 0x69, 0xa9, 0xb5, 0x26, 0x46, 0x8b, 0xa6 + }, + { + 0xc3, 0x94, 0x22, 0xdd, 0x05, 0x73, 0x05, 0xce, 0x48, 0x38, 0x91, 0x7d, 0xeb, 0x2e, 0x7f, 0x88, + 0xcb, 0x34, 0x1b, 0x1f, 0xa4, 0x6d, 0xa0, 0xe8, 0xa9, 0xac, 0xb1, 0x21, 0x0b, 0x2c, 0xd2, 0xc0 + }, + { + 0x1b, 0xd1, 0x31, 0x1c, 0xb5, 0xb5, 0x1a, 0x04, 0xd7, 0x6e, 0x56, 0x6a, 0x52, 0x92, 0xb8, 0x60, + 0x49, 0xf8, 0x06, 0x3f, 0x5f, 0x92, 0xe5, 0xed, 0xf9, 0x94, 0x2e, 0x41, 0xc5, 0xfd, 0x1c, 0xdd + }, + { + 0x1b, 0xf9, 0x5b, 0xc7, 0x0c, 0xec, 0x21, 0xec, 0xb3, 0xd3, 0x24, 0x20, 0x6f, 0x32, 0x49, 0xda, + 0xf5, 0x59, 0x2d, 0xbb, 0x19, 0x7b, 0x40, 0xb6, 0xc1, 0xfe, 0xab, 0x61, 0x69, 0xd1, 0xf3, 0xa3 + }, + { + 0x25, 0xe6, 0xe3, 0xf2, 0x03, 0xe4, 0x55, 0x09, 0x36, 0xee, 0x7a, 0x36, 0x02, 0x9b, 0x0d, 0xc5, + 0xa9, 0x49, 0xcb, 0x3c, 0x3f, 0x66, 0x2d, 0xfd, 0x72, 0xc6, 0x55, 0xda, 0x61, 0xe9, 0x6e, 0xba + }, + { + 0x24, 0x1c, 0x6e, 0xb6, 0x98, 0xb4, 0x7c, 0x21, 0x88, 0x9f, 0xda, 0xfa, 0x5e, 0xd4, 0x4a, 0x38, + 0x4e, 0x43, 0x79, 0x10, 0xad, 0xfd, 0x84, 0x37, 0x8d, 0x0e, 0x27, 0xfb, 0x2b, 0x7f, 0x89, 0x14 + }, + { + 0xad, 0x8d, 0xbc, 0xe5, 0x30, 0x68, 0xf1, 0x5a, 0xcc, 0xe2, 0x9d, 0x02, 0xec, 0x72, 0xa7, 0xb1, + 0xd5, 0x2e, 0x55, 0xcd, 0x82, 0xef, 0x77, 0xd0, 0x7a, 0xb3, 0x2b, 0x5e, 0xf1, 0x6d, 0xe2, 0x1a, + 0x6f, 0x81, 0xe4, 0xb9, 0xe7, 0x09, 0xd5, 0x83, 0xad, 0x69, 0x64, 0xb5, 0x80, 0x24, 0xb3, 0x47, + 0x5f, 0x8b, 0x96, 0x4b, 0xaf, 0x09, 0x36, 0xdc, 0xc0, 0x84, 0xd3, 0x6f, 0x22, 0xe7, 0xe5, 0x09 + } +}; + +static UCHAR rsaPublicBlobWithInvalidPublicExpSize[] = +{ + 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0xc7, 0x8f, 0xac, 0x2a, 0xce, 0xbf, 0xc9, 0x6c, 0x7b, + 0x85, 0x74, 0x71, 0xbb, 0xff, 0xbb, 0x9b, 0x20, 0x03, 0x79, 0x17, 0x34, + 0xe7, 0x26, 0x91, 0x5c, 0x1f, 0x1b, 0x03, 0x3d, 0x46, 0xdf, 0xb6, 0xf2, + 0x10, 0x55, 0xf0, 0x39, 0x55, 0x0a, 0xe3, 0x9c, 0x0c, 0x63, 0xc2, 0x14, + 0x03, 0x94, 0x51, 0x0d, 0xb4, 0x22, 0x09, 0xf2, 0x5c, 0xb2, 0xd1, 0xc3, + 0xac, 0x6f, 0xa8, 0xc4, 0xac, 0xb8, 0xbc, 0x59, 0xe7, 0xed, 0x77, 0x6e, + 0xb1, 0x80, 0x58, 0x7d, 0xb2, 0x94, 0x46, 0xe5, 0x00, 0xe2, 0xb7, 0x33, + 0x48, 0x7a, 0xd3, 0x78, 0xe9, 0x26, 0x01, 0xc7, 0x00, 0x7b, 0x41, 0x6d, + 0x94, 0x3a, 0xe1, 0x50, 0x2b, 0x9f, 0x6b, 0x1c, 0x08, 0xa3, 0xfc, 0x0a, + 0x44, 0x81, 0x09, 0x41, 0x80, 0x23, 0x7b, 0xf6, 0x3f, 0xaf, 0x91, 0xa1, + 0x87, 0x75, 0x33, 0x15, 0xb8, 0xde, 0x32, 0x30, 0xb4, 0x5e, 0xfd +}; + +static const UCHAR rsa_encrypted_no_padding[] = +{ + 0x4a, 0xc1, 0xfa, 0x4f, 0xe0, 0x3f, 0x36, 0x9a, 0x64, 0xbf, 0x2e, 0x00, 0xb4, 0xb5, 0x40, 0xbe, + 0x2d, 0x9a, 0x14, 0xf6, 0x8f, 0xa5, 0xc2, 0xe2, 0x20, 0xaf, 0x21, 0x79, 0xc6, 0x32, 0x7e, 0xea, + 0x73, 0x00, 0x01, 0xbb, 0x9a, 0x19, 0x73, 0x41, 0x96, 0xae, 0x88, 0x6e, 0x36, 0x56, 0xe9, 0x9c, + 0xac, 0x04, 0x82, 0xa8, 0x00, 0xdb, 0x4e, 0x29, 0x61, 0x7e, 0xaf, 0x64, 0xdb, 0xa2, 0x70, 0x0f, +}; + +static void test_rsa_encrypt(void) +{ + UCHAR input[] = "Hello World!", input_no_padding[64] = { 0 }, encrypted[64], decrypted[64]; + BCRYPT_ALG_HANDLE rsa; + BCRYPT_KEY_HANDLE key, key2; + NTSTATUS ret; + DWORD encrypted_size, decrypted_size; + UCHAR *encrypted_a = NULL, *encrypted_b = NULL; + BCRYPT_OAEP_PADDING_INFO oaep_pad; + + oaep_pad.pszAlgId = BCRYPT_SHA256_ALGORITHM; + oaep_pad.pbLabel = (UCHAR *)"test"; + oaep_pad.cbLabel = 5; + + ret = pBCryptOpenAlgorithmProvider(&rsa, BCRYPT_RSA_ALGORITHM, NULL, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ret = pBCryptGenerateKeyPair(rsa, &key, 512, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + /* Not finalized key */ + ret = pBCryptEncrypt(key, input, sizeof(input), NULL, NULL, 0, NULL, 0, &encrypted_size, 0); + ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + pBCryptDestroyKey(key); + + /* Import a different public key first to make sure a public key from private key improted next + * overrides it. */ + ret = pBCryptImportKeyPair(rsa, NULL, BCRYPT_RSAPUBLIC_BLOB, &key, rsaPublicBlob, sizeof(rsaPublicBlob), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptImportKeyPair(rsa, NULL, BCRYPT_RSAPRIVATE_BLOB, &key, rsaPrivateBlob, sizeof(rsaPrivateBlob), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + /* No padding */ + todo_wine { + memset(input_no_padding, 0, sizeof(input_no_padding)); + strcpy((char *)input_no_padding, "Hello World"); + encrypted_size = 0; + ret = pBCryptEncrypt(key, input_no_padding, sizeof(input_no_padding), NULL, NULL, 0, NULL, 0, &encrypted_size, BCRYPT_PAD_NONE); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(encrypted_size == 64, "got size of %d\n", encrypted_size); + + encrypted_a = malloc(encrypted_size); + memset(encrypted_a, 0, encrypted_size); + encrypted_b = malloc(encrypted_size); + memset(encrypted_b, 0xff, encrypted_size); + + ret = pBCryptEncrypt(key, input, sizeof(input), NULL, NULL, 0, encrypted_a, encrypted_size, &encrypted_size, BCRYPT_PAD_NONE); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + ret = pBCryptEncrypt(key, input_no_padding, sizeof(input_no_padding), NULL, NULL, 0, encrypted_a, 12, &encrypted_size, BCRYPT_PAD_NONE); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + ok(encrypted_size == 64, "got size of %d\n", encrypted_size); + + ret = pBCryptEncrypt(key, input_no_padding, sizeof(input_no_padding), NULL, NULL, 0, encrypted_a, encrypted_size, &encrypted_size, BCRYPT_PAD_NONE); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(encrypted_size == 64, "got size of %d\n", encrypted_size); + + ret = pBCryptEncrypt(key, input_no_padding, sizeof(input_no_padding), NULL, NULL, 0, encrypted_b, encrypted_size, &encrypted_size, BCRYPT_PAD_NONE); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + } + ok(!memcmp(encrypted_a, encrypted_b, encrypted_size), "Both outputs should be the same\n"); + ok(!memcmp(encrypted_b, rsa_encrypted_no_padding, encrypted_size), "Data mismatch.\n"); + + todo_wine { + decrypted_size = 0; + ret = pBCryptDecrypt(key, encrypted_a, encrypted_size, NULL, NULL, 0, NULL, 0, &decrypted_size, BCRYPT_PAD_NONE); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(decrypted_size == sizeof(input_no_padding), "got %u\n", decrypted_size); + + ret = pBCryptDecrypt(key, encrypted_a, encrypted_size, NULL, NULL, 0, decrypted, decrypted_size, &decrypted_size, BCRYPT_PAD_NONE); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(decrypted_size == sizeof(input_no_padding), "got %u\n", decrypted_size); + ok(!memcmp(decrypted, input_no_padding, sizeof(input_no_padding)), "unexpected output\n"); + } + + /* PKCS1 Padding */ + encrypted_size = 0; + ret = pBCryptEncrypt(key, input, sizeof(input), NULL, NULL, 0, NULL, 0, &encrypted_size, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(encrypted_size == 64, "got size of %d\n", encrypted_size); + + encrypted_a = realloc(encrypted_a, encrypted_size); + memset(encrypted_a, 0, encrypted_size); + encrypted_b = realloc(encrypted_b, encrypted_size); + memset(encrypted_b, 0, encrypted_size); + + ret = pBCryptEncrypt(key, input, sizeof(input), NULL, NULL, 0, encrypted_a, encrypted_size, &encrypted_size, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(encrypted_size == 64, "got size of %d\n", encrypted_size); + + ret = pBCryptEncrypt(key, input, sizeof(input), NULL, NULL, 0, encrypted_b, encrypted_size, &encrypted_size, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(encrypted_size == 64, "got size of %d\n", encrypted_size); + ok(memcmp(encrypted_a, encrypted_b, encrypted_size), "Both outputs are the same\n"); + + decrypted_size = 0; + ret = pBCryptDecrypt(key, encrypted_a, encrypted_size, NULL, NULL, 0, NULL, 0, &decrypted_size, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(decrypted_size == sizeof(input), "got size of %d\n", decrypted_size); + + ret = pBCryptDecrypt(key, encrypted_a, encrypted_size, NULL, NULL, 0, decrypted, decrypted_size, &decrypted_size, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(decrypted_size == sizeof(input), "got size of %d\n", decrypted_size); + ok(!memcmp(decrypted, input, sizeof(input)), "unexpected output\n"); + + ret = pBCryptImportKeyPair(rsa, NULL, LEGACY_RSAPRIVATE_BLOB, &key2, (UCHAR *)&rsaLegacyPrivateBlob, + sizeof(rsaLegacyPrivateBlob), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptEncrypt(key2, input, sizeof(input), NULL, NULL, 0, encrypted, sizeof(encrypted), + &encrypted_size, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(encrypted_size == 64, "got size of %d\n", encrypted_size); + + memset(decrypted, 0, sizeof(decrypted)); + ret = pBCryptDecrypt(key, encrypted, sizeof(encrypted), NULL, NULL, 0, decrypted, sizeof(decrypted), + &decrypted_size, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(decrypted_size == sizeof(input), "got size of %d\n", decrypted_size); + ok(!memcmp(decrypted, input, sizeof(input)), "unexpected output\n"); + pBCryptDestroyKey(key2); + pBCryptDestroyKey(key); + + /* OAEP Padding */ + ret = pBCryptGenerateKeyPair(rsa, &key, 640 /* minimum size for sha256 hash */, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptFinalizeKeyPair(key, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + todo_wine { + encrypted_size = 0; + ret = pBCryptEncrypt(key, input, sizeof(input), &oaep_pad, NULL, 0, NULL, 0, &encrypted_size, BCRYPT_PAD_OAEP); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(encrypted_size == 80, "got size of %d\n", encrypted_size); + + encrypted_a = realloc(encrypted_a, encrypted_size); + memset(encrypted_a, 0, encrypted_size); + encrypted_b = realloc(encrypted_b, encrypted_size); + memset(encrypted_b, 0, encrypted_size); + + ret = pBCryptEncrypt(key, input, sizeof(input), &oaep_pad, NULL, 0, encrypted_a, encrypted_size, &encrypted_size, BCRYPT_PAD_OAEP); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(encrypted_size == 80, "got size of %d\n", encrypted_size); + + ret = pBCryptEncrypt(key, input, sizeof(input), &oaep_pad, NULL, 0, encrypted_b, encrypted_size, &encrypted_size, BCRYPT_PAD_OAEP); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(encrypted_size == 80, "got size of %d\n", encrypted_size); + ok(memcmp(encrypted_a, encrypted_b, encrypted_size), "Both outputs are the same\n"); + + decrypted_size = 0; + memset(decrypted, 0, sizeof(decrypted)); + ret = pBCryptDecrypt(key, encrypted_a, encrypted_size, &oaep_pad, NULL, 0, NULL, 0, &decrypted_size, BCRYPT_PAD_OAEP); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(decrypted_size == sizeof(input), "got %u\n", decrypted_size); + + ret = pBCryptDecrypt(key, encrypted_a, encrypted_size, &oaep_pad, NULL, 0, decrypted, decrypted_size, &decrypted_size, BCRYPT_PAD_OAEP); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(decrypted_size == sizeof(input), "got %u\n", decrypted_size); + ok(!memcmp(decrypted, input, sizeof(input)), "unexpected output\n"); + } + + free(encrypted_a); + free(encrypted_b); + + pBCryptDestroyKey(key); + + if (pBCryptHash) + { + ret = pBCryptGenerateKeyPair(BCRYPT_RSA_ALG_HANDLE, &key, 512, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + pBCryptDestroyKey(key); + } +} + +static void test_RSA(void) +{ + static UCHAR hash[] = + {0x7e,0xe3,0x74,0xe7,0xc5,0x0b,0x6b,0x70,0xdb,0xab,0x32,0x6d,0x1d,0x51,0xd6,0x74,0x79,0x8e,0x5b,0x4b}; + static UCHAR hash48[] = + {0x62,0xb2,0x1e,0x90,0xc9,0x02,0x2b,0x10,0x16,0x71,0xba,0x1f,0x80,0x8f,0x86,0x31,0xa8,0x14,0x9f,0x0f, + 0x12,0x90,0x40,0x55,0x83,0x9a,0x35,0xc1,0xca,0x78,0xae,0x53,0x1b,0xb3,0x36,0x06,0xba,0x90,0x89,0x12, + 0xa8,0x42,0x21,0x10,0x9d,0x29,0xcd,0x7e}; + BCRYPT_PKCS1_PADDING_INFO pad; + BCRYPT_PSS_PADDING_INFO pad_pss; + BCRYPT_ALG_HANDLE alg; + BCRYPT_KEY_HANDLE key; + BCRYPT_RSAKEY_BLOB *rsablob; + UCHAR sig[256], sig_pss[256]; + ULONG len, size, size2, schemes; + NTSTATUS ret; + BYTE *buf; + DWORD keylen; + + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_RSA_ALGORITHM, NULL, 0); + ok(!ret, "got 0x%x\n", ret); + + schemes = size = 0; + ret = pBCryptGetProperty(alg, L"PaddingSchemes", (UCHAR *)&schemes, sizeof(schemes), &size, 0); + ok(!ret, "got 0x%x\n", ret); + ok(schemes, "schemes not set\n"); + ok(size == sizeof(schemes), "got %u\n", size); + + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_PUBLIC_KEY_BLOB, &key, rsaPublicBlob, sizeof(rsaPublicBlob), 0); + ok(!ret, "BCryptImportKeyPair failed: 0x%x\n", ret); + pBCryptDestroyKey(key); + + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_RSAPUBLIC_BLOB, &key, rsaPublicBlob, sizeof(rsaPublicBlob), 0); + ok(!ret, "BCryptImportKeyPair failed: 0x%x\n", ret); + + keylen = 0; + ret = pBCryptGetProperty(key, BCRYPT_KEY_STRENGTH, (UCHAR *)&keylen, sizeof(keylen), &size, 0); + ok(!ret, "got 0x%x\n", ret); + ok(size == sizeof(keylen), "got %u\n", size); + ok(keylen == 2048, "got %u\n", keylen); + + pad.pszAlgId = BCRYPT_SHA1_ALGORITHM; + ret = pBCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); + ok(!ret, "BCryptVerifySignature failed: 0x%x\n", ret); + + ret = pBCryptVerifySignature(key, NULL, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); + ok(ret == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got 0x%x\n", ret); + + pad.pszAlgId = BCRYPT_SHA1_ALGORITHM; + ret = pBCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), 0); + ok(ret == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got 0x%x\n", ret); + + ret = pBCryptVerifySignature(key, NULL, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), 0); + ok(ret == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got 0x%x\n", ret); + + pad.pszAlgId = BCRYPT_AES_ALGORITHM; + ret = pBCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); + ok(ret == STATUS_NOT_SUPPORTED, "Expected STATUS_NOT_SUPPORTED, got 0x%x\n", ret); + + pad.pszAlgId = NULL; + ret = pBCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); + ok(ret == STATUS_INVALID_SIGNATURE, "Expected STATUS_INVALID_SIGNATURE, got 0x%x\n", ret); + + ret = pBCryptDestroyKey(key); + ok(!ret, "BCryptDestroyKey failed: 0x%x\n", ret); + + /* sign/verify with export/import round-trip */ + ret = pBCryptGenerateKeyPair(alg, &key, 1024, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + keylen = 2048; + ret = pBCryptSetProperty(key, BCRYPT_KEY_LENGTH, (UCHAR *)&keylen, 2, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + ret = pBCryptSetProperty(key, BCRYPT_KEY_LENGTH, (UCHAR *)&keylen, sizeof(keylen), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptFinalizeKeyPair(key, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + keylen = 0; + ret = pBCryptGetProperty(key, BCRYPT_KEY_STRENGTH, (UCHAR *)&keylen, sizeof(keylen), &size, 0); + ok(!ret, "got 0x%x\n", ret); + ok(size == sizeof(keylen), "got %u\n", size); + ok(keylen == 2048, "got %u\n", keylen); + + ret = pBCryptSetProperty(key, BCRYPT_KEY_LENGTH, (UCHAR *)&keylen, sizeof(keylen), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + pad.pszAlgId = BCRYPT_MD5_ALGORITHM; + memset(sig, 0, sizeof(sig)); + len = 0; + ret = pBCryptSignHash(key, &pad, hash, 16, sig, sizeof(sig), &len, BCRYPT_PAD_PKCS1); + ok(!ret, "got 0x%x\n", ret); + ok(len == 256, "got %u\n", len); + pad.pszAlgId = BCRYPT_MD5_ALGORITHM; + ret = pBCryptVerifySignature(key, &pad, hash, 16, sig, len, BCRYPT_PAD_PKCS1); + ok(!ret, "BCryptVerifySignature failed: 0x%x\n", ret); + + pad.pszAlgId = BCRYPT_SHA1_ALGORITHM; + memset(sig, 0, sizeof(sig)); + len = 0; + ret = pBCryptSignHash(key, &pad, hash, sizeof(hash), sig, sizeof(sig), &len, BCRYPT_PAD_PKCS1); + ok(!ret, "got 0x%x\n", ret); + ok(len == 256, "got %u\n", len); + pad.pszAlgId = BCRYPT_SHA1_ALGORITHM; + ret = pBCryptVerifySignature(key, &pad, hash, sizeof(hash), sig, len, BCRYPT_PAD_PKCS1); + ok(!ret, "BCryptVerifySignature failed: 0x%x\n", ret); + + pad.pszAlgId = NULL; + memset(sig, 0, sizeof(sig)); + len = 0; + ret = pBCryptSignHash(key, &pad, hash, sizeof(hash), sig, sizeof(sig), &len, BCRYPT_PAD_PKCS1); + ok(!ret, "got 0x%x\n", ret); + ok(len == 256, "got %u\n", len); + + pad.pszAlgId = BCRYPT_SHA1_ALGORITHM; + ret = pBCryptVerifySignature(key, &pad, hash, sizeof(hash), sig, len, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_INVALID_SIGNATURE, "BCryptVerifySignature failed: 0x%x, len %d\n", ret, len); + + pad.pszAlgId = NULL; + ret = pBCryptVerifySignature(key, &pad, hash, sizeof(hash), sig, len, BCRYPT_PAD_PKCS1); + ok(!ret, "BCryptVerifySignature failed: 0x%x, len %d\n", ret, len); + + pad_pss.pszAlgId = BCRYPT_SHA384_ALGORITHM; + pad_pss.cbSalt = 48; + memset(sig_pss, 0, sizeof(sig_pss)); + len = 0; + ret = pBCryptSignHash(key, &pad_pss, hash48, sizeof(hash48), sig_pss, sizeof(sig_pss), &len, BCRYPT_PAD_PSS); + ok(!ret, "got 0x%x\n", ret); + ok(len == 256, "got %u\n", len); + + /* export private key */ + size = 0; + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAPRIVATE_BLOB, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size, "size not set\n"); + + buf = malloc(size); + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAPRIVATE_BLOB, buf, size, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + rsablob = (BCRYPT_RSAKEY_BLOB *)buf; + ok(rsablob->Magic == BCRYPT_RSAPRIVATE_MAGIC, "got 0x%x\n", rsablob->Magic); + ok(rsablob->BitLength == 2048, "got %u\n", rsablob->BitLength); + ok(rsablob->cbPublicExp == 3, "got %u\n", rsablob->cbPublicExp); + ok(rsablob->cbModulus == 256, "got %u\n", rsablob->cbModulus); + ok(rsablob->cbPrime1 == 128, "got %u\n", rsablob->cbPrime1); + ok(rsablob->cbPrime2 == 128, "got %u\n", rsablob->cbPrime2); + size2 = sizeof(*rsablob) + rsablob->cbPublicExp + rsablob->cbModulus + rsablob->cbPrime1 + rsablob->cbPrime2; + ok(size == size2, "got %u expected %u\n", size2, size); + free(buf); + + size = 0; + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAFULLPRIVATE_BLOB, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size, "size not set\n"); + + buf = malloc(size); + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAFULLPRIVATE_BLOB, buf, size, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + rsablob = (BCRYPT_RSAKEY_BLOB *)buf; + ok(rsablob->Magic == BCRYPT_RSAFULLPRIVATE_MAGIC, "got 0x%x\n", rsablob->Magic); + ok(rsablob->BitLength == 2048, "got %u\n", rsablob->BitLength); + ok(rsablob->cbPublicExp == 3, "got %u\n", rsablob->cbPublicExp); + ok(rsablob->cbModulus == 256, "got %u\n", rsablob->cbModulus); + ok(rsablob->cbPrime1 == 128, "got %u\n", rsablob->cbPrime1); + ok(rsablob->cbPrime2 == 128, "got %u\n", rsablob->cbPrime2); + size2 = sizeof(*rsablob) + rsablob->cbPublicExp + rsablob->cbModulus * 2 + rsablob->cbPrime1 * 3 + rsablob->cbPrime2 * 2; + ok(size == size2, "got %u expected %u\n", size2, size); + free(buf); + + /* import/export public key */ + size = 0; + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size, "size not set\n"); + + buf = malloc(size); + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAPUBLIC_BLOB, buf, size, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + rsablob = (BCRYPT_RSAKEY_BLOB *)buf; + ok(rsablob->Magic == BCRYPT_RSAPUBLIC_MAGIC, "got 0x%x\n", rsablob->Magic); + ok(rsablob->BitLength == 2048, "got %u\n", rsablob->BitLength); + ok(rsablob->cbPublicExp == 3, "got %u\n", rsablob->cbPublicExp); + ok(rsablob->cbModulus == 256, "got %u\n", rsablob->cbModulus); + ok(!rsablob->cbPrime1, "got %u\n", rsablob->cbPrime1); + ok(!rsablob->cbPrime2, "got %u\n", rsablob->cbPrime2); + ok(size == sizeof(*rsablob) + rsablob->cbPublicExp + rsablob->cbModulus, "got %u\n", size); + ret = pBCryptDestroyKey(key); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_RSAPUBLIC_BLOB, &key, rsaPublicBlobWithInvalidPublicExpSize, + sizeof(rsaPublicBlobWithInvalidPublicExpSize), 0); + ok(ret == NTE_BAD_DATA, "got 0x%x\n", ret); + + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_RSAPUBLIC_BLOB, &key, buf, size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + free(buf); + + ret = pBCryptVerifySignature(key, &pad, hash, sizeof(hash), sig, sizeof(sig), BCRYPT_PAD_PKCS1); + ok(!ret, "got 0x%x\n", ret); + ret = pBCryptVerifySignature(key, &pad_pss, hash48, sizeof(hash48), sig_pss, sizeof(sig_pss), BCRYPT_PAD_PSS); + ok(!ret, "got 0x%x\n", ret); + ret = pBCryptDestroyKey(key); + ok(!ret, "got 0x%x\n", ret); + + /* import/export private key */ + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_RSAPRIVATE_BLOB, &key, rsaPrivateBlob, sizeof(rsaPrivateBlob), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptFinalizeKeyPair(key, 0); + ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + + size = 0; + buf = malloc(sizeof(rsaPrivateBlob)); + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAPRIVATE_BLOB, buf, sizeof(rsaPrivateBlob), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(rsaPrivateBlob), "got %u\n", size); + ok(!memcmp(buf, rsaPrivateBlob, size), "wrong data\n"); + free(buf); + pBCryptDestroyKey(key); + + /* import/export full private key */ + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_RSAFULLPRIVATE_BLOB, &key, rsaFullPrivateBlob, sizeof(rsaFullPrivateBlob), 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + size = 0; + buf = malloc(sizeof(rsaFullPrivateBlob)); + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAFULLPRIVATE_BLOB, buf, sizeof(rsaFullPrivateBlob), &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(rsaFullPrivateBlob), "got %u\n", size); + ok(!memcmp(buf, rsaFullPrivateBlob, size), "wrong data\n"); + free(buf); + pBCryptDestroyKey(key); + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(!ret, "got 0x%x\n", ret); +} + +static void test_RSA_SIGN(void) +{ + BCRYPT_PKCS1_PADDING_INFO pad; + BCRYPT_ALG_HANDLE alg = NULL; + BCRYPT_KEY_HANDLE key = NULL; + BCRYPT_RSAKEY_BLOB *rsablob; + NTSTATUS ret; + ULONG size, size2; + BYTE *buf, buf2[sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(rsaPublicBlob)]; + + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_RSA_SIGN_ALGORITHM, NULL, 0); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_RSAPUBLIC_BLOB, &key, rsaPublicBlob, sizeof(rsaPublicBlob), 0); + ok(!ret, "BCryptImportKeyPair failed: 0x%x\n", ret); + + memset(buf2, 0xcc, sizeof(buf2)); + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAPUBLIC_BLOB, buf2, sizeof(buf2), &size, 0); + ok(!ret, "got 0x%x\n", ret); + rsablob = (BCRYPT_RSAKEY_BLOB *)buf2; + ok(rsablob->Magic == BCRYPT_RSAPUBLIC_MAGIC, "got 0x%x\n", rsablob->Magic); + ok(rsablob->BitLength == 2048, "got %u\n", rsablob->BitLength); + ok(rsablob->cbPublicExp == 3, "got %u\n", rsablob->cbPublicExp); + ok(rsablob->cbModulus == 256, "got %u\n", rsablob->cbModulus); + ok(rsablob->cbPrime1 == 0, "got %u\n", rsablob->cbPrime1); + ok(rsablob->cbPrime2 == 0, "got %u\n", rsablob->cbPrime2); + size2 = sizeof(*rsablob) + rsablob->cbPublicExp + rsablob->cbModulus + rsablob->cbPrime1 + rsablob->cbPrime2; + ok(size == size2, "got %u expected %u\n", size2, size); + + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAPRIVATE_BLOB, buf2, sizeof(buf2), &size, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + pad.pszAlgId = NULL; + ret = pBCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); + ok(ret == STATUS_INVALID_SIGNATURE, "BCryptVerifySignature failed: 0x%x\n", ret); + + pad.pszAlgId = BCRYPT_SHA1_ALGORITHM; + ret = pBCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); + ok(!ret, "BCryptVerifySignature failed: 0x%x\n", ret); + + ret = pBCryptVerifySignature(key, NULL, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); + ok(ret == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got 0x%x\n", ret); + + pad.pszAlgId = BCRYPT_SHA1_ALGORITHM; + ret = pBCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), 0); + ok(ret == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got 0x%x\n", ret); + + ret = pBCryptVerifySignature(key, NULL, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), 0); + ok(ret == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got 0x%x\n", ret); + + pad.pszAlgId = BCRYPT_AES_ALGORITHM; + ret = pBCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); + ok(ret == STATUS_NOT_SUPPORTED, "Expected STATUS_NOT_SUPPORTED, got 0x%x\n", ret); + + pad.pszAlgId = NULL; + ret = pBCryptVerifySignature(key, &pad, rsaHash, sizeof(rsaHash), rsaSignature, sizeof(rsaSignature), BCRYPT_PAD_PKCS1); + ok(ret == STATUS_INVALID_SIGNATURE, "Expected STATUS_INVALID_SIGNATURE, got 0x%x\n", ret); + + ret = pBCryptDestroyKey(key); + ok(!ret, "BCryptDestroyKey failed: 0x%x\n", ret); + + /* export private key */ + ret = pBCryptGenerateKeyPair(alg, &key, 512, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptFinalizeKeyPair(key, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + size = 0; + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAPRIVATE_BLOB, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size, "size not set\n"); + + buf = malloc(size); + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAPRIVATE_BLOB, buf, size, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + rsablob = (BCRYPT_RSAKEY_BLOB *)buf; + ok(rsablob->Magic == BCRYPT_RSAPRIVATE_MAGIC, "got 0x%x\n", rsablob->Magic); + ok(rsablob->BitLength == 512, "got %u\n", rsablob->BitLength); + ok(rsablob->cbPublicExp == 3, "got %u\n", rsablob->cbPublicExp); + ok(rsablob->cbModulus == 64, "got %u\n", rsablob->cbModulus); + ok(rsablob->cbPrime1 == 32, "got %u\n", rsablob->cbPrime1); + ok(rsablob->cbPrime2 == 32, "got %u\n", rsablob->cbPrime2); + size2 = sizeof(*rsablob) + rsablob->cbPublicExp + rsablob->cbModulus + rsablob->cbPrime1 + rsablob->cbPrime2; + ok(size == size2, "got %u expected %u\n", size2, size); + free(buf); + + size = 0; + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAFULLPRIVATE_BLOB, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size, "size not set\n"); + + buf = malloc(size); + ret = pBCryptExportKey(key, NULL, BCRYPT_RSAFULLPRIVATE_BLOB, buf, size, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + rsablob = (BCRYPT_RSAKEY_BLOB *)buf; + ok(rsablob->Magic == BCRYPT_RSAFULLPRIVATE_MAGIC, "got 0x%x\n", rsablob->Magic); + ok(rsablob->BitLength == 512, "got %u\n", rsablob->BitLength); + ok(rsablob->cbPublicExp == 3, "got %u\n", rsablob->cbPublicExp); + ok(rsablob->cbModulus == 64, "got %u\n", rsablob->cbModulus); + ok(rsablob->cbPrime1 == 32, "got %u\n", rsablob->cbPrime1); + ok(rsablob->cbPrime2 == 32, "got %u\n", rsablob->cbPrime2); + size2 = sizeof(*rsablob) + rsablob->cbPublicExp + rsablob->cbModulus * 2 + rsablob->cbPrime1 * 3 + rsablob->cbPrime2 * 2; + ok(size == size2, "got %u expected %u\n", size2, size); + free(buf); + pBCryptDestroyKey(key); + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(!ret, "BCryptCloseAlgorithmProvider failed: 0x%x\n", ret); +} + +static BYTE eccprivkey[] = +{ + 0x45, 0x43, 0x4b, 0x32, 0x20, 0x00, 0x00, 0x00, + 0xfb, 0xbd, 0x3d, 0x20, 0x1b, 0x6d, 0x66, 0xb3, 0x7c, 0x9f, 0x89, 0xf3, 0xe4, 0x41, 0x16, 0xa5, + 0x68, 0x52, 0x77, 0xac, 0xab, 0x55, 0xb2, 0x6c, 0xb0, 0x23, 0x55, 0xcb, 0x96, 0x14, 0xfd, 0x0b, + 0x1c, 0xef, 0xdf, 0x07, 0x6d, 0x31, 0xaf, 0x39, 0xce, 0x8c, 0x8f, 0x9d, 0x75, 0xd0, 0x7b, 0xea, + 0x81, 0xdc, 0x40, 0x21, 0x1f, 0x58, 0x22, 0x5f, 0x72, 0x55, 0xfc, 0x58, 0x8a, 0xeb, 0x88, 0x5d, + 0x02, 0x09, 0x90, 0xd2, 0xe3, 0x36, 0xac, 0xfe, 0x83, 0x13, 0x6c, 0x88, 0x1a, 0xab, 0x9b, 0xdd, + 0xaa, 0x8a, 0xee, 0x69, 0x9a, 0x6a, 0x62, 0x86, 0x6a, 0x13, 0x69, 0x88, 0xb7, 0xd5, 0xa3, 0xcd +}; + +static BYTE ecdh_pubkey[] = +{ + 0x45, 0x43, 0x4b, 0x31, 0x20, 0x00, 0x00, 0x00, + 0x07, 0x61, 0x9d, 0x49, 0x63, 0x6b, 0x96, 0x94, 0xd1, 0x8f, 0xd1, 0x48, 0xcc, 0xcf, 0x72, 0x4d, + 0xff, 0x43, 0xf4, 0x97, 0x0f, 0xa3, 0x8a, 0x72, 0xe9, 0xe0, 0xba, 0x87, 0x6d, 0xc3, 0x62, 0x15, + 0xae, 0x65, 0xdd, 0x31, 0x51, 0xfc, 0x3b, 0xc9, 0x59, 0xa1, 0x0a, 0x92, 0x17, 0x2b, 0x64, 0x55, + 0x03, 0x3e, 0x62, 0x1d, 0xac, 0x3e, 0x37, 0x40, 0x6a, 0x4c, 0xb6, 0x21, 0x3f, 0x73, 0x5c, 0xf5 +}; + +/* little endian */ +static BYTE ecdh_secret[] = +{ + 0x48, 0xb0, 0x11, 0xdb, 0x69, 0x4e, 0xb4, 0xf4, 0xf5, 0x3e, 0xe1, 0x9b, 0xca, 0x00, 0x04, 0xc8, + 0x9b, 0x69, 0xaf, 0xd1, 0xaf, 0x1f, 0xc2, 0xd7, 0x83, 0x0a, 0xb7, 0xf8, 0x4f, 0x24, 0x32, 0x8e, +}; + +BCryptBuffer hash_param_buffers[] = +{ +{ + sizeof(BCRYPT_SHA1_ALGORITHM), + KDF_HASH_ALGORITHM, + (void *)BCRYPT_SHA1_ALGORITHM, +} +}; + +BCryptBufferDesc hash_params = +{ + BCRYPTBUFFER_VERSION, + ARRAY_SIZE(hash_param_buffers), + hash_param_buffers, +}; + +static BYTE hashed_secret[] = +{ + 0x1b, 0xe7, 0xbf, 0x0f, 0x65, 0x1e, 0xd0, 0x07, 0xf9, 0xf4, 0x77, 0x48, 0x48, 0x39, 0xd0, 0xf8, + 0xf3, 0xce, 0xfc, 0x89 +}; + +static void test_ECDH(void) +{ + BYTE *buf; + BCRYPT_ECCKEY_BLOB *ecckey; + BCRYPT_ALG_HANDLE alg; + BCRYPT_KEY_HANDLE key, privkey, pubkey; + BCRYPT_SECRET_HANDLE secret; + NTSTATUS status; + ULONG size; + + status = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDH_P256_ALGORITHM, NULL, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + key = NULL; + status = pBCryptGenerateKeyPair(alg, &key, 256, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(key != NULL, "key not set\n"); + + status = pBCryptFinalizeKeyPair(key, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + size = 0; + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size, "size not set\n"); + + buf = malloc(size); + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ecckey = (BCRYPT_ECCKEY_BLOB *)buf; + ok(ecckey->dwMagic == BCRYPT_ECDH_PUBLIC_P256_MAGIC, "got 0x%x\n", ecckey->dwMagic); + ok(ecckey->cbKey == 32, "got %u\n", ecckey->cbKey); + ok(size == sizeof(*ecckey) + ecckey->cbKey * 2, "got %u\n", size); + + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_PUBLIC_KEY_BLOB, &pubkey, buf, size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + pBCryptDestroyKey(pubkey); + + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &pubkey, buf, size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + free(buf); + + size = 0; + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPRIVATE_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size, "size not set\n"); + + buf = malloc(size); + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPRIVATE_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ecckey = (BCRYPT_ECCKEY_BLOB *)buf; + ok(ecckey->dwMagic == BCRYPT_ECDH_PRIVATE_P256_MAGIC, "got 0x%x\n", ecckey->dwMagic); + ok(ecckey->cbKey == 32, "got %u\n", ecckey->cbKey); + ok(size == sizeof(*ecckey) + ecckey->cbKey * 3, "got %u\n", size); + + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &privkey, buf, size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + free(buf); + pBCryptDestroyKey(pubkey); + pBCryptDestroyKey(privkey); + pBCryptDestroyKey(key); + + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &privkey, eccprivkey, sizeof(eccprivkey), 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + size = 0; + status = pBCryptExportKey(privkey, NULL, BCRYPT_ECCPRIVATE_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size, "size not set\n"); + + buf = malloc(size); + status = pBCryptExportKey(privkey, NULL, BCRYPT_ECCPRIVATE_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size == sizeof(eccprivkey), "got %u\n", size); + ok(!memcmp(buf, eccprivkey, size), "wrong data\n"); + free(buf); + + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &pubkey, ecdh_pubkey, sizeof(ecdh_pubkey), 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + status = pBCryptSecretAgreement(privkey, pubkey, &secret, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + if (status != STATUS_SUCCESS) goto derive_end; + + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, NULL, 0, &size, 0); + if (status == STATUS_NOT_SUPPORTED) /* < win10 */ + { + win_skip("BCRYPT_KDF_RAW_SECRET not supported\n"); + goto raw_secret_end; + } + todo_wine ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + if (status != STATUS_SUCCESS) goto raw_secret_end; + + ok(size == 32, "size of secret key incorrect, got %u, expected 32\n", size); + buf = malloc(size); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(!(memcmp(ecdh_secret, buf, size)), "wrong data\n"); + free(buf); + +raw_secret_end: + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, NULL, 0, &size, 0); + todo_wine ok (status == STATUS_SUCCESS, "got 0x%x\n", status); + if (status != STATUS_SUCCESS) goto derive_end; + + ok (size == 20, "got %u\n", size); + buf = malloc(size); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(!(memcmp(hashed_secret, buf, size)), "wrong data\n"); + free(buf); + + /* ulVersion is not verified */ + hash_params.ulVersion = 0xdeadbeef; + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, NULL, 0, &size, 0); + ok (status == STATUS_SUCCESS, "got 0x%x\n", status); + + hash_params.ulVersion = BCRYPTBUFFER_VERSION; + hash_param_buffers[0].pvBuffer = (void*) L"INVALID"; + hash_param_buffers[0].cbBuffer = sizeof(L"INVALID"); + + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, NULL, 0, &size, 0); + ok (status == STATUS_NOT_SUPPORTED || broken (status == STATUS_NOT_FOUND) /* < win8 */, "got 0x%x\n", status); + + hash_param_buffers[0].pvBuffer = (void*) BCRYPT_RNG_ALGORITHM; + hash_param_buffers[0].cbBuffer = sizeof(BCRYPT_RNG_ALGORITHM); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, NULL, 0, &size, 0); + ok (status == STATUS_NOT_SUPPORTED, "got 0x%x\n", status); + +derive_end: + pBCryptDestroySecret(secret); + pBCryptDestroyKey(pubkey); + pBCryptDestroyKey(privkey); + pBCryptCloseAlgorithmProvider(alg, 0); + + status = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDH_P384_ALGORITHM, NULL, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + key = NULL; + status = pBCryptGenerateKeyPair(alg, &key, 384, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(key != NULL, "key not set\n"); + + status = pBCryptFinalizeKeyPair(key, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + size = 0; + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size, "size not set\n"); + + buf = malloc(size); + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ecckey = (BCRYPT_ECCKEY_BLOB *)buf; + ok(ecckey->dwMagic == BCRYPT_ECDH_PUBLIC_P384_MAGIC, "got 0x%x\n", ecckey->dwMagic); + ok(ecckey->cbKey == 48, "got %u\n", ecckey->cbKey); + ok(size == sizeof(*ecckey) + ecckey->cbKey * 2, "got %u\n", size); + + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_PUBLIC_KEY_BLOB, &pubkey, buf, size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + pBCryptDestroyKey(pubkey); + + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &pubkey, buf, size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + free(buf); + pBCryptDestroyKey(pubkey); + pBCryptCloseAlgorithmProvider(alg, 0); +} + +static BYTE dh_pubkey[] = +{ + /* BCRYPT_DH_KEY_BLOB */ + 0x44, 0x48, 0x50, 0x42, 0x40, 0x00, 0x00, 0x00, + /* p */ + 0xcf, 0x18, 0xe9, 0xa9, 0xb3, 0x97, 0x59, 0xae, 0x0d, 0xac, 0xf0, 0x99, 0x39, 0xdc, 0xd2, 0xfe, + 0x1e, 0xf3, 0xfc, 0x2c, 0x49, 0xdf, 0x76, 0x89, 0xff, 0x13, 0x57, 0xc7, 0xe6, 0xbd, 0xed, 0xa7, + 0x42, 0xc0, 0xc3, 0xd7, 0x8e, 0x84, 0xa8, 0xdf, 0xcd, 0x52, 0x50, 0x81, 0x73, 0x8a, 0x33, 0x60, + 0xde, 0x6d, 0x56, 0xeb, 0xd5, 0xec, 0x1f, 0x9f, 0x9f, 0xd6, 0x2c, 0xe4, 0x8f, 0xab, 0x58, 0x0b, + /* g */ + 0x5f, 0xc5, 0x50, 0x9a, 0xde, 0xf6, 0x84, 0x48, 0x39, 0xa9, 0xa7, 0xb1, 0x73, 0x0c, 0x56, 0xd4, + 0x28, 0xbb, 0x12, 0x93, 0x51, 0x44, 0x33, 0xdf, 0xa6, 0xe7, 0x7f, 0x0b, 0x3f, 0xe9, 0x41, 0xef, + 0x32, 0x80, 0xcd, 0x8e, 0x2b, 0x38, 0x85, 0x49, 0x4d, 0x0c, 0xcc, 0x74, 0x02, 0x07, 0x92, 0xd3, + 0xe4, 0x3e, 0x37, 0x84, 0x27, 0x1f, 0xa3, 0xad, 0x94, 0x8c, 0xc1, 0xc2, 0x22, 0x99, 0x36, 0xf0, + /* y */ + 0x22, 0xaf, 0x98, 0xeb, 0xd9, 0xc4, 0xb5, 0xbd, 0xe1, 0xab, 0x19, 0x1b, 0xe3, 0x36, 0x20, 0xca, + 0xff, 0xe8, 0x6c, 0x30, 0x96, 0x3c, 0x90, 0x77, 0x0e, 0xe0, 0x96, 0xae, 0xb1, 0x47, 0xd1, 0x52, + 0x2c, 0xc3, 0x65, 0x5e, 0x9b, 0x41, 0x9a, 0xa6, 0xfe, 0xab, 0x54, 0xa0, 0xf0, 0x71, 0xab, 0x6c, + 0xd0, 0x0e, 0x01, 0x08, 0x5b, 0x66, 0xe5, 0x62, 0xd2, 0xe5, 0x5d, 0xae, 0x9c, 0x60, 0xb2, 0xc6, +}; + +static BYTE dh_privkey[] = +{ + /* BCRYPT_DH_KEY_BLOB */ + 0x44, 0x48, 0x50, 0x56, 0x40, 0x00, 0x00, 0x00, + /* p */ + 0xcf, 0x18, 0xe9, 0xa9, 0xb3, 0x97, 0x59, 0xae, 0x0d, 0xac, 0xf0, 0x99, 0x39, 0xdc, 0xd2, 0xfe, + 0x1e, 0xf3, 0xfc, 0x2c, 0x49, 0xdf, 0x76, 0x89, 0xff, 0x13, 0x57, 0xc7, 0xe6, 0xbd, 0xed, 0xa7, + 0x42, 0xc0, 0xc3, 0xd7, 0x8e, 0x84, 0xa8, 0xdf, 0xcd, 0x52, 0x50, 0x81, 0x73, 0x8a, 0x33, 0x60, + 0xde, 0x6d, 0x56, 0xeb, 0xd5, 0xec, 0x1f, 0x9f, 0x9f, 0xd6, 0x2c, 0xe4, 0x8f, 0xab, 0x58, 0x0b, + /* g */ + 0x5f, 0xc5, 0x50, 0x9a, 0xde, 0xf6, 0x84, 0x48, 0x39, 0xa9, 0xa7, 0xb1, 0x73, 0x0c, 0x56, 0xd4, + 0x28, 0xbb, 0x12, 0x93, 0x51, 0x44, 0x33, 0xdf, 0xa6, 0xe7, 0x7f, 0x0b, 0x3f, 0xe9, 0x41, 0xef, + 0x32, 0x80, 0xcd, 0x8e, 0x2b, 0x38, 0x85, 0x49, 0x4d, 0x0c, 0xcc, 0x74, 0x02, 0x07, 0x92, 0xd3, + 0xe4, 0x3e, 0x37, 0x84, 0x27, 0x1f, 0xa3, 0xad, 0x94, 0x8c, 0xc1, 0xc2, 0x22, 0x99, 0x36, 0xf0, + /* y */ + 0x22, 0xaf, 0x98, 0xeb, 0xd9, 0xc4, 0xb5, 0xbd, 0xe1, 0xab, 0x19, 0x1b, 0xe3, 0x36, 0x20, 0xca, + 0xff, 0xe8, 0x6c, 0x30, 0x96, 0x3c, 0x90, 0x77, 0x0e, 0xe0, 0x96, 0xae, 0xb1, 0x47, 0xd1, 0x52, + 0x2c, 0xc3, 0x65, 0x5e, 0x9b, 0x41, 0x9a, 0xa6, 0xfe, 0xab, 0x54, 0xa0, 0xf0, 0x71, 0xab, 0x6c, + 0xd0, 0x0e, 0x01, 0x08, 0x5b, 0x66, 0xe5, 0x62, 0xd2, 0xe5, 0x5d, 0xae, 0x9c, 0x60, 0xb2, 0xc6, + /* x */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x71, 0x3e, 0x82, + 0x8b, 0xea, 0x11, 0x77, 0xc4, 0xb4, 0x62, 0xc7, 0x4d, 0xff, 0x0f, 0x63, 0xd9, 0xe2, 0xda, 0xab, +}; + +static BYTE dh_secret[] = +{ + 0x73, 0x84, 0x62, 0xc1, 0x9a, 0x9c, 0xc2, 0x91, 0x9f, 0xc1, 0xc2, 0x94, 0x0c, 0xa8, 0x2f, 0x58, + 0xac, 0x50, 0xcd, 0xd5, 0x29, 0x43, 0x41, 0x8e, 0x5d, 0xca, 0x73, 0x55, 0x4d, 0x46, 0x50, 0xe5, + 0xb7, 0x34, 0xa9, 0xcb, 0x3a, 0x18, 0x68, 0x99, 0x30, 0xef, 0x58, 0x26, 0xd3, 0x03, 0x61, 0x02, + 0x17, 0xb7, 0xba, 0x01, 0xbc, 0xae, 0xdf, 0x3f, 0xb5, 0xb5, 0x4a, 0xb0, 0x08, 0xe5, 0xea, 0xc3, +}; + +static BYTE dh_hashed_secret[] = +{ + 0xa7, 0xfc, 0xff, 0x21, 0xb3, 0xd1, 0x46, 0xb8, 0x21, 0x3d, 0xc6, 0xd4, 0xe3, 0x61, 0x97, 0x5e, + 0xb5, 0x0a, 0xfe, 0x8f, +}; + +BCryptBuffer dh_hash_param_buffers[] = +{ + { + sizeof(BCRYPT_SHA1_ALGORITHM), + KDF_HASH_ALGORITHM, + (void *)BCRYPT_SHA1_ALGORITHM, + } +}; + +BCryptBufferDesc dh_hash_params = +{ + BCRYPTBUFFER_VERSION, + ARRAY_SIZE(dh_hash_param_buffers), + dh_hash_param_buffers, +}; + +static void test_DH(void) +{ + UCHAR hash[20]; + BCRYPT_KEY_HANDLE key, pubkey, privkey; + BCRYPT_SECRET_HANDLE secret; + BCRYPT_DH_KEY_BLOB *dhkey; + NTSTATUS status; + UCHAR *buf; + ULONG size; + + if (!pBCryptHash) /* < Win10 */ + { + win_skip("broken DH detected\n"); + return; + } + + key = NULL; + status = pBCryptGenerateKeyPair(BCRYPT_DH_ALG_HANDLE, &key, 512, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(key != NULL, "key not set\n"); + + status = pBCryptFinalizeKeyPair(key, 0); + todo_wine ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + if (status != STATUS_SUCCESS) + { + pBCryptDestroyKey(key); + return; + } + + size = 0; + status = pBCryptExportKey(key, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size, "size not set\n"); + + buf = malloc(size); + status = pBCryptExportKey(key, NULL, BCRYPT_DH_PUBLIC_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + dhkey = (BCRYPT_DH_KEY_BLOB *)buf; + ok(dhkey->dwMagic == BCRYPT_DH_PUBLIC_MAGIC, "got 0x%x\n", dhkey->dwMagic); + ok(dhkey->cbKey == 64, "got %u\n", dhkey->cbKey); + ok(size == sizeof(*dhkey) + dhkey->cbKey * 3, "got %u\n", size); + + status = pBCryptImportKeyPair(BCRYPT_DH_ALG_HANDLE, NULL, BCRYPT_DH_PUBLIC_BLOB, &pubkey, buf, size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + free(buf); + pBCryptDestroyKey(pubkey); + + size = 0; + status = pBCryptExportKey(key, NULL, BCRYPT_DH_PRIVATE_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size == sizeof(*dhkey) + 64 * 4, "size not set\n"); + + buf = calloc(1, size); + status = pBCryptExportKey(key, NULL, BCRYPT_DH_PRIVATE_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + dhkey = (BCRYPT_DH_KEY_BLOB *)buf; + ok(dhkey->dwMagic == BCRYPT_DH_PRIVATE_MAGIC, "got 0x%x\n", dhkey->dwMagic); + ok(dhkey->cbKey == 64, "got %u\n", dhkey->cbKey); + ok(size == sizeof(*dhkey) + dhkey->cbKey * 4, "got %u\n", size); + pBCryptDestroyKey(key); + + status = pBCryptImportKeyPair(BCRYPT_DH_ALG_HANDLE, NULL, BCRYPT_DH_PRIVATE_BLOB, &privkey, buf, size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + free(buf); + pBCryptDestroyKey(privkey); + + status = pBCryptImportKeyPair(BCRYPT_DH_ALG_HANDLE, NULL, BCRYPT_DH_PRIVATE_BLOB, &privkey, dh_privkey, + sizeof(dh_privkey), 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + size = 0; + status = pBCryptExportKey(privkey, NULL, BCRYPT_DH_PRIVATE_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size, "size not set\n"); + + buf = malloc(size); + status = pBCryptExportKey(privkey, NULL, BCRYPT_DH_PRIVATE_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size == sizeof(dh_privkey), "got %u\n", size); + ok(!memcmp(buf, dh_privkey, size), "wrong data\n"); + free(buf); + + status = pBCryptImportKeyPair(BCRYPT_DH_ALG_HANDLE, NULL, BCRYPT_DH_PUBLIC_BLOB, &pubkey, dh_pubkey, + sizeof(dh_pubkey), 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + size = 0; + status = pBCryptExportKey(privkey, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size, "size not set\n"); + + buf = malloc(size); + status = pBCryptExportKey(privkey, NULL, BCRYPT_DH_PUBLIC_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size == sizeof(dh_pubkey), "got %u\n", size); + ok(!memcmp(buf, dh_pubkey, size), "wrong data\n"); + free(buf); + + status = pBCryptSignHash(privkey, NULL, hash, sizeof(hash), NULL, 0, &size, 0); + ok(status == STATUS_NOT_SUPPORTED, "got 0x%x\n", status); + + status = pBCryptEncrypt(privkey, NULL, 0, NULL, NULL, 0, NULL, 0, &size, 0); + ok(status == STATUS_NOT_SUPPORTED, "got 0x%x\n", status); + + status = pBCryptSecretAgreement(privkey, pubkey, &secret, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + pBCryptDestroyKey(pubkey); + pBCryptDestroyKey(privkey); + + size = 0; + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, NULL, 0, &size, 0); + if (status == STATUS_NOT_SUPPORTED) + { + win_skip("BCRYPT_KDF_RAW_SECRET not supported\n"); /* < win10 */ + pBCryptDestroySecret(secret); + return; + } + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size == 64, "got %u\n", size); + + buf = calloc(1, size); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(!memcmp(dh_secret, buf, size), "wrong data\n"); + free(buf); + + size = 0; + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, NULL, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size == 20, "got %u\n", size); + + size = 0; + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &dh_hash_params, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(size == 20, "got %u\n", size); + + buf = calloc(1, size); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &dh_hash_params, buf, size, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "got 0x%x\n", status); + + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &dh_hash_params, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(!memcmp(dh_hashed_secret, buf, size), "wrong data\n"); + ok(size == 20, "got %u\n", size); + + memset(buf, 0, 20); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &dh_hash_params, buf, 10, &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(!memcmp(dh_hashed_secret, buf, size), "wrong data\n"); + ok(size == 10, "got %u\n", size); + free(buf); + + pBCryptDestroySecret(secret); +} + +#ifndef NCRYPT_SCHANNEL_INTERFACE +#define NCRYPT_SCHANNEL_INTERFACE 0x00010002 +#endif +static void test_BCryptEnumContextFunctions(void) +{ + CRYPT_CONTEXT_FUNCTIONS *buffer; + NTSTATUS status; + ULONG buflen; + + buffer = NULL; + status = pBCryptEnumContextFunctions( CRYPT_LOCAL, L"SSL", NCRYPT_SCHANNEL_INTERFACE, &buflen, &buffer ); + todo_wine ok( status == STATUS_SUCCESS, "got 0x%08x\n", status); + if (status == STATUS_SUCCESS) pBCryptFreeBuffer( buffer ); +} + +static BYTE rsapublic[] = +{ + 0x52, 0x53, 0x41, 0x31, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xd5, 0xfe, 0xf6, 0x7a, 0x9a, 0xa1, 0x2d, 0xcf, 0x98, + 0x60, 0xca, 0x38, 0x60, 0x0b, 0x74, 0x4c, 0x7e, 0xa1, 0x42, 0x64, 0xad, 0x05, 0xa5, 0x29, 0x25, 0xcb, 0xd5, + 0x9c, 0xaf, 0x6f, 0x63, 0x85, 0x6d, 0x5b, 0x59, 0xe5, 0x17, 0x8f, 0xf9, 0x18, 0x90, 0xa7, 0x63, 0xae, 0xe0, + 0x3a, 0x62, 0xf7, 0x98, 0x57, 0xe9, 0x91, 0xda, 0xfb, 0xd9, 0x36, 0x45, 0xe4, 0x9e, 0x75, 0xf6, 0x73, 0xc4, + 0x99, 0x23, 0x21, 0x1b, 0x3d, 0xe1, 0xe0, 0xa6, 0xa0, 0x4a, 0x50, 0x2a, 0xcb, 0x2a, 0x50, 0xf0, 0x8b, 0x70, + 0x9c, 0xe4, 0x1a, 0x14, 0x3b, 0xbe, 0x35, 0xa5, 0x5a, 0x91, 0xa3, 0xa1, 0x82, 0xea, 0x84, 0x4d, 0xe8, 0x62, + 0x3b, 0x11, 0xec, 0x61, 0x09, 0x6c, 0xfe, 0xb2, 0xcc, 0x4b, 0xa8, 0xff, 0xaf, 0x73, 0x72, 0x05, 0x4e, 0x7e, + 0xe5, 0x73, 0xdf, 0x24, 0xcf, 0x7f, 0x5d, 0xaf, 0x8a, 0xf0, 0xd8, 0xcb, 0x08, 0x1e, 0xf2, 0x36, 0x70, 0x8d, + 0x1b, 0x9e, 0xc8, 0x98, 0x60, 0x54, 0xeb, 0x45, 0x34, 0x21, 0x43, 0x4d, 0x42, 0x0a, 0x3a, 0x2d, 0x0f, 0x0e, + 0xd6, 0x0d, 0xe4, 0x2e, 0x8c, 0x31, 0x87, 0xa8, 0x09, 0x89, 0x61, 0x16, 0xca, 0x5b, 0xbe, 0x76, 0x69, 0xbb, + 0xfd, 0x91, 0x63, 0xd2, 0x66, 0x57, 0x08, 0xef, 0xe2, 0x40, 0x67, 0xd7, 0x7f, 0x50, 0x15, 0x42, 0x33, 0x97, + 0x54, 0x73, 0x47, 0xe7, 0x9c, 0x14, 0xa8, 0xb0, 0x3d, 0xc9, 0x23, 0xb0, 0x27, 0x3b, 0xe7, 0xdd, 0x5f, 0xd1, + 0x4f, 0x31, 0x10, 0x7d, 0xdd, 0x69, 0x8e, 0xde, 0xa3, 0xe8, 0x92, 0x00, 0xfa, 0xa5, 0xa4, 0x40, 0x51, 0x23, + 0x82, 0x84, 0xc7, 0xce, 0x19, 0x61, 0x26, 0xf1, 0xae, 0xf3, 0x90, 0x93, 0x98, 0x56, 0x23, 0x9a, 0xd1, 0xbd, + 0xf2, 0xdf, 0xfd, 0x13, 0x9c, 0x30, 0x07, 0xf9, 0x5a, 0x2e, 0x00, 0xc6, 0x1f +}; + +static void test_BCryptSignHash(void) +{ + static UCHAR hash[] = + {0x7e,0xe3,0x74,0xe7,0xc5,0x0b,0x6b,0x70,0xdb,0xab,0x32,0x6d,0x1d,0x51,0xd6,0x74,0x79,0x8e,0x5b,0x4b}; + static UCHAR hash_sha256[] = + {0x25,0x2f,0x10,0xc8,0x36,0x10,0xeb,0xca,0x1a,0x05,0x9c,0x0b,0xae,0x82,0x55,0xeb,0xa2,0xf9,0x5b,0xe4, + 0xd1,0xd7,0xbC,0xfA,0x89,0xd7,0x24,0x8a,0x82,0xd9,0xf1,0x11}; + BCRYPT_PKCS1_PADDING_INFO pad; + BCRYPT_ALG_HANDLE alg; + BCRYPT_KEY_HANDLE key; + UCHAR sig[256]; + NTSTATUS ret; + ULONG len; + + /* RSA */ + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_RSA_ALGORITHM, NULL, 0); + ok(!ret, "got 0x%x\n", ret); + + /* public key */ + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_RSAPUBLIC_BLOB, &key, rsapublic, sizeof(rsapublic), 0); + ok(!ret, "got 0x%x\n", ret); + + len = 0; + pad.pszAlgId = BCRYPT_SHA1_ALGORITHM; + ret = pBCryptSignHash(key, &pad, NULL, 0, NULL, 0, &len, BCRYPT_PAD_PKCS1); + ok(!ret, "got 0x%x\n", ret); + ok(len == 256, "got %u\n", len); + + /* test len return when only output is NULL, as described in BCryptSignHash doc */ + ret = pBCryptSignHash(key, &pad, hash, sizeof(hash), NULL, 0, &len, BCRYPT_PAD_PKCS1); + ok(!ret, "got 0x%x\n", ret); + ok(len == 256, "got %u\n", len); + + len = 0; + ret = pBCryptSignHash(key, &pad, hash, sizeof(hash), sig, sizeof(sig), &len, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_INVALID_PARAMETER || broken(ret == STATUS_INTERNAL_ERROR) /* < win7 */, "got 0x%x\n", ret); + ret = pBCryptDestroyKey(key); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptGenerateKeyPair(alg, &key, 512, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptFinalizeKeyPair(key, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptFinalizeKeyPair(key, 0); + ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + + len = 0; + memset(sig, 0, sizeof(sig)); + + /* inference of padding info on RSA not supported */ + ret = pBCryptSignHash(key, NULL, hash, sizeof(hash), sig, sizeof(sig), &len, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + ret = pBCryptSignHash(key, &pad, hash, sizeof(hash), sig, 0, &len, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got 0x%x\n", ret); + + ret = pBCryptSignHash(key, &pad, hash, sizeof(hash), sig, sizeof(sig), &len, BCRYPT_PAD_PKCS1); + ok(!ret, "got 0x%x\n", ret); + ok(len == 64, "got %u\n", len); + + ret = pBCryptVerifySignature(key, &pad, hash, sizeof(hash), sig, len, BCRYPT_PAD_PKCS1); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptDestroyKey(key); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(!ret, "got 0x%x\n", ret); + + /* ECDSA */ + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDSA_P256_ALGORITHM, NULL, 0); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptGenerateKeyPair(alg, &key, 256, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptFinalizeKeyPair(key, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + memset(sig, 0, sizeof(sig)); + len = 0; + + /* automatically detects padding info */ + ret = pBCryptSignHash(key, NULL, hash, sizeof(hash), sig, sizeof(sig), &len, 0); + ok (!ret, "got 0x%x\n", ret); + ok (len == 64, "got %u\n", len); + + ret = pBCryptVerifySignature(key, NULL, hash, sizeof(hash), sig, len, 0); + ok(!ret, "got 0x%x\n", ret); + + /* mismatch info (SHA-1 != SHA-256) */ + ret = pBCryptSignHash(key, &pad, hash_sha256, sizeof(hash_sha256), sig, sizeof(sig), &len, BCRYPT_PAD_PKCS1); + ok (ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + ret = pBCryptDestroyKey(key); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(!ret, "got 0x%x\n", ret); +} + +static void test_BCryptEnumAlgorithms(void) +{ + BCRYPT_ALGORITHM_IDENTIFIER *list; + NTSTATUS ret; + ULONG count, op; + + ret = pBCryptEnumAlgorithms(0, NULL, NULL, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + ret = pBCryptEnumAlgorithms(0, &count, NULL, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + ret = pBCryptEnumAlgorithms(0, NULL, &list, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + ret = pBCryptEnumAlgorithms(~0u, &count, &list, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + count = 0; + list = NULL; + ret = pBCryptEnumAlgorithms(0, &count, &list, 0); + ok(!ret, "got 0x%x\n", ret); + ok(list != NULL, "NULL list\n"); + ok(count, "got %u\n", count); + pBCryptFreeBuffer( list ); + + op = BCRYPT_CIPHER_OPERATION | BCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION | BCRYPT_SIGNATURE_OPERATION | + BCRYPT_SECRET_AGREEMENT_OPERATION; + count = 0; + list = NULL; + ret = pBCryptEnumAlgorithms(op, &count, &list, 0); + ok(!ret, "got 0x%x\n", ret); + ok(list != NULL, "NULL list\n"); + ok(count, "got %u\n", count); + pBCryptFreeBuffer( list ); +} + +static void test_aes_vector(void) +{ + static const UCHAR secret[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10}; + static const UCHAR expect[] = {0xb0,0xcb,0xf5,0x80,0xd4,0xe3,0x55,0x23,0x6e,0x19,0x5b,0xdb,0xfe,0xe0,0x6c,0xd3}; + static const UCHAR expect2[] = {0x06,0x0c,0x81,0xab,0xd4,0x28,0x80,0x42,0xce,0x30,0x56,0x17,0x15,0x00,0x9e,0xc1}; + static const UCHAR expect3[] = {0x3e,0x99,0xbf,0x02,0xf5,0xd3,0xb8,0x81,0x91,0x4d,0x93,0xea,0xd4,0x92,0x93,0x46}; + static UCHAR iv[16], input[] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p'}; + UCHAR output[16]; + BCRYPT_ALG_HANDLE alg; + BCRYPT_KEY_HANDLE key; + UCHAR data[sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + sizeof(secret)]; + BCRYPT_KEY_DATA_BLOB_HEADER *blob = (BCRYPT_KEY_DATA_BLOB_HEADER *)data; + ULONG size; + NTSTATUS ret; + + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_AES_ALGORITHM, NULL, 0); + ok(!ret, "got 0x%x\n", ret); + + size = sizeof(BCRYPT_CHAIN_MODE_CBC); + ret = pBCryptSetProperty(alg, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_CBC, size, 0); + ok(!ret, "got 0x%x\n", ret); + + blob->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC; + blob->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1; + blob->cbKeyData = sizeof(secret); + memcpy(data + sizeof(*blob), secret, sizeof(secret)); + size = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + sizeof(secret); + ret = pBCryptImportKey(alg, NULL, BCRYPT_KEY_DATA_BLOB, &key, NULL, 0, data, size, 0); + ok(!ret, "got 0x%x\n", ret); + + /* zero initialization vector */ + size = 0; + memset(output, 0, sizeof(output)); + ret = pBCryptEncrypt(key, input, sizeof(input), NULL, iv, sizeof(iv), output, sizeof(output), &size, 0); + ok(!ret, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(output, expect, sizeof(expect)), "wrong cipher text\n"); + + /* same initialization vector */ + size = 0; + memset(output, 0, sizeof(output)); + ret = pBCryptEncrypt(key, input, sizeof(input), NULL, iv, sizeof(iv), output, sizeof(output), &size, 0); + ok(!ret, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(output, expect2, sizeof(expect2)), "wrong cipher text\n"); + + /* different initialization vector */ + iv[0] = 0x1; + size = 0; + memset(output, 0, sizeof(output)); + ret = pBCryptEncrypt(key, input, sizeof(input), NULL, iv, sizeof(iv), output, sizeof(output), &size, 0); + ok(!ret, "got 0x%x\n", ret); + ok(size == 16, "got %u\n", size); + ok(!memcmp(output, expect3, sizeof(expect3)), "wrong cipher text\n"); + + ret = pBCryptDestroyKey(key); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(!ret, "got 0x%x\n", ret); +} + +static void test_BcryptDeriveKeyCapi(void) +{ + static const UCHAR expect[] = + {0xda,0x39,0xa3,0xee,0x5e,0x6b,0x4b,0x0d,0x32,0x55,0xbf,0xef,0x95,0x60,0x18,0x90,0xaf,0xd8,0x07,0x09}; + static const UCHAR expect2[] = + {0x9b,0x03,0x17,0x41,0xf4,0x75,0x11,0xac,0xff,0x22,0xee,0x40,0xbb,0xe8,0xf9,0x74,0x17,0x26,0xb6,0xf2, + 0xf8,0xc7,0x88,0x02,0x9a,0xdc,0x0d,0xd7,0x83,0x58,0xea,0x65,0x2e,0x8b,0x85,0xc6,0xdb,0xb7,0xed,0x1c}; + BCRYPT_ALG_HANDLE alg; + BCRYPT_HASH_HANDLE hash; + UCHAR key[40]; + NTSTATUS ret; + + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA1_ALGORITHM, NULL, 0); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptCreateHash(alg, &hash, NULL, 0, NULL, 0, 0); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptDeriveKeyCapi(NULL, NULL, NULL, 0, 0); + ok(ret == STATUS_INVALID_PARAMETER || ret == STATUS_INVALID_HANDLE /* win7 */, "got 0x%x\n", ret); + + ret = pBCryptDeriveKeyCapi(hash, NULL, NULL, 0, 0); + ok(ret == STATUS_INVALID_PARAMETER || !ret /* win7 */, "got 0x%x\n", ret); + + ret = pBCryptDestroyHash(hash); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptCreateHash(alg, &hash, NULL, 0, NULL, 0, 0); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptDeriveKeyCapi(hash, NULL, key, 0, 0); + ok(ret == STATUS_INVALID_PARAMETER || !ret /* win7 */, "got 0x%x\n", ret); + + ret = pBCryptDestroyHash(hash); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptCreateHash(alg, &hash, NULL, 0, NULL, 0, 0); + ok(!ret, "got 0x%x\n", ret); + + memset(key, 0, sizeof(key)); + ret = pBCryptDeriveKeyCapi(hash, NULL, key, 41, 0); + ok(ret == STATUS_INVALID_PARAMETER || !ret /* win7 */, "got 0x%x\n", ret); + if (!ret) + ok(!memcmp(key, expect, sizeof(expect) - 1), "wrong key data\n"); + + ret = pBCryptDestroyHash(hash); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptCreateHash(alg, &hash, NULL, 0, NULL, 0, 0); + ok(!ret, "got 0x%x\n", ret); + + memset(key, 0, sizeof(key)); + ret = pBCryptDeriveKeyCapi(hash, NULL, key, 20, 0); + ok(!ret, "got 0x%x\n", ret); + ok(!memcmp(key, expect, sizeof(expect) - 1), "wrong key data\n"); + + ret = pBCryptDeriveKeyCapi(hash, NULL, key, 20, 0); + todo_wine ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + + ret = pBCryptHashData(hash, NULL, 0, 0); + todo_wine ok(ret == STATUS_INVALID_HANDLE, "got 0x%x\n", ret); + + ret = pBCryptDestroyHash(hash); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptCreateHash(alg, &hash, NULL, 0, NULL, 0, 0); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptHashData(hash, (UCHAR *)"test", 4, 0); + ok(!ret, "got 0x%x\n", ret); + + /* padding */ + memset(key, 0, sizeof(key)); + ret = pBCryptDeriveKeyCapi(hash, NULL, key, 40, 0); + ok(!ret, "got 0x%x\n", ret); + ok(!memcmp(key, expect2, sizeof(expect2) - 1), "wrong key data\n"); + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(!ret, "got 0x%x\n", ret); +} +//#endif +static UCHAR dsaHash[] = +{ + 0x7e,0xe3,0x74,0xe7,0xc5,0x0b,0x6b,0x70,0xdb,0xab,0x32,0x6d,0x1d,0x51,0xd6,0x74,0x79,0x8e,0x5b,0x4b +}; + +static UCHAR dsaSignature[] = +{ + 0x5f,0x95,0x1f,0x08,0x19,0x44,0xa5,0xab,0x28,0x11,0x51,0x68,0x82,0x9b,0xe4,0xc3,0x04,0x1b,0xc9,0xdc, + 0x41,0x2a,0x89,0xd4,0x4a,0x8b,0x86,0xaf,0x98,0x2c,0x59,0x0b,0xd2,0x88,0xf6,0xe8,0x29,0x13,0x84,0x49 +}; + +static UCHAR dsaPublicBlob[] = +{ + 0x44,0x53,0x50,0x42,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x8f,0xd2,0x92,0xbb,0x92,0xb9,0x00,0xc5,0xed, + 0x52,0xcc,0x48,0x4a,0x44,0x1d,0xd3,0x74,0xfb,0x75,0xd1,0x7e,0xb6,0x24,0x9b,0x5d,0x57,0x0a,0x8a,0xc4, + 0x5d,0xab,0x9c,0x26,0x86,0xc6,0x25,0x16,0x20,0xf9,0xa9,0x71,0xbc,0x1d,0x30,0xc4,0xef,0x8c,0xc4,0xdf, + 0x1a,0xaf,0x96,0xdf,0x90,0xd8,0x85,0x9d,0xf9,0x2c,0x86,0x8c,0x91,0x39,0x6c,0x6d,0x11,0x4e,0x53,0x63, + 0x2a,0x2b,0x26,0xa7,0xf9,0x76,0x74,0x51,0xbf,0x08,0x87,0x6f,0xe0,0x71,0x91,0x24,0x8a,0xc2,0x84,0x2d, + 0x84,0x9c,0x5f,0x94,0xaa,0x38,0x53,0x77,0x84,0xba,0xbc,0xff,0x49,0x3a,0x08,0x0f,0x38,0xb5,0x91,0x5c, + 0x06,0x15,0xa4,0x27,0xf4,0xa5,0x59,0xaa,0x1c,0x41,0xa3,0xa0,0xbb,0xf7,0x32,0x86,0xfb,0x94,0x41,0xff, + 0xcd,0xed,0x69,0xeb,0xc6,0x5e,0xb6,0xa8,0x15,0x82,0x3b,0x60,0x1e,0x91,0x55,0xd5,0x2c,0xa5,0x74,0x5a, + 0x65,0x8f,0xc6,0x56,0xc4,0x3f,0x4e,0xe3,0x3a,0x71,0xb2,0x63,0x66,0xa4,0x0d,0x0d,0xf9,0xdd,0x1e,0x48, + 0x81,0xe9,0xbf,0x8f,0xbb,0x85,0x47,0x81,0x68,0x11,0xb5,0x91,0x6b,0xc4,0x05,0xef,0xa3,0xc7,0xbf,0x26, + 0x53,0x4f,0xc4,0x10,0xfd,0xfa,0xed,0x61,0x64,0xd6,0x2e,0xad,0x04,0x3e,0x82,0xed,0xb2,0x22,0x76,0xd0, + 0x44,0xad,0xc1,0x4c,0xde,0x33,0xa3,0x61,0x55,0xec,0x24,0xe5,0x79,0x45,0xcf,0x94,0x39,0x92,0x9f,0xd8, + 0x24,0xce,0x85,0xb9 +}; + +static UCHAR dssKey[] = +{ + 0x07,0x02,0x00,0x00,0x00,0x22,0x00,0x00,0x44,0x53,0x53,0x32,0x00,0x04,0x00,0x00,0x01,0xd1,0xfc,0x7a, + 0x70,0x53,0xb2,0x48,0x70,0x23,0x19,0x1f,0x3c,0xe1,0x26,0x14,0x7e,0x9f,0x0f,0x7f,0x33,0x5e,0x2b,0xf7, + 0xca,0x01,0x74,0x8c,0xb4,0xfd,0xf6,0x44,0x95,0x35,0x56,0xaa,0x4d,0x62,0x48,0xe2,0xd1,0xa2,0x7e,0x6e, + 0xeb,0xd6,0xcc,0x7c,0xe8,0xfd,0x21,0x9a,0xa2,0xfd,0x7a,0x9d,0x1a,0x38,0x69,0x87,0x39,0x5a,0x91,0xc0, + 0x52,0x2b,0x9f,0x2a,0x54,0x78,0x37,0x82,0x9a,0x70,0x57,0xab,0xec,0x93,0x8e,0xac,0x73,0x04,0xe8,0x53, + 0x72,0x72,0x32,0xc6,0xcb,0xef,0x47,0x98,0x3c,0x56,0x49,0x62,0xcb,0xbb,0xe7,0x34,0x84,0xa6,0x72,0x3a, + 0xbe,0x26,0x46,0x86,0xca,0xcb,0x35,0x62,0x4f,0x19,0x18,0x0b,0xb0,0x78,0xae,0xd5,0x42,0xdf,0x26,0xdb, + 0x85,0x63,0x77,0x85,0x01,0x3b,0x32,0xbe,0x5c,0xf8,0x05,0xc8,0xde,0x17,0x7f,0xb9,0x03,0x82,0xfa,0xf1, + 0x9e,0x32,0x73,0xfa,0x8d,0xea,0xa3,0x30,0x48,0xe2,0xdf,0x5a,0xcb,0x83,0x3d,0xff,0x56,0xe9,0xc0,0x94, + 0xf8,0x6d,0xb3,0xaf,0x4a,0x97,0xb9,0x43,0x0e,0xd4,0x28,0x98,0x57,0x2e,0x3a,0xca,0xde,0x6f,0x45,0x0d, + 0xfb,0x58,0xec,0x78,0x34,0x2e,0x46,0x4d,0xfe,0x98,0x02,0xbb,0xef,0x07,0x1a,0x13,0xb6,0xc2,0x2c,0x06, + 0xd9,0x0c,0xc4,0xb0,0x4c,0x3a,0xfc,0x01,0x63,0xb5,0x5a,0x5d,0x2d,0x9c,0x47,0x04,0x67,0x51,0xf2,0x52, + 0xf5,0x82,0x36,0xeb,0x6e,0x66,0x58,0x4c,0x10,0x2c,0x29,0x72,0x4a,0x6f,0x6b,0x6c,0xe0,0x93,0x31,0x42, + 0xf6,0xda,0xfa,0x5b,0x22,0x43,0x9b,0x1a,0x98,0x71,0xe7,0x41,0x74,0xe9,0x12,0xa4,0x1f,0x27,0x0a,0x63, + 0x94,0x49,0xd7,0xad,0xa5,0xc4,0x5c,0xc3,0xc9,0x70,0xb3,0x7b,0x16,0xb6,0x1d,0xd4,0x09,0xc4,0x9a,0x46, + 0x2d,0x0e,0x75,0x07,0x31,0x7b,0xed,0x45,0xcd,0x99,0x84,0x14,0xf1,0x01,0x00,0x00,0x93,0xd5,0xa3,0xe4, + 0x34,0x05,0xeb,0x98,0x3b,0x5f,0x2f,0x11,0xa4,0xa5,0xc4,0xff,0xfb,0x22,0x7c,0x54 +}; + +static void test_DSA(void) +{ + BCRYPT_ALG_HANDLE alg; + BCRYPT_KEY_HANDLE key; + BCRYPT_DSA_KEY_BLOB *dsablob; + UCHAR sig[40], schemes; + ULONG len, size; + NTSTATUS ret; + BYTE *buf, buf2[sizeof(BCRYPT_DSA_KEY_BLOB) + sizeof(dsaPublicBlob)]; + + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_DSA_ALGORITHM, NULL, 0); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptGetProperty(alg, L"PaddingSchemes", (UCHAR *)&schemes, sizeof(schemes), &size, 0); + ok(ret == STATUS_NOT_SUPPORTED, "got 0x%x\n", ret); + + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_PUBLIC_KEY_BLOB, &key, dsaPublicBlob, sizeof(dsaPublicBlob), 0); + ok(!ret, "got 0x%x\n", ret); + if(ret) + { + win_skip("BCRYPT_DSA_ALGORITHM not supported\n"); + goto dsa_end; + } + pBCryptDestroyKey(key); + + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_DSA_PUBLIC_BLOB, &key, dsaPublicBlob, sizeof(dsaPublicBlob), 0); + ok(!ret, "got 0x%x\n", ret); + + memset(buf2, 0xcc, sizeof(buf2)); + ret = pBCryptExportKey(key, NULL, BCRYPT_DSA_PUBLIC_BLOB, buf2, sizeof(buf2), &size, 0); + ok(!ret, "got 0x%x\n", ret); + dsablob = (BCRYPT_DSA_KEY_BLOB *)buf2; + ok(dsablob->dwMagic == BCRYPT_DSA_PUBLIC_MAGIC, "got 0x%x\n", dsablob->dwMagic); + ok(dsablob->cbKey == 64, "got %u\n", dsablob->cbKey); + ok(size == sizeof(*dsablob) + dsablob->cbKey * 3, "got %u\n", size); + + ret = pBCryptExportKey(key, NULL, BCRYPT_DSA_PRIVATE_BLOB, buf2, sizeof(buf2), &size, 0); + todo_wine ok(ret == STATUS_INVALID_PARAMETER, "got 0x%x\n", ret); + + ret = pBCryptVerifySignature(key, NULL, dsaHash, sizeof(dsaHash), dsaSignature, sizeof(dsaSignature), 0); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptDestroyKey(key); + ok(!ret, "got 0x%x\n", ret); + + /* sign/verify with export/import round-trip */ + ret = pBCryptGenerateKeyPair(alg, &key, 512, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + ret = pBCryptFinalizeKeyPair(key, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + + len = 0; + memset(sig, 0, sizeof(sig)); + ret = pBCryptSignHash(key, NULL, dsaHash, sizeof(dsaHash), sig, sizeof(sig), &len, 0); + ok(!ret, "got 0x%x\n", ret); + ok(len == 40, "got %u\n", len); + + size = 0; + ret = pBCryptExportKey(key, NULL, BCRYPT_DSA_PUBLIC_BLOB, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size, "size not set\n"); + + buf = calloc(1, size); + ret = pBCryptExportKey(key, NULL, BCRYPT_DSA_PUBLIC_BLOB, buf, size, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + dsablob = (BCRYPT_DSA_KEY_BLOB *)buf; + ok(dsablob->dwMagic == BCRYPT_DSA_PUBLIC_MAGIC, "got 0x%x\n", dsablob->dwMagic); + ok(dsablob->cbKey == 64, "got %u\n", dsablob->cbKey); + ok(size == sizeof(*dsablob) + dsablob->cbKey * 3, "got %u\n", size); + + ret = pBCryptDestroyKey(key); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptImportKeyPair(alg, NULL, BCRYPT_DSA_PUBLIC_BLOB, &key, buf, size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + free(buf); + + ret = pBCryptVerifySignature(key, NULL, dsaHash, sizeof(dsaHash), sig, len, 0); + ok(!ret, "got 0x%x\n", ret); + ret = pBCryptDestroyKey(key); + ok(!ret, "got 0x%x\n", ret); + + ret = pBCryptImportKeyPair(alg, NULL, LEGACY_DSA_V2_PRIVATE_BLOB, &key, dssKey, sizeof(dssKey), 0); + ok(!ret, "got 0x%x\n", ret); + + size = 0; + ret = pBCryptExportKey(key, NULL, LEGACY_DSA_V2_PRIVATE_BLOB, NULL, 0, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size, "size not set\n"); + + buf = calloc(1, size); + ret = pBCryptExportKey(key, NULL, LEGACY_DSA_V2_PRIVATE_BLOB, buf, size, &size, 0); + ok(ret == STATUS_SUCCESS, "got 0x%x\n", ret); + ok(size == sizeof(dssKey), "got %u expected %Iu\n", size, sizeof(dssKey)); + ok(!memcmp(dssKey, buf, size), "wrong data\n"); + free(buf); + + ret = pBCryptDestroyKey(key); + ok(!ret, "got 0x%x\n", ret); +dsa_end: + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(!ret, "got 0x%x\n", ret); +} + +static void test_SecretAgreement(void) +{ + static BCryptBuffer hash_param_buffers[] = + { + { + sizeof(BCRYPT_SHA256_ALGORITHM), + KDF_HASH_ALGORITHM, + (void *)BCRYPT_SHA256_ALGORITHM, + } + }; + static BCryptBufferDesc hash_params = + { + BCRYPTBUFFER_VERSION, + ARRAY_SIZE(hash_param_buffers), + hash_param_buffers, + }; + + static const ULONG dh_private_key[] = + { + 0xc4caf69c, 0x57b4db27, 0x36f7135f, 0x5ccba686, 0xc37b8819, 0x1d35c9b2, 0xbb07a1cf, 0x0c5d1c1b, + 0xc79acb10, 0x31dfdabb, 0x702e02b9, 0x1efab345, 0x262a8074, 0x5edf7698, 0x9b9dc630, 0x13c34b93, + 0xacbc928b, 0xb79eed8c, 0x7413dce9, 0xa5521280, 0x88d8e695, 0xa310269f, 0xca7c5719, 0xcd0c775b, + 0x9a6e2cf2, 0x9e235c51, 0xf49db62d, 0x28e72424, 0x4a44da5a, 0x3d98268d, 0x8e4d2be3, 0x254e44e6, + + 0x18a67e55, 0x572e13a1, 0x46f81ca8, 0xc331c9b9, 0xf8fe3dd4, 0x8a889e5a, 0x6c0505fd, 0xbd97a121, + 0xed2dbd67, 0xf39efa8e, 0x36f9c287, 0xf6bbfa6c, 0x461e42ad, 0x17dc170e, 0xc002dc2e, 0x4813d9a4, + 0x0b6fabb8, 0x6a9e1860, 0xa8a8cbd9, 0xb7ed6b5d, 0xabb34d23, 0xf2fbe1fd, 0x8670df1e, 0xba7fa4e6, + 0xf7039712, 0x94448f30, 0xe10c812e, 0x3e311976, 0xcfdd72c4, 0xbdbea98f, 0xc9a540d6, 0x89646d57, + + 0x7ab63b33, 0x03a1e9b6, 0x947f7a9b, 0x5ae59eeb, 0x1d12eb05, 0x3f425d92, 0xe028c6ba, 0xbf90ddc9, + 0xb554f55a, 0x7aeb88b6, 0x4a443a5f, 0xbab35111, 0x82c78a0c, 0x298dd482, 0x02937cb1, 0xc94cdc2e, + 0x59b010eb, 0x3bbc0a2b, 0xd845fee0, 0x04c1d0db, 0x0c8c9424, 0x1cafd4b2, 0x9aa7aed9, 0x6a478486, + 0xa8841fd7, 0xbfeff40a, 0x8fd7bcc5, 0x3bb28977, 0x2b9a7955, 0xa55cd2e4, 0x1b6ad657, 0x067cdf21, + + 0x06f36920, 0x63280e1b, 0xf17d930f, 0xa06e74a8, 0x463b3a6f, 0x2a464507, 0x93f8a982, 0x8f620a7d, + 0xeda32d11, 0x9706a6d4, 0x33dce588, 0x75a1c446, 0x048ab567, 0xd735aafa, 0x806f7c1c, 0xdcb9651a, + 0x26acf3b4, 0x45f91cc9, 0x2a0de6fc, 0xf3c03d0c, 0xf5aee0aa, 0x3eeaaf36, 0x18ccee61, 0x83faa783, + 0x4b2b5250, 0xf4ccea22, 0x5ac0714b, 0x3f0b2bc6, 0x481b13ce, 0x12040ea7, 0x66e0bbed, 0x158e1a67, + }; + static const ULONG dh_private_key2[] = + { + 0xffffffff, 0xffffffff, 0xa2da0fc9, 0x34c26821, 0x8b62c6c4, 0xd11cdc80, 0x084e0229, 0x74cc678a, + 0xa6be0b02, 0x229b133b, 0x79084a51, 0xdd04348e, 0xb31995ef, 0x1b433acd, 0x6d0a2b30, 0x37145ff2, + 0x6d35e14f, 0x45c2516d, 0x76b585e4, 0xc67e5e62, 0xe9424cf4, 0x6bed37a6, 0xb65cff0b, 0xedb706f4, + 0xfb6b38ee, 0xa59f895a, 0x11249fae, 0xe61f4b7c, 0x51662849, 0x8153e6ec, 0xffffffff, 0xffffffff, + + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000000, + + 0xa0c3c734, 0xc130c92d, 0x5265abf8, 0xff409f17, 0xbcdce187, 0xff64dae3, 0x170560aa, 0xb2423ed8, + 0x9ee5a8b9, 0x92548030, 0x02bba1f9, 0x823e39a4, 0x69c438f5, 0xf91016ac, 0x89bfd166, 0x7f996446, + 0x86224203, 0x15bf689c, 0x619354a4, 0x0c1d3a1f, 0x11bcf3d2, 0x58aae029, 0x41c69824, 0x3fafc179, + 0xa742747c, 0x60658c7a, 0xd3b0bde4, 0x78d3f08b, 0x6cefa061, 0x33752536, 0xe84d4901, 0x48cd73f4, + + 0x8d449700, 0x1f95120e, 0xceb31745, 0x3663177b, 0xbd9bb2d5, 0x9c23c0d9, 0x814d34f8, 0xbc54edb0, + 0xb874659a, 0x3bac8a30, 0xa1f3dd46, 0x1705c900, 0xbc46fefe, 0x7d13875b, 0x3064351a, 0x4bd89a1c, + 0x9e938761, 0x931949db, 0x34490719, 0x84fb08ca, 0xa9dd355a, 0x5b3f5061, 0x2ac96663, 0xc594429e, + 0xbe58395d, 0x2f7d872a, 0x303d37b3, 0xa3a9b606, 0x735a6732, 0xa095bd95, 0x3d55a7c3, 0x00e54635, + }; + static const ULONG dh_peer_key[] = + { + 0xffffffff, 0xffffffff, 0xa2da0fc9, 0x34c26821, 0x8b62c6c4, 0xd11cdc80, 0x084e0229, 0x74cc678a, + 0xa6be0b02, 0x229b133b, 0x79084a51, 0xdd04348e, 0xb31995ef, 0x1b433acd, 0x6d0a2b30, 0x37145ff2, + 0x6d35e14f, 0x45c2516d, 0x76b585e4, 0xc67e5e62, 0xe9424cf4, 0x6bed37a6, 0xb65cff0b, 0xedb706f4, + 0xfb6b38ee, 0xa59f895a, 0x11249fae, 0xe61f4b7c, 0x51662849, 0x8153e6ec, 0xffffffff, 0xffffffff, + + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000000, + + 0x3bf7404b, 0x6284fffe, 0x97c0d565, 0xd830c658, 0xcc21bf39, 0xcae45bb6, 0x019df7df, 0xbf4cd293, + 0x6bf1989d, 0x78a81f52, 0xa4ed861c, 0x6bacf493, 0xa3e700d1, 0xd06cc206, 0x411b9727, 0x01e9c9ab, + 0x9b7e6efa, 0xf46bb25d, 0xd1027242, 0x6130787c, 0xa7b87d8b, 0xfee41492, 0x50db6213, 0x321199b6, + 0x7dace53a, 0xe8b1ec51, 0x2181b113, 0x3b33e3c0, 0x5b3a2d67, 0xbd34f0c1, 0x7037c542, 0x4a8d5540, + }; + static const ULONG dh_shared_secret_raw[] = + { + 0x375d89b5, 0x35a9c270, 0xfbc5ba82, 0x09eb3069, 0xd50965b0, 0xace510f7, 0x981e8731, 0x80a76115, + 0xf386d348, 0xca17b8df, 0x0b0e84ec, 0xf81f756e, 0x5030fa20, 0x03113b71, 0x97b7e879, 0x899b5fae, + 0xe6913299, 0x09270076, 0x39bc813a, 0xde3ef070, 0x65ad5b3a, 0x2b7c4ba4, 0x86c98ef9, 0x3236feaf, + 0x3e0253f7, 0x0489d2dd, 0x97669a3d, 0x50242fca, 0x5d4aecb1, 0xcf2d805f, 0x2258afff, 0x750e92cd, + }; + static const ULONG dh_shared_secret_raw2[] = + { + 0x0815f37d, 0x19ee74ab, 0x9f63f123, 0xe1b3f10c, 0xbcc9be83, 0xaddf5b9d, 0x28174e72, 0xf8a33825, + 0xfc74e47d, 0x2c950888, 0xf5b776d9, 0xfc712fef, 0x5b213b32, 0x489a9829, 0xfc0a4d1d, 0x6e641d3b, + 0x3bb2ff57, 0x63500318, 0x081ee54f, 0xf33a2805, 0xb3759e98, 0xa9a64afe, 0x964b8897, 0x04691bbc, + 0x80f4aae1, 0x617405ee, 0xab71724d, 0x6c10c214, 0x6f60b96f, 0xdc777b0b, 0x22f40d4f, 0x8a1c4eb5, + }; + static const ULONG dh_shared_secret_sha1[] = + { + 0x0babba9c, 0x0bdeacbd, 0x04e36574, 0xdd504dcd, 0x0cd88db0, + }; + static const ULONG dh_shared_secret_sha256[] = + { + 0x3213db5b, 0x8cc8250b, 0xc829eaab, 0x00933709, 0x68160aa9, 0xfb9f1e20, 0xf92368e6, 0x2b8e18eb, + }; + static const ULONG length = 1024; + BCRYPT_DH_PARAMETER_HEADER *dh_header; + BCRYPT_DH_KEY_BLOB *dh_key_blob; + BCRYPT_SECRET_HANDLE secret, secret2; + BCRYPT_ALG_HANDLE alg; + BCRYPT_KEY_HANDLE key, key2; + UCHAR buffer[2048]; + NTSTATUS status; + ULONG size, i; + + status = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDH_P256_ALGORITHM, NULL, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + key = NULL; + status = pBCryptGenerateKeyPair(alg, &key, 256, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(key != NULL, "key not set\n"); + + status = pBCryptFinalizeKeyPair(key, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + status = pBCryptSecretAgreement(NULL, key, &secret, 0); + ok(status == STATUS_INVALID_HANDLE, "got 0x%x\n", status); + + status = pBCryptSecretAgreement(key, NULL, &secret, 0); + ok(status == STATUS_INVALID_HANDLE, "got 0x%x\n", status); + + status = pBCryptSecretAgreement(key, key, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "got 0x%x\n", status); + + status = pBCryptSecretAgreement(key, key, &secret, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + status = pBCryptDeriveKey(NULL, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got 0x%x\n", status); + + status = pBCryptDeriveKey(key, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got 0x%x\n", status); + + status = pBCryptDeriveKey(secret, NULL, NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_PARAMETER, "got 0x%x\n", status); + + status = pBCryptDeriveKey(secret, L"HASH", NULL, NULL, 0, &size, 0); + todo_wine + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + status = pBCryptDestroyHash(secret); + ok(status == STATUS_INVALID_PARAMETER, "got 0x%x\n", status); + + status = pBCryptDestroyKey(secret); + ok(status == STATUS_INVALID_HANDLE, "got 0x%x\n", status); + + status = pBCryptDestroySecret(NULL); + ok(status == STATUS_INVALID_HANDLE, "got 0x%x\n", status); + + status = pBCryptDestroySecret(alg); + ok(status == STATUS_INVALID_HANDLE, "got 0x%x\n", status); + + status = pBCryptDestroySecret(secret); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + status = pBCryptDestroyKey(key); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + status = pBCryptCloseAlgorithmProvider(alg, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + /* DH */ + status = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_DH_ALGORITHM, NULL, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + key = NULL; + status = pBCryptGenerateKeyPair(alg, &key, 1024, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(key != NULL, "key not set\n"); + + status = pBCryptFinalizeKeyPair(key, 0); + if (status == STATUS_INVALID_PARAMETER) + { + win_skip("broken DH detected\n"); + pBCryptCloseAlgorithmProvider(alg, 0); + return; + } + todo_wine ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + status = pBCryptSecretAgreement(key, key, &secret, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + status = pBCryptDestroyKey(key); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + status = pBCryptDeriveKey(secret, L"HASH", NULL, NULL, 0, &size, 0); + todo_wine ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + status = pBCryptDestroySecret(secret); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + key = NULL; + status = pBCryptGenerateKeyPair(alg, &key, 256, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %08x\n", status); + + status = pBCryptGenerateKeyPair(alg, &key, length, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(key != NULL, "key not set\n"); + + memset(buffer, 0xcc, sizeof(buffer)); + status = pBCryptGetProperty(key, BCRYPT_DH_PARAMETERS, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08x\n", status); + + status = pBCryptExportKey(key, NULL, BCRYPT_DH_PUBLIC_BLOB, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08x\n", status); + + status = pBCryptFinalizeKeyPair(key, 0); + if (status != STATUS_SUCCESS) + { + pBCryptDestroyKey(key); + pBCryptCloseAlgorithmProvider(alg, 0); + return; + } + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + status = pBCryptFinalizeKeyPair(key, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08x\n", status); + + size = 0xdeadbeef; + status = pBCryptGetProperty(key, BCRYPT_DH_PARAMETERS, NULL, sizeof(buffer), &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == sizeof(BCRYPT_DH_PARAMETER_HEADER) + length / 8 * 2, "Got unexpected size %u.\n", size); + + size = 0xdeadbeef; + status = pBCryptGetProperty(key, BCRYPT_DH_PARAMETERS, buffer, 28, &size, 0); + ok(status == STATUS_BUFFER_TOO_SMALL, "got %08x\n", status); + ok(size == sizeof(BCRYPT_DH_PARAMETER_HEADER) + length / 8 * 2, "Got unexpected size %u.\n", size); + + size = 0xdeadbeef; + status = pBCryptGetProperty(key, BCRYPT_DH_PARAMETERS, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == sizeof(BCRYPT_DH_PARAMETER_HEADER) + length / 8 * 2, "Got unexpected size %u.\n", size); + + dh_header = (BCRYPT_DH_PARAMETER_HEADER *)buffer; + ok(dh_header->cbLength == sizeof(*dh_header) + length / 8 * 2, "Got unexpected length %u.\n", dh_header->cbLength); + ok(dh_header->cbKeyLength == length / 8, "Got unexpected length %u.\n", dh_header->cbKeyLength); + ok(dh_header->dwMagic == BCRYPT_DH_PARAMETERS_MAGIC, "Got unexpected magic 0x%x.\n", dh_header->dwMagic); + + status = pBCryptDestroyKey(key); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)buffer; + dh_key_blob->dwMagic = BCRYPT_DH_PRIVATE_MAGIC; + dh_key_blob->cbKey = length / 8; + memcpy(dh_key_blob + 1, dh_private_key, sizeof(dh_private_key)); + size = sizeof(buffer); + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_DH_PRIVATE_BLOB, &key, buffer, size, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %08x\n", status); + size = sizeof(*dh_key_blob) + length / 8 * 4; + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_DH_PRIVATE_BLOB, &key, buffer, size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + memset(buffer, 0xcc, sizeof(buffer)); + size = 0xdeadbeef; + status = pBCryptExportKey(key, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == sizeof(BCRYPT_DH_KEY_BLOB) + length / 8 * 3, "Got unexpected size %u.\n", size); + + size = 0xdeadbeef; + status = pBCryptExportKey(key, NULL, BCRYPT_DH_PUBLIC_BLOB, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == sizeof(BCRYPT_DH_KEY_BLOB) + length / 8 * 3, "Got unexpected size %u.\n", size); + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)buffer; + ok(dh_key_blob->dwMagic == BCRYPT_DH_PUBLIC_MAGIC, "Got unexpected magic 0x%x.\n", dh_key_blob->dwMagic); + ok(dh_key_blob->cbKey == length / 8, "Got unexpected length %u.\n", dh_key_blob->cbKey); + ok(!memcmp(dh_key_blob + 1, dh_private_key, length / 8 * 3), "Key data does not match.\n"); + + status = pBCryptGenerateKeyPair(alg, &key2, length, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + dh_header = (BCRYPT_DH_PARAMETER_HEADER *)buffer; + dh_header->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC; + dh_header->cbLength = sizeof(*dh_header) + length / 8 * 2; + dh_header->cbKeyLength = length / 8; + memcpy(dh_header + 1, dh_private_key, length / 8 * 2); + status = pBCryptSetProperty(key2, BCRYPT_DH_PARAMETERS, buffer, dh_header->cbLength, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + status = pBCryptFinalizeKeyPair(key2, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + status = pBCryptExportKey(key2, NULL, BCRYPT_DH_PUBLIC_BLOB, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == sizeof(BCRYPT_DH_KEY_BLOB) + length / 8 * 3, "Got unexpected size %u.\n", size); + ok(dh_key_blob->dwMagic == BCRYPT_DH_PUBLIC_MAGIC, "Got unexpected dwMagic 0x%x.\n", dh_key_blob->dwMagic); + ok(dh_key_blob->cbKey == length / 8, "Got unexpected length %u.\n", dh_key_blob->cbKey); + todo_wine ok(!memcmp(dh_key_blob + 1, dh_private_key, length / 8 * 2), "DH parameters do not match.\n"); + ok(memcmp((BYTE *)(dh_key_blob + 1) + length / 8 * 2, (BYTE *)dh_private_key + length / 8 * 2, length / 8), + "Random public key data matches.\n"); + + memset(buffer, 0xcc, sizeof(buffer)); + status = pBCryptExportKey(key, NULL, BCRYPT_DH_PRIVATE_BLOB, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)buffer; + ok(size == sizeof(BCRYPT_DH_KEY_BLOB) + length / 8 * 4, "Got unexpected size %u.\n", size); + ok(dh_key_blob->dwMagic == BCRYPT_DH_PRIVATE_MAGIC, "Got unexpected dwMagic 0x%x.\n", dh_key_blob->dwMagic); + ok(dh_key_blob->cbKey == length / 8, "Got unexpected length %u.\n", dh_key_blob->cbKey); + ok(!memcmp(dh_key_blob + 1, dh_private_key, length / 8 * 4), "Private key data does not match.\n"); + + status = pBCryptSecretAgreement(NULL, key, &secret, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08x\n", status); + + status = pBCryptSecretAgreement(key, NULL, &secret, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08x\n", status); + + status = pBCryptSecretAgreement(key, key, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %08x\n", status); + + status = pBCryptSecretAgreement(key, key, &secret, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + status = pBCryptDeriveKey(NULL, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08x\n", status); + + status = pBCryptDeriveKey(key, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08x\n", status); + + status = pBCryptDeriveKey(secret, NULL, NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %08x\n", status); + + size = 0xdeadbeef; + status = pBCryptDeriveKey(secret, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == 20, "Got unexpected size %u.\n", size); + + size = 0xdeadbeef; + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == length / 8, "Got unexpected size %u.\n", size); + + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buffer, 128, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == length / 8, "Got unexpected size %u.\n", size); + ok(!memcmp(buffer, dh_shared_secret_raw, size), "Raw shared secret data does not match.\n"); + + size = sizeof(buffer); + memset(buffer, 0xcc, sizeof(buffer)); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, NULL, buffer, 128, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == 20, "Got unexpected size %u.\n", size); + ok(!memcmp(buffer, dh_shared_secret_sha1, sizeof(dh_shared_secret_sha1)), + "sha1 shared secret data does not match.\n"); + + size = sizeof(buffer); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, buffer, size, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == 32, "Got unexpected size %u.\n", size); + ok(!memcmp(buffer, dh_shared_secret_sha256, sizeof(dh_shared_secret_sha256)), + "sha256 shared secret data does not match.\n"); + + for (i = size; i < sizeof(buffer); ++i) + if (buffer[i] != 0xcc) break; + ok(i == sizeof(buffer), "Buffer modified at %u, value %#x.\n", i, buffer[i]); + + status = pBCryptDestroySecret(secret); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + status = pBCryptSecretAgreement(key, key2, &secret, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + status = pBCryptSecretAgreement(key2, key, &secret2, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buffer, 128, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buffer + size, 128, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(!memcmp(buffer, buffer + size, size), "Shared secrets do not match.\n"); + + status = pBCryptDestroySecret(secret); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + status = pBCryptDestroySecret(secret2); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + status = pBCryptDestroyKey(key); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + status = pBCryptDestroyKey(key2); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)buffer; + dh_key_blob->dwMagic = BCRYPT_DH_PRIVATE_MAGIC; + dh_key_blob->cbKey = length / 8; + memcpy(dh_key_blob + 1, dh_private_key2, sizeof(dh_private_key2)); + + size = sizeof(*dh_key_blob) + length / 8 * 4; + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_DH_PRIVATE_BLOB, &key, buffer, size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)buffer; + dh_key_blob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC; + dh_key_blob->cbKey = length / 8; + memcpy(dh_key_blob + 1, dh_peer_key, sizeof(dh_peer_key)); + + size = sizeof(*dh_key_blob) + length / 8 * 3; + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_DH_PUBLIC_BLOB, &key2, buffer, size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + status = pBCryptSecretAgreement(key, key2, &secret, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buffer, 128, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == length / 8, "Got unexpected size %u.\n", size); + ok(!memcmp(buffer, dh_shared_secret_raw2, size), "Raw shared secret data does not match.\n"); + + status = pBCryptDestroySecret(secret); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + status = pBCryptDestroyKey(key); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + status = pBCryptDestroyKey(key2); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + status = pBCryptCloseAlgorithmProvider(alg, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); +} + +static void test_RC4(void) +{ + BCRYPT_ALG_HANDLE alg; + NTSTATUS status; + ULONG len, size; + + status = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_RC4_ALGORITHM, NULL, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + + len = size = 0; + status = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(len, "expected non-zero len\n"); + ok(size == sizeof(len), "got %u\n", size); + + len = size = 0; + status = pBCryptGetProperty(alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); + ok(len == 1, "got %u\n", len); + ok(size == sizeof(len), "got %u\n", size); + + size = sizeof(BCRYPT_CHAIN_MODE_NA); + status = pBCryptSetProperty(alg, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_NA, size, 0); + ok(!status, "got 0x%x\n", status); + + status = pBCryptCloseAlgorithmProvider(alg, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); +} + +static void test_PBKDF2(void) +{ + static char salt[] = "cCxuHMEHLibcglJOG88dIw=="; + static ULONGLONG iter_count = 25; + static BCryptBuffer pbkdf2_param_buffers[] = + { + { + sizeof(BCRYPT_SHA1_ALGORITHM), + KDF_HASH_ALGORITHM, + (void *)BCRYPT_SHA1_ALGORITHM, + }, + { + sizeof(salt) - 1, + KDF_SALT, + salt, + }, + { + sizeof(iter_count), + KDF_ITERATION_COUNT, + (void *)&iter_count, + } + }; + static BCryptBufferDesc pbkdf2_params = + { + BCRYPTBUFFER_VERSION, + ARRAY_SIZE(pbkdf2_param_buffers), + pbkdf2_param_buffers, + }; + static UCHAR pbkdf2_hash[] = + { + 0x18, 0xf2, 0x58, 0x0b, 0x92, 0x6e, 0x6d, 0xfe, + 0x55, 0xbb, 0x62, 0x87, 0x5b, 0x1f, 0x61, 0x83, + 0x4d, 0x16, 0xe4, 0x04, 0xcd, 0xab, 0x43, 0x77, + 0x25, 0x3e, 0x1d, 0xb4, 0x95, 0x5f, 0xf7, 0x01, + }; + + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; + BCRYPT_ALG_HANDLE alg; + BCRYPT_KEY_HANDLE key; + NTSTATUS status; + ULONG val, size; + static BYTE buf[32]; + + status = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_PBKDF2_ALGORITHM, NULL, 0); + if (status == STATUS_NOT_FOUND) + { + win_skip("PBKDF2 not available\n"); + return; + } + ok(!status, "got 0x%x\n", status); + ok(pBCryptKeyDerivation != NULL, "BCryptKeyDerivation not available\n"); + + val = size = 0; + status = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&val, sizeof(val), &size, 0); + ok(!status, "got 0x%x\n", status); + ok(val, "got %u\n", val); + ok(size == sizeof(val), "got %u\n", size); + + val = size = 0; + status = pBCryptGetProperty(alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&val, sizeof(val), &size, 0); + ok(status == STATUS_NOT_SUPPORTED, "got 0x%x\n", status); + + memset(&key_lengths, 0xfe, sizeof(key_lengths)); + size = 0; + status = pBCryptGetProperty(alg, BCRYPT_KEY_LENGTHS, (UCHAR *)&key_lengths, sizeof(key_lengths), &size, 0); + ok(!status, "got 0x%x\n", status); + ok(size == sizeof(key_lengths), "got %u\n", size); + ok(key_lengths.dwMinLength == 0, "got %u\n", key_lengths.dwMinLength); + ok(key_lengths.dwMaxLength == 16384, "got %u\n", key_lengths.dwMaxLength); + ok(key_lengths.dwIncrement == 8, "got %u\n", key_lengths.dwIncrement); + + key = 0; + status = pBCryptGenerateSymmetricKey(alg, &key, NULL, 0, (UCHAR *)"test", 4, 0); + ok(!status, "got 0x%x\n", status); + val = size = 0; + status = pBCryptGetProperty(key, BCRYPT_KEY_STRENGTH, (UCHAR *)&val, sizeof(val), &size, 0); + ok(!status, "got 0x%x\n", status); + ok(val == strlen("test") * 8, "got %u\n", val); + + status = pBCryptKeyDerivation(key, &pbkdf2_params, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_PARAMETER, "got 0x%x\n", status); + + buf[0] = buf[1] = 'x'; + status = pBCryptKeyDerivation(key, &pbkdf2_params, buf, 1, &size, 0); + ok(!status, "got 0x%x\n", status); + ok(size == 1, "size = %u\n", size); + ok(buf[0] == pbkdf2_hash[0], "buf[0] = %x\n", buf[0]); + ok(buf[1] == 'x', "buf[1] = %x\n", buf[1]); + + memset(buf, 'x', sizeof(buf)); + status = pBCryptKeyDerivation(key, &pbkdf2_params, buf, sizeof(buf), &size, 0); + ok(!status, "got 0x%x\n", status); + ok(size == sizeof(buf), "size = %u\n", size); + ok(!memcmp(buf, pbkdf2_hash, sizeof(pbkdf2_hash)), + "wrong data (%s)\n", wine_dbgstr_an((char *)buf, size)); + + status = pBCryptDestroyKey(key); + ok(!status, "got 0x%x\n", status); + status = pBCryptCloseAlgorithmProvider(alg, 0); + ok(status == STATUS_SUCCESS, "got 0x%x\n", status); } START_TEST(bcrypt) @@ -786,20 +4544,56 @@ START_TEST(bcrypt) pBCryptDestroyHash = (void *)GetProcAddress(module, "BCryptDestroyHash"); pBCryptGenRandom = (void *)GetProcAddress(module, "BCryptGenRandom"); pBCryptGetProperty = (void *)GetProcAddress(module, "BCryptGetProperty"); + pBCryptKeyDerivation = (void *)GetProcAddress(module, "BCryptKeyDerivation"); + pBCryptDeriveKeyPBKDF2 = (void *)GetProcAddress(module, "BCryptDeriveKeyPBKDF2"); + pBCryptSetProperty = (void *)GetProcAddress(module, "BCryptSetProperty"); + pBCryptGenerateSymmetricKey = (void *)GetProcAddress(module, "BCryptGenerateSymmetricKey"); + pBCryptEncrypt = (void *)GetProcAddress(module, "BCryptEncrypt"); + pBCryptDuplicateKey = (void *)GetProcAddress(module, "BCryptDuplicateKey"); + pBCryptDestroyKey = (void *)GetProcAddress(module, "BCryptDestroyKey"); + pBCryptDecrypt = (void *)GetProcAddress(module, "BCryptDecrypt"); + pBCryptImportKey = (void *)GetProcAddress(module, "BCryptImportKey"); + pBCryptExportKey = (void *)GetProcAddress(module, "BCryptExportKey"); + pBCryptImportKeyPair = (void *)GetProcAddress(module, "BCryptImportKeyPair"); + pBCryptVerifySignature = (void *)GetProcAddress(module, "BCryptVerifySignature"); + pBCryptGenerateKeyPair = (void *)GetProcAddress(module, "BCryptGenerateKeyPair"); + pBCryptFinalizeKeyPair = (void *)GetProcAddress(module, "BCryptFinalizeKeyPair"); + pBCryptSignHash = (void *)GetProcAddress(module, "BCryptSignHash"); + pBCryptSecretAgreement = (void *)GetProcAddress(module, "BCryptSecretAgreement"); + pBCryptDeriveKey = (void *)GetProcAddress(module, "BCryptDeriveKey"); + pBCryptDestroySecret = (void *)GetProcAddress(module, "BCryptDestroySecret"); + pBCryptEnumAlgorithms = (void *)GetProcAddress(module, "BCryptEnumAlgorithms"); + pBCryptFreeBuffer = (void *)GetProcAddress(module, "BCryptFreeBuffer"); + pBCryptDeriveKeyCapi = (void *)GetProcAddress(module, "BCryptDeriveKeyCapi"); + pBCryptEnumContextFunctions = (void *)GetProcAddress(module, "BCryptEnumContextFunctions"); test_BCryptGenRandom(); test_BCryptGetFipsAlgorithmMode(); - test_sha1(); - test_sha256(); - test_sha384(); - test_sha512(); - test_md5(); + test_hashes(); + test_BcryptHash(); + test_BcryptDeriveKeyPBKDF2(); test_rng(); - - if (pBCryptHash) /* >= Win 10 */ - test_BcryptHash(); - else - win_skip("BCryptHash is not available\n"); + test_3des(); + test_aes(); + test_BCryptGenerateSymmetricKey(); + test_BCryptEncrypt(); + test_BCryptDecrypt(); + test_key_import_export(); + test_ECDSA(); + test_RSA(); + test_RSA_SIGN(); + test_ECDH(); + test_DH(); + test_BCryptEnumContextFunctions(); + test_BCryptSignHash(); + test_BCryptEnumAlgorithms(); + test_aes_vector(); + test_BcryptDeriveKeyCapi(); + test_DSA(); + test_SecretAgreement(); + test_rsa_encrypt(); + test_RC4(); + test_PBKDF2(); FreeLibrary(module); } diff --git a/sdk/include/psdk/bcrypt.h b/sdk/include/psdk/bcrypt.h index 55c13bcf7e3..df1b738b07c 100644 --- a/sdk/include/psdk/bcrypt.h +++ b/sdk/include/psdk/bcrypt.h @@ -40,7 +40,6 @@ typedef _Return_type_success_(return >= 0) LONG NTSTATUS; typedef NTSTATUS *PNTSTATUS; #endif -#if defined(_MSC_VER) || defined(__MINGW32__) #define BCRYPT_ALGORITHM_NAME L"AlgorithmName" #define BCRYPT_AUTH_TAG_LENGTH L"AuthTagLength" #define BCRYPT_BLOCK_LENGTH L"BlockLength" @@ -106,6 +105,7 @@ typedef NTSTATUS *PNTSTATUS; #define BCRYPT_SHA256_ALGORITHM L"SHA256" #define BCRYPT_SHA384_ALGORITHM L"SHA384" #define BCRYPT_SHA512_ALGORITHM L"SHA512" +#define BCRYPT_PBKDF2_ALGORITHM L"PBKDF2" #define BCRYPT_CHAIN_MODE_NA L"ChainingModeN/A" #define BCRYPT_CHAIN_MODE_CBC L"ChainingModeCBC" @@ -121,90 +121,6 @@ typedef NTSTATUS *PNTSTATUS; #define BCRYPT_KDF_RAW_SECRET L"TRUNCATE" #define BCRYPT_DH_PARAMETERS L"DHParameters" -#else -static const WCHAR BCRYPT_ALGORITHM_NAME[] = {'A','l','g','o','r','i','t','h','m','N','a','m','e',0}; -static const WCHAR BCRYPT_AUTH_TAG_LENGTH[] = {'A','u','t','h','T','a','g','L','e','n','g','t','h',0}; -static const WCHAR BCRYPT_BLOCK_LENGTH[] = {'B','l','o','c','k','L','e','n','g','t','h',0}; -static const WCHAR BCRYPT_BLOCK_SIZE_LIST[] = {'B','l','o','c','k','S','i','z','e','L','i','s','t',0}; -static const WCHAR BCRYPT_CHAINING_MODE[] = {'C','h','a','i','n','i','n','g','M','o','d','e',0}; -static const WCHAR BCRYPT_EFFECTIVE_KEY_LENGTH[] = {'E','f','f','e','c','t','i','v','e','K','e','y','L','e','n','g','t','h',0}; -static const WCHAR BCRYPT_HASH_BLOCK_LENGTH[] = {'H','a','s','h','B','l','o','c','k','L','e','n','g','t','h',0}; -static const WCHAR BCRYPT_HASH_LENGTH[] = {'H','a','s','h','D','i','g','e','s','t','L','e','n','g','t','h',0}; -static const WCHAR BCRYPT_HASH_OID_LIST[] = {'H','a','s','h','O','I','D','L','i','s','t',0}; -static const WCHAR BCRYPT_KEY_LENGTH[] = {'K','e','y','L','e','n','g','t','h',0}; -static const WCHAR BCRYPT_KEY_LENGTHS[] = {'K','e','y','L','e','n','g','t','h','s',0}; -static const WCHAR BCRYPT_KEY_OBJECT_LENGTH[] = {'K','e','y','O','b','j','e','c','t','L','e','n','g','t','h',0}; -static const WCHAR BCRYPT_KEY_STRENGTH[] = {'K','e','y','S','t','r','e','n','g','t','h',0}; -static const WCHAR BCRYPT_OBJECT_LENGTH[] = {'O','b','j','e','c','t','L','e','n','g','t','h',0}; -static const WCHAR BCRYPT_PADDING_SCHEMES[] = {'P','a','d','d','i','n','g','S','c','h','e','m','e','s',0}; -static const WCHAR BCRYPT_PROVIDER_HANDLE[] = {'P','r','o','v','i','d','e','r','H','a','n','d','l','e',0}; -static const WCHAR BCRYPT_SIGNATURE_LENGTH[] = {'S','i','g','n','a','t','u','r','e','L','e','n','g','t','h',0}; -static const WCHAR BCRYPT_PUBLIC_KEY_LENGTH[] = {'P','u','b','l','i','c','K','e','y','L','e','n','g','t','h',0}; - -static const WCHAR BCRYPT_OPAQUE_KEY_BLOB[] = {'O','p','a','q','u','e','K','e','y','B','l','o','b',0}; -static const WCHAR BCRYPT_KEY_DATA_BLOB[] = {'K','e','y','D','a','t','a','B','l','o','b',0}; -static const WCHAR BCRYPT_AES_WRAP_KEY_BLOB[] = {'R','f','c','3','5','6','5','K','e','y','W','r','a','p','B','l','o','b',0}; -static const WCHAR BCRYPT_ECCPUBLIC_BLOB[] = {'E','C','C','P','U','B','L','I','C','B','L','O','B',0}; -static const WCHAR BCRYPT_ECCPRIVATE_BLOB[] = {'E','C','C','P','R','I','V','A','T','E','B','L','O','B',0}; -static const WCHAR BCRYPT_RSAPUBLIC_BLOB[] = {'R','S','A','P','U','B','L','I','C','B','L','O','B',0}; -static const WCHAR BCRYPT_RSAPRIVATE_BLOB[] = {'R','S','A','P','R','I','V','A','T','E','B','L','O','B',0}; -static const WCHAR BCRYPT_RSAFULLPRIVATE_BLOB[] = {'R','S','A','F','U','L','L','P','R','I','V','A','T','E','B','L','O','B',0}; -static const WCHAR BCRYPT_DSA_PUBLIC_BLOB[] = {'D','S','A','P','U','B','L','I','C','B','L','O','B',0}; -static const WCHAR BCRYPT_DSA_PRIVATE_BLOB[] = {'D','S','A','P','R','I','V','A','T','E','B','L','O','B',0}; -static const WCHAR BCRYPT_PUBLIC_KEY_BLOB[] = {'P','U','B','L','I','C','B','L','O','B',0}; -static const WCHAR BCRYPT_PRIVATE_KEY_BLOB[] = {'P','R','I','V','A','T','E','B','L','O','B',0}; -static const WCHAR BCRYPT_DH_PUBLIC_BLOB[] = {'D','H','P','U','B','L','I','C','B','L','O','B',0}; -static const WCHAR BCRYPT_DH_PRIVATE_BLOB[] = {'D','H','P','R','I','V','A','T','E','B','L','O','B',0}; -static const WCHAR LEGACY_DSA_PUBLIC_BLOB[] = {'C','A','P','I','D','S','A','P','U','B','L','I','C','B','L','O','B',0}; -static const WCHAR LEGACY_DSA_PRIVATE_BLOB[] = {'C','A','P','I','D','S','A','P','R','I','V','A','T','E','B','L','O','B',0}; -static const WCHAR LEGACY_DSA_V2_PUBLIC_BLOB[] = {'V','2','C','A','P','I','D','S','A','P','U','B','L','I','C','B','L','O','B',0}; -static const WCHAR LEGACY_DSA_V2_PRIVATE_BLOB[] = {'V','2','C','A','P','I','D','S','A','P','R','I','V','A','T','E','B','L','O','B',0}; -static const WCHAR LEGACY_RSAPUBLIC_BLOB[] = {'C','A','P','I','P','U','B','L','I','C','B','L','O','B',0}; -static const WCHAR LEGACY_RSAPRIVATE_BLOB[] = {'C','A','P','I','P','R','I','V','A','T','E','B','L','O','B',0}; - -static const WCHAR MS_PRIMITIVE_PROVIDER[] = \ -{'M','i','c','r','o','s','o','f','t',' ','P','r','i','m','i','t','i','v','e',' ','P','r','o','v','i','d','e','r',0}; -static const WCHAR MS_PLATFORM_CRYPTO_PROVIDER[] = \ -{'M','i','c','r','o','s','o','f','t',' ','P','l','a','t','f','o','r','m',' ','C','r','y','p','t','o',' ','P','r','o','v','i','d','e','r',0}; - -static const WCHAR BCRYPT_3DES_ALGORITHM[] = {'3','D','E','S',0}; -static const WCHAR BCRYPT_AES_ALGORITHM[] = {'A','E','S',0}; -static const WCHAR BCRYPT_DES_ALGORITHM[] = {'D','E','S',0}; -static const WCHAR BCRYPT_DH_ALGORITHM[] = {'D','H',0}; -static const WCHAR BCRYPT_DSA_ALGORITHM[] = {'D','S','A',0}; -static const WCHAR BCRYPT_ECDH_P256_ALGORITHM[] = {'E','C','D','H','_','P','2','5','6',0}; -static const WCHAR BCRYPT_ECDH_P384_ALGORITHM[] = {'E','C','D','H','_','P','3','8','4',0}; -static const WCHAR BCRYPT_ECDSA_P256_ALGORITHM[] = {'E','C','D','S','A','_','P','2','5','6',0}; -static const WCHAR BCRYPT_ECDSA_P384_ALGORITHM[] = {'E','C','D','S','A','_','P','3','8','4',0}; -static const WCHAR BCRYPT_ECDSA_P521_ALGORITHM[] = {'E','C','D','S','A','_','P','5','2','1',0}; -static const WCHAR BCRYPT_MD2_ALGORITHM[] = {'M','D','2',0}; -static const WCHAR BCRYPT_MD4_ALGORITHM[] = {'M','D','4',0}; -static const WCHAR BCRYPT_MD5_ALGORITHM[] = {'M','D','5',0}; -static const WCHAR BCRYPT_RC2_ALGORITHM[] = {'R','C','2',0}; -static const WCHAR BCRYPT_RC4_ALGORITHM[] = {'R','C','4',0}; -static const WCHAR BCRYPT_RNG_ALGORITHM[] = {'R','N','G',0}; -static const WCHAR BCRYPT_RSA_ALGORITHM[] = {'R','S','A',0}; -static const WCHAR BCRYPT_RSA_SIGN_ALGORITHM[] = {'R','S','A','_','S','I','G','N',0}; -static const WCHAR BCRYPT_SHA1_ALGORITHM[] = {'S','H','A','1',0}; -static const WCHAR BCRYPT_SHA256_ALGORITHM[] = {'S','H','A','2','5','6',0}; -static const WCHAR BCRYPT_SHA384_ALGORITHM[] = {'S','H','A','3','8','4',0}; -static const WCHAR BCRYPT_SHA512_ALGORITHM[] = {'S','H','A','5','1','2',0}; - -static const WCHAR BCRYPT_CHAIN_MODE_NA[] = {'C','h','a','i','n','i','n','g','M','o','d','e','N','/','A',0}; -static const WCHAR BCRYPT_CHAIN_MODE_CBC[] = {'C','h','a','i','n','i','n','g','M','o','d','e','C','B','C',0}; -static const WCHAR BCRYPT_CHAIN_MODE_ECB[] = {'C','h','a','i','n','i','n','g','M','o','d','e','E','C','B',0}; -static const WCHAR BCRYPT_CHAIN_MODE_CFB[] = {'C','h','a','i','n','i','n','g','M','o','d','e','C','F','B',0}; -static const WCHAR BCRYPT_CHAIN_MODE_CCM[] = {'C','h','a','i','n','i','n','g','M','o','d','e','C','C','M',0}; -static const WCHAR BCRYPT_CHAIN_MODE_GCM[] = {'C','h','a','i','n','i','n','g','M','o','d','e','G','C','M',0}; - -static const WCHAR BCRYPT_KDF_HASH[] = {'H','A','S','H',0}; -static const WCHAR BCRYPT_KDF_HMAC[] = {'H','M','A','C',0}; -static const WCHAR BCRYPT_KDF_TLS_PRF[] = {'T','L','S','_','P','R','F',0}; -static const WCHAR BCRYPT_KDF_SP80056A_CONCAT[] = {'S','P','8','0','0','_','5','6','A','_','C','O','N','C','A','T',0}; -static const WCHAR BCRYPT_KDF_RAW_SECRET[] = {'T','R','U','N','C','A','T','E',0}; - -static const WCHAR BCRYPT_DH_PARAMETERS[] = {'D','H','P','a','r','a','m','e','t','e','r','s',0}; -#endif #define BCRYPT_ECDSA_PUBLIC_P256_MAGIC 0x31534345 #define BCRYPT_ECDSA_PRIVATE_P256_MAGIC 0x32534345 @@ -226,6 +142,7 @@ static const WCHAR BCRYPT_DH_PARAMETERS[] = {'D','H','P','a','r','a','m','e','t' #define BCRYPT_SECRET_AGREEMENT_OPERATION 0x00000008 #define BCRYPT_SIGNATURE_OPERATION 0x00000010 #define BCRYPT_RNG_OPERATION 0x00000020 +#define BCRYPT_KEY_DERIVATION_OPERATION 0x00000040 #define BCRYPT_CIPHER_INTERFACE 0x00000001 #define BCRYPT_HASH_INTERFACE 0x00000002 @@ -413,9 +330,23 @@ typedef struct _BCRYPT_KEY_DATA_BLOB_HEADER ULONG cbKeyData; } BCRYPT_KEY_DATA_BLOB_HEADER, *PBCRYPT_KEY_DATA_BLOB_HEADER; -#define KDF_HASH_ALGORITHM 0x00000000 -#define KDF_SECRET_PREPEND 0x00000001 -#define KDF_SECRET_APPEND 0x00000002 +#define KDF_HASH_ALGORITHM 0x00 +#define KDF_SECRET_PREPEND 0x01 +#define KDF_SECRET_APPEND 0x02 +#define KDF_HMAC_KEY 0x03 +#define KDF_TLS_PRF_LABEL 0x04 +#define KDF_TLS_PRF_SEED 0x05 +#define KDF_SECRET_HANDLE 0x06 +#define KDF_TLS_PRF_PROTOCOL 0x07 +#define KDF_ALGORITHMID 0x08 +#define KDF_PARTYUINFO 0x09 +#define KDF_PARTYVINFO 0x0a +#define KDF_SUPPPUBINFO 0x0b +#define KDF_SUPPPRIVINFO 0x0c +#define KDF_LABEL 0x0d +#define KDF_CONTEXT 0x0e +#define KDF_SALT 0x0f +#define KDF_ITERATION_COUNT 0x10 typedef struct _BCryptBuffer { @@ -552,6 +483,7 @@ NTSTATUS WINAPI BCryptHash(BCRYPT_ALG_HANDLE, PUCHAR, ULONG, PUCHAR, ULONG, PUCH NTSTATUS WINAPI BCryptHashData(BCRYPT_HASH_HANDLE, PUCHAR, ULONG, ULONG); NTSTATUS WINAPI BCryptImportKey(BCRYPT_ALG_HANDLE, BCRYPT_KEY_HANDLE, LPCWSTR, BCRYPT_KEY_HANDLE *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG); NTSTATUS WINAPI BCryptImportKeyPair(BCRYPT_ALG_HANDLE, BCRYPT_KEY_HANDLE, LPCWSTR, BCRYPT_KEY_HANDLE *, UCHAR *, ULONG, ULONG); +NTSTATUS WINAPI BCryptKeyDerivation(BCRYPT_KEY_HANDLE, BCryptBufferDesc *, UCHAR *, ULONG, ULONG *, ULONG); NTSTATUS WINAPI BCryptOpenAlgorithmProvider(BCRYPT_ALG_HANDLE *, LPCWSTR, LPCWSTR, ULONG); NTSTATUS WINAPI BCryptRemoveContextFunction(ULONG, LPCWSTR, ULONG, LPCWSTR); NTSTATUS WINAPI BCryptSecretAgreement(BCRYPT_KEY_HANDLE, BCRYPT_KEY_HANDLE, BCRYPT_SECRET_HANDLE *, ULONG); diff --git a/sdk/include/reactos/libs/mbedtls/cipher.h b/sdk/include/reactos/libs/mbedtls/cipher.h index 1f41b528c48..cb7190ff61f 100644 --- a/sdk/include/reactos/libs/mbedtls/cipher.h +++ b/sdk/include/reactos/libs/mbedtls/cipher.h @@ -135,6 +135,11 @@ typedef enum { MBEDTLS_CIPHER_AES_128_CBC, /**< AES cipher with 128-bit CBC mode. */ MBEDTLS_CIPHER_AES_192_CBC, /**< AES cipher with 192-bit CBC mode. */ MBEDTLS_CIPHER_AES_256_CBC, /**< AES cipher with 256-bit CBC mode. */ +#ifdef __REACTOS__ + MBEDTLS_CIPHER_AES_128_CFB8, /**< AES cipher with 128-bit CFB8 mode. */ + MBEDTLS_CIPHER_AES_192_CFB8, /**< AES cipher with 192-bit CFB8 mode. */ + MBEDTLS_CIPHER_AES_256_CFB8, /**< AES cipher with 256-bit CFB8 mode. */ +#endif MBEDTLS_CIPHER_AES_128_CFB128, /**< AES cipher with 128-bit CFB128 mode. */ MBEDTLS_CIPHER_AES_192_CFB128, /**< AES cipher with 192-bit CFB128 mode. */ MBEDTLS_CIPHER_AES_256_CFB128, /**< AES cipher with 256-bit CFB128 mode. */ diff --git a/sdk/include/reactos/libs/mbedtls/config.h b/sdk/include/reactos/libs/mbedtls/config.h index e006cec873a..55160ccba3d 100644 --- a/sdk/include/reactos/libs/mbedtls/config.h +++ b/sdk/include/reactos/libs/mbedtls/config.h @@ -2366,6 +2366,21 @@ */ #define MBEDTLS_ECDSA_C +/** + * \def MBEDTLS_DSA_C + * + * Enable the DSA library. + * + * Module: library/dsa.c + * Caller: + * + * This module is used by the following key exchanges: + * DSA + * + * Requires: MBEDTLS_ASN1_WRITE_C, MBEDTLS_ASN1_PARSE_C + */ +#define MBEDTLS_DSA_C + /** * \def MBEDTLS_ECJPAKE_C * @@ -2531,7 +2546,7 @@ * it, and considering stronger message digests instead. * */ -//#define MBEDTLS_MD2_C +#define MBEDTLS_MD2_C /** * \def MBEDTLS_MD4_C @@ -2548,7 +2563,7 @@ * it, and considering stronger message digests instead. * */ -//#define MBEDTLS_MD4_C +#define MBEDTLS_MD4_C /** * \def MBEDTLS_MD5_C diff --git a/sdk/include/reactos/libs/mbedtls/dsa.h b/sdk/include/reactos/libs/mbedtls/dsa.h new file mode 100644 index 00000000000..3209a257433 --- /dev/null +++ b/sdk/include/reactos/libs/mbedtls/dsa.h @@ -0,0 +1,183 @@ +/* + * Digital Signature Algorithm (DSA) + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#ifndef MBEDTLS_DSA_H +#define MBEDTLS_DSA_H + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif +#include "bignum.h" +#include "md.h" + +/* + * DSA-specific error codes + */ +#define MBEDTLS_ERR_DSA_BAD_INPUT_DATA -0x3180 /**< Bad input parameters to function. */ +#define MBEDTLS_ERR_DSA_INVALID_KEY -0x3200 /**< The private key is not valid. */ +#define MBEDTLS_ERR_DSA_VERIFY_FAILED -0x3280 /**< The signature is not valid. */ +#define MBEDTLS_ERR_DSA_ALLOC_FAILED -0x3300 /**< Memory allocation failed. */ +#define MBEDTLS_ERR_DSA_RANDOM_FAILED -0x3380 /**< Generation of random value, such as k, failed. */ +#define MBEDTLS_ERR_DSA_FILE_IO_ERROR -0x3400 /**< Read/write of file failed. */ +#define MBEDTLS_ERR_DSA_SIG_LEN_MISMATCH -0x3480 /**< The signature is valid but its length is not matches to the given length. */ + +#define MBEDTLS_DSA_ASN_SIGNATURE_MAX_LEN 64 + +/* Max DSA group size in bytes */ +#define MBEDTLS_DSA_MAX_GROUP 64 + +/* Max DSA modulus size in bytes (the actual DSA size, max 8192 bits) */ +#define MBEDTLS_DSA_MAX_MODULUS 1024 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief DSA context structure + */ +typedef struct mbedtls_dsa_context +{ + size_t len; + mbedtls_mpi P; /*!< prime modulus P */ + mbedtls_mpi Q; /*!< prime sub-modulus Q */ + mbedtls_mpi G; /*!< generator G */ + mbedtls_mpi Y; /*!< public key Y = G^X mod P */ + mbedtls_mpi X; /*!< private key X */ +} +mbedtls_dsa_context; + +/** + * \brief This function initializes a DSA context. + * + * \param ctx The DSA context to initialize. + */ +void mbedtls_dsa_init( mbedtls_dsa_context *ctx ); + +/** + * \brief This function frees a DSA context. + * + * \param ctx The DSA context to free. + */ +void mbedtls_dsa_free( mbedtls_dsa_context *ctx ); + +/** + * \brief This function sets the DSA group parameters. + * + * \param ctx The DSA context to store the parameters in. + * \param P The prime modulus P. + * \param Q The prime sub-modulus Q. + * \param G The generator G. + * + * \return 0 if successful, or a specific error code. + */ +int mbedtls_dsa_set_group( mbedtls_dsa_context *ctx, const mbedtls_mpi *P, const mbedtls_mpi *Q, const mbedtls_mpi *G ); + +/** + * \brief This function generates a DSA keypair. + * + * \param ctx The DSA context to store the keypair in. + * \param group_size The group size for the keypair. + * \param modulus_size The modulus size for the keypair. + * \param f_rng The RNG function. + * \param p_rng The RNG context. + * + * \return 0 if successful, or a specific error code. + */ +int mbedtls_dsa_genkey( mbedtls_dsa_context *ctx, size_t group_size, size_t modulus_size, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief This function checks if the DSA group parameters are valid. + * + * \param P The prime modulus P. + * \param Q The prime sub-modulus Q. + * \param G The generator G. + * + * \return 0 if successful, or a specific error code. + */ +int mbedtls_dsa_check_pqg( const mbedtls_mpi *P, const mbedtls_mpi *Q, const mbedtls_mpi *G ); + +/** + * \brief This function checks if the public key is valid. + * + * \param ctx The DSA context. + * + * \return 0 if successful, or a specific error code. + */ +int mbedtls_dsa_check_pubkey( const mbedtls_dsa_context *ctx ); + +/** + * \brief This function checks if the private key is valid. + * + * \param ctx The DSA context. + * + * \return 0 if successful, or a specific error code. + */ +int mbedtls_dsa_check_privkey( const mbedtls_dsa_context *ctx ); + +/** + * \brief This function derived public key from private key. + * + * \param ctx The DSA context. + * + * \return 0 if successful, or a specific error code. + */ +int mbedtls_dsa_pubkey_from_privkey( mbedtls_dsa_context *ctx ); + +/** + * \brief This function computes the DSA signature of a message hash. + * + * \param ctx The DSA context. + * \param hash The message hash. + * \param hlen The length of the hash. + * \param sig The buffer to write the signature to. + * \param slen The length of the signature written. + * \param f_rng The RNG function. + * \param p_rng The RNG context. + * + * \return 0 if successful, or a specific error code. + */ +int mbedtls_dsa_sign( mbedtls_dsa_context *ctx, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief This function reads and verifies a DSA signature. + * + * \param ctx The DSA context. + * \param hash The message hash. + * \param hlen The length of the hash. + * \param sig The signature to read and verify. + * \param slen The length of the signature. + * + * \return 0 if successful, or a specific error code. + */ +int mbedtls_dsa_verify( mbedtls_dsa_context *ctx, + const unsigned char *hash, size_t hlen, + const unsigned char *sig, size_t slen ); + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief The DSA checkup routine. + * + * \param verbose Verbosity level. + * + * \return 0 if successful, or 1 if the test failed. + */ +int mbedtls_dsa_self_test( int verbose ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_DSA_H */ \ No newline at end of file