From 7f8dd873c86f1cc8c3189fe8b3ccc67ca989e18e Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Wed, 16 Aug 2017 11:23:47 +0200 Subject: [PATCH] bcrypt: Implement BCryptEncrypt. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Partially based on a patch by Michael Müller. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/bcrypt/bcrypt_main.c | 133 ++++++++++++++++++++++++++++++++++++- dlls/bcrypt/tests/bcrypt.c | 48 ++++++++++--- dlls/ncrypt/ncrypt.spec | 2 +- 3 files changed, 169 insertions(+), 14 deletions(-) diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index b05130b00c5..633765bb931 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -51,6 +51,9 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag); static void *libgnutls_handle; #define MAKE_FUNCPTR(f) static typeof(f) * p##f +MAKE_FUNCPTR(gnutls_cipher_init); +MAKE_FUNCPTR(gnutls_cipher_deinit); +MAKE_FUNCPTR(gnutls_cipher_encrypt2); MAKE_FUNCPTR(gnutls_global_deinit); MAKE_FUNCPTR(gnutls_global_init); MAKE_FUNCPTR(gnutls_global_set_log_function); @@ -80,6 +83,9 @@ static BOOL gnutls_initialize(void) goto fail; \ } + LOAD_FUNCPTR(gnutls_cipher_init) + LOAD_FUNCPTR(gnutls_cipher_deinit) + LOAD_FUNCPTR(gnutls_cipher_encrypt2) LOAD_FUNCPTR(gnutls_global_deinit) LOAD_FUNCPTR(gnutls_global_init) LOAD_FUNCPTR(gnutls_global_set_log_function) @@ -723,8 +729,69 @@ static NTSTATUS key_init( struct key *key, enum alg_id id, const UCHAR *secret, return STATUS_SUCCESS; } +static gnutls_cipher_algorithm_t get_gnutls_cipher( const struct key *key ) +{ + switch (key->alg_id) + { + case ALG_ID_AES: + FIXME( "handle block size and chaining mode\n" ); + return GNUTLS_CIPHER_AES_128_CBC; + + default: + FIXME( "algorithm %u not supported\n", key->alg_id ); + return GNUTLS_CIPHER_UNKNOWN; + } +} + +static NTSTATUS key_set_params( struct key *key, UCHAR *iv, ULONG iv_len ) +{ + gnutls_cipher_algorithm_t cipher; + gnutls_datum_t secret, vector; + int ret; + + if (key->handle) + { + pgnutls_cipher_deinit( key->handle ); + key->handle = NULL; + } + + if ((cipher = get_gnutls_cipher( key )) == GNUTLS_CIPHER_UNKNOWN) + return STATUS_NOT_SUPPORTED; + + secret.data = key->secret; + secret.size = key->secret_len; + if (iv) + { + vector.data = iv; + vector.size = iv_len; + } + + if ((ret = pgnutls_cipher_init( &key->handle, cipher, &secret, iv ? &vector : NULL ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS key_encrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output, + ULONG output_len ) +{ + int ret; + + if ((ret = pgnutls_cipher_encrypt2( key->handle, input, input_len, output, output_len ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + return STATUS_SUCCESS; +} + static NTSTATUS key_destroy( struct key *key ) { + if (key->handle) pgnutls_cipher_deinit( key->handle ); HeapFree( GetProcessHeap(), 0, key->secret ); HeapFree( GetProcessHeap(), 0, key ); return STATUS_SUCCESS; @@ -742,6 +809,19 @@ static NTSTATUS key_init( struct key *key, enum alg_id id, const UCHAR *secret, return STATUS_NOT_IMPLEMENTED; } +static NTSTATUS key_set_params( struct key *key, UCHAR *iv, ULONG iv_len ) +{ + ERR( "support for keys not available at build time\n" ); + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS key_encrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output, + ULONG output_len ) +{ + ERR( "support for keys not available at build time\n" ); + return STATUS_NOT_IMPLEMENTED; +} + static NTSTATUS key_destroy( struct key *key ) { ERR( "support for keys not available at build time\n" ); @@ -789,9 +869,58 @@ NTSTATUS WINAPI BCryptEncrypt( BCRYPT_KEY_HANDLE handle, UCHAR *input, ULONG inp void *padding, UCHAR *iv, ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags ) { - FIXME( "%p, %p, %u, %p, %p, %u, %p, %u, %p, %08x\n", handle, input, input_len, + struct key *key = handle; + ULONG bytes_left = input_len; + UCHAR *buf, *src, *dst; + NTSTATUS status; + + TRACE( "%p, %p, %u, %p, %p, %u, %p, %u, %p, %08x\n", handle, input, input_len, padding, iv, iv_len, output, output_len, ret_len, flags ); - return STATUS_NOT_IMPLEMENTED; + + if (!key || key->hdr.magic != MAGIC_KEY) return STATUS_INVALID_HANDLE; + if (padding) + { + FIXME( "padding info not implemented\n" ); + return STATUS_NOT_IMPLEMENTED; + } + if (flags & ~BCRYPT_BLOCK_PADDING) + { + FIXME( "flags %08x not implemented\n", flags ); + return STATUS_NOT_IMPLEMENTED; + } + + if ((status = key_set_params( key, iv, iv_len ))) return status; + + *ret_len = input_len; + + if (flags & BCRYPT_BLOCK_PADDING) + *ret_len = (input_len + key->block_size) & ~(key->block_size - 1); + else if (input_len & (key->block_size - 1)) + return STATUS_INVALID_BUFFER_SIZE; + + if (!output) return STATUS_SUCCESS; + if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL; + + src = input; + dst = output; + while (bytes_left >= key->block_size) + { + if ((status = key_encrypt( key, src, key->block_size, dst, key->block_size ))) return status; + bytes_left -= key->block_size; + src += key->block_size; + dst += key->block_size; + } + + if (flags & BCRYPT_BLOCK_PADDING) + { + if (!(buf = HeapAlloc( GetProcessHeap(), 0, key->block_size ))) return STATUS_NO_MEMORY; + memcpy( buf, src, bytes_left ); + memset( buf + bytes_left, key->block_size - bytes_left, key->block_size - bytes_left ); + status = key_encrypt( key, buf, key->block_size, dst, key->block_size ); + HeapFree( GetProcessHeap(), 0, buf ); + } + + return status; } NTSTATUS WINAPI BCryptDecrypt( BCRYPT_KEY_HANDLE handle, UCHAR *input, ULONG input_len, diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index d472b5cfd64..3c01330b8b1 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -774,11 +774,6 @@ static void test_BCryptGenerateSymmetricKey(void) size = 0xdeadbeef; ret = pBCryptEncrypt(key, NULL, 0, NULL, NULL, 0, NULL, 0, &size, 0); - if (ret == STATUS_NOT_IMPLEMENTED) /* remove whole IF when Wine is fixed */ - { - todo_wine ok(0, "BCryptEncrypt not implemented\n"); - return; - } ok(ret == STATUS_SUCCESS, "got %08x\n", ret); ok(!size, "got %u\n", size); @@ -800,6 +795,11 @@ static void test_BCryptGenerateSymmetricKey(void) size = 0xdeadbeef; ret = pBCryptDecrypt(key, NULL, 0, NULL, NULL, 0, NULL, 0, &size, 0); + if (ret == STATUS_NOT_IMPLEMENTED) /* remove whole IF when Wine is fixed */ + { + todo_wine ok(0, "BCryptDecrypt not implemented\n"); + return; + } ok(ret == STATUS_SUCCESS, "got %08x\n", ret); ok(!size, "got %u\n", size); @@ -833,14 +833,21 @@ static void test_BCryptEncrypt(void) {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}; BCRYPT_ALG_HANDLE aes; BCRYPT_KEY_HANDLE key; - UCHAR *buf, ciphertext[32], ivbuf[16]; + UCHAR *buf, ciphertext[48], ivbuf[16]; ULONG size, len, i; NTSTATUS ret; @@ -860,11 +867,6 @@ static void test_BCryptEncrypt(void) size = 0; memcpy(ivbuf, iv, sizeof(iv)); ret = pBCryptEncrypt(key, data, 16, NULL, ivbuf, 16, NULL, 0, &size, 0); - if (ret == STATUS_NOT_IMPLEMENTED) /* remove whole IF when Wine is fixed */ - { - todo_wine ok(0, "BCryptEncrypt not implemented\n"); - return; - } ok(ret == STATUS_SUCCESS, "got %08x\n", ret); ok(size == 16, "got %u\n", size); @@ -902,6 +904,23 @@ static void test_BCryptEncrypt(void) for (i = 0; i < 32; i++) ok(ciphertext[i] == expected2[i], "%u: %02x != %02x\n", i, ciphertext[i], expected2[i]); + /* 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 %08x\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 %08x\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]); + /* output size too small */ size = 0; memcpy(ivbuf, iv, sizeof(iv)); @@ -910,6 +929,13 @@ static void test_BCryptEncrypt(void) ok(ret == STATUS_BUFFER_TOO_SMALL, "got %08x\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 %08x\n", ret); + ok(size == 48, "got %u\n", size); + ret = pBCryptDestroyKey(key); ok(ret == STATUS_SUCCESS, "got %08x\n", ret); HeapFree(GetProcessHeap(), 0, buf); diff --git a/dlls/ncrypt/ncrypt.spec b/dlls/ncrypt/ncrypt.spec index 739b1cea9a0..1a78853bf49 100644 --- a/dlls/ncrypt/ncrypt.spec +++ b/dlls/ncrypt/ncrypt.spec @@ -11,7 +11,7 @@ @ stub BCryptDeriveKeyCapi @ stub BCryptDeriveKeyPBKDF2 @ stdcall BCryptDestroyHash(ptr) bcrypt.BCryptDestroyHash -@ stub BCryptDestroyKey +@ stdcall BCryptDestroyKey(ptr) bcrypt.BCryptDestroyKey @ stub BCryptDestroySecret @ stdcall BCryptDuplicateHash(ptr ptr ptr long long) bcrypt.BCryptDuplicateHash @ stub BCryptDuplicateKey