bcrypt: Avoid recreating cipher handles when the initialization vector doesn't change.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2019-11-06 10:16:54 +01:00 committed by Alexandre Julliard
parent 5c708c857e
commit f4d6df8947
5 changed files with 190 additions and 37 deletions

View File

@ -163,6 +163,8 @@ struct key_symmetric
enum mode_id mode;
ULONG block_size;
gnutls_cipher_hd_t handle;
UCHAR *vector;
ULONG vector_len;
UCHAR *secret;
ULONG secret_len;
};
@ -192,6 +194,8 @@ struct key_symmetric
ULONG block_size;
CCCryptorRef ref_encrypt;
CCCryptorRef ref_decrypt;
UCHAR *vector;
ULONG vector_len;
UCHAR *secret;
ULONG secret_len;
};
@ -234,7 +238,7 @@ NTSTATUS get_alg_property( const struct algorithm *, const WCHAR *, UCHAR *, ULO
NTSTATUS key_set_property( struct key *, const WCHAR *, UCHAR *, ULONG, ULONG ) DECLSPEC_HIDDEN;
NTSTATUS key_symmetric_init( struct key *, struct algorithm *, const UCHAR *, ULONG ) DECLSPEC_HIDDEN;
NTSTATUS key_symmetric_set_params( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
NTSTATUS key_symmetric_set_vector( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
NTSTATUS key_symmetric_set_auth_data( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
NTSTATUS key_symmetric_encrypt( struct key *, const UCHAR *, ULONG, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
NTSTATUS key_symmetric_decrypt( struct key *, const UCHAR *, ULONG, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
@ -248,6 +252,9 @@ BOOL key_is_symmetric( struct key * ) DECLSPEC_HIDDEN;
NTSTATUS key_export_ecc( struct key *, UCHAR *, ULONG, ULONG * ) DECLSPEC_HIDDEN;
NTSTATUS key_import_ecc( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
BOOL is_zero_vector( const UCHAR *, ULONG ) DECLSPEC_HIDDEN;
BOOL is_equal_vector( const UCHAR *, ULONG, const UCHAR *, ULONG ) DECLSPEC_HIDDEN;
BOOL gnutls_initialize(void) DECLSPEC_HIDDEN;
void gnutls_uninitialize(void) DECLSPEC_HIDDEN;

View File

@ -831,6 +831,21 @@ BOOL key_is_symmetric( struct key *key )
return builtin_algorithms[key->alg_id].class == BCRYPT_CIPHER_INTERFACE;
}
BOOL is_zero_vector( const UCHAR *vector, ULONG len )
{
ULONG i;
if (!vector) return FALSE;
for (i = 0; i < len; i++) if (vector[i]) return FALSE;
return TRUE;
}
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_import( BCRYPT_ALG_HANDLE algorithm, const WCHAR *type, BCRYPT_KEY_HANDLE *key, UCHAR *object,
ULONG object_len, UCHAR *input, ULONG input_len )
{
@ -965,7 +980,7 @@ static NTSTATUS key_encrypt( struct key *key, UCHAR *input, ULONG input_len, vo
if (auth_info->dwFlags & BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG)
FIXME( "call chaining not implemented\n" );
if ((status = key_symmetric_set_params( key, auth_info->pbNonce, auth_info->cbNonce )))
if ((status = key_symmetric_set_vector( key, auth_info->pbNonce, auth_info->cbNonce )))
return status;
*ret_len = input_len;
@ -980,7 +995,6 @@ static NTSTATUS key_encrypt( struct key *key, UCHAR *input, ULONG input_len, vo
return key_symmetric_get_tag( key, auth_info->pbTag, auth_info->cbTag );
}
if ((status = key_symmetric_set_params( key, iv, iv_len ))) return status;
*ret_len = input_len;
if (flags & BCRYPT_BLOCK_PADDING)
@ -991,6 +1005,7 @@ static NTSTATUS key_encrypt( struct key *key, UCHAR *input, ULONG input_len, vo
if (!output) return STATUS_SUCCESS;
if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (key->u.s.mode == MODE_ID_ECB && iv) return STATUS_INVALID_PARAMETER;
if ((status = key_symmetric_set_vector( key, iv, iv_len ))) return status;
src = input;
dst = output;
@ -998,7 +1013,7 @@ static NTSTATUS key_encrypt( struct key *key, UCHAR *input, ULONG input_len, vo
{
if ((status = key_symmetric_encrypt( key, src, key->u.s.block_size, dst, key->u.s.block_size )))
return status;
if (key->u.s.mode == MODE_ID_ECB && (status = key_symmetric_set_params( key, NULL, 0 ))) return status;
if (key->u.s.mode == MODE_ID_ECB && (status = key_symmetric_set_vector( key, NULL, 0 ))) return status;
bytes_left -= key->u.s.block_size;
src += key->u.s.block_size;
dst += key->u.s.block_size;
@ -1033,7 +1048,7 @@ static NTSTATUS key_decrypt( struct key *key, UCHAR *input, ULONG input_len, voi
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_params( key, auth_info->pbNonce, auth_info->cbNonce )))
if ((status = key_symmetric_set_vector( key, auth_info->pbNonce, auth_info->cbNonce )))
return status;
*ret_len = input_len;
@ -1051,8 +1066,6 @@ static NTSTATUS key_decrypt( struct key *key, UCHAR *input, ULONG input_len, voi
return STATUS_SUCCESS;
}
if ((status = key_symmetric_set_params( key, iv, iv_len ))) return status;
*ret_len = input_len;
if (input_len & (key->u.s.block_size - 1)) return STATUS_INVALID_BUFFER_SIZE;
@ -1066,6 +1079,7 @@ static NTSTATUS key_decrypt( struct key *key, UCHAR *input, ULONG input_len, voi
else if (output_len < *ret_len) return STATUS_BUFFER_TOO_SMALL;
if (key->u.s.mode == MODE_ID_ECB && iv) return STATUS_INVALID_PARAMETER;
if ((status = key_symmetric_set_vector( key, iv, iv_len ))) return status;
src = input;
dst = output;
@ -1073,7 +1087,7 @@ static NTSTATUS key_decrypt( struct key *key, UCHAR *input, ULONG input_len, voi
{
if ((status = key_symmetric_decrypt( key, src, key->u.s.block_size, dst, key->u.s.block_size )))
return status;
if (key->u.s.mode == MODE_ID_ECB && (status = key_symmetric_set_params( key, NULL, 0 ))) return status;
if (key->u.s.mode == MODE_ID_ECB && (status = key_symmetric_set_vector( key, NULL, 0 ))) return status;
bytes_left -= key->u.s.block_size;
src += key->u.s.block_size;
dst += key->u.s.block_size;

View File

@ -441,6 +441,8 @@ NTSTATUS key_symmetric_init( struct key *key, struct algorithm *alg, const UCHAR
key->alg_id = alg->id;
key->u.s.mode = alg->mode;
key->u.s.handle = 0; /* initialized on first use */
key->u.s.vector = NULL;
key->u.s.vector_len = 0;
return STATUS_SUCCESS;
}
@ -475,30 +477,45 @@ static gnutls_cipher_algorithm_t get_gnutls_cipher( const struct key *key )
}
}
NTSTATUS key_symmetric_set_params( struct key *key, UCHAR *iv, ULONG iv_len )
NTSTATUS key_symmetric_set_vector( struct key *key, UCHAR *vector, ULONG vector_len )
{
if (key->u.s.handle && (!is_zero_vector( vector, vector_len ) ||
!is_equal_vector( key->u.s.vector, key->u.s.vector_len, vector, vector_len )))
{
TRACE( "invalidating cipher handle\n" );
pgnutls_cipher_deinit( key->u.s.handle );
key->u.s.handle = NULL;
}
heap_free( key->u.s.vector );
key->u.s.vector = NULL;
key->u.s.vector_len = 0;
if (vector)
{
if (!(key->u.s.vector = heap_alloc( vector_len ))) return STATUS_NO_MEMORY;
memcpy( key->u.s.vector, vector, vector_len );
key->u.s.vector_len = vector_len;
}
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->u.s.handle)
{
pgnutls_cipher_deinit( key->u.s.handle );
key->u.s.handle = NULL;
}
if ((cipher = get_gnutls_cipher( key )) == GNUTLS_CIPHER_UNKNOWN)
return STATUS_NOT_SUPPORTED;
if (key->u.s.handle) 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;
if (iv)
{
vector.data = iv;
vector.size = iv_len;
}
if ((ret = pgnutls_cipher_init( &key->u.s.handle, cipher, &secret, iv ? &vector : NULL )))
vector.data = key->u.s.vector;
vector.size = key->u.s.vector_len;
if ((ret = pgnutls_cipher_init( &key->u.s.handle, cipher, &secret, key->u.s.vector ? &vector : NULL )))
{
pgnutls_perror( ret );
return STATUS_INTERNAL_ERROR;
@ -509,8 +526,12 @@ NTSTATUS key_symmetric_set_params( struct key *key, UCHAR *iv, ULONG iv_len )
NTSTATUS key_symmetric_set_auth_data( struct key *key, UCHAR *auth_data, ULONG len )
{
NTSTATUS status;
int ret;
if (!auth_data) return STATUS_SUCCESS;
if ((status = init_cipher_handle( key ))) return status;
if ((ret = pgnutls_cipher_add_auth( key->u.s.handle, auth_data, len )))
{
pgnutls_perror( ret );
@ -521,7 +542,11 @@ NTSTATUS key_symmetric_set_auth_data( struct key *key, UCHAR *auth_data, ULONG l
NTSTATUS key_symmetric_encrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output, ULONG output_len )
{
NTSTATUS status;
int ret;
if ((status = init_cipher_handle( key ))) return status;
if ((ret = pgnutls_cipher_encrypt2( key->u.s.handle, input, input_len, output, output_len )))
{
pgnutls_perror( ret );
@ -532,7 +557,11 @@ NTSTATUS key_symmetric_encrypt( struct key *key, const UCHAR *input, ULONG input
NTSTATUS key_symmetric_decrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output, ULONG output_len )
{
NTSTATUS status;
int ret;
if ((status = init_cipher_handle( key ))) return status;
if ((ret = pgnutls_cipher_decrypt2( key->u.s.handle, input, input_len, output, output_len )))
{
pgnutls_perror( ret );
@ -543,7 +572,11 @@ NTSTATUS key_symmetric_decrypt( struct key *key, const UCHAR *input, ULONG input
NTSTATUS key_symmetric_get_tag( struct key *key, UCHAR *tag, ULONG len )
{
NTSTATUS status;
int ret;
if ((status = init_cipher_handle( key ))) return status;
if ((ret = pgnutls_cipher_tag( key->u.s.handle, tag, len )))
{
pgnutls_perror( ret );
@ -1128,6 +1161,7 @@ NTSTATUS key_destroy( struct key *key )
if (key_is_symmetric( key ))
{
if (key->u.s.handle) pgnutls_cipher_deinit( key->u.s.handle );
heap_free( key->u.s.vector );
heap_free( key->u.s.secret );
}
else

View File

@ -106,6 +106,8 @@ NTSTATUS key_symmetric_init( struct key *key, struct algorithm *alg, const UCHAR
key->u.s.mode = alg->mode;
key->u.s.ref_encrypt = NULL; /* initialized on first use */
key->u.s.ref_decrypt = NULL;
key->u.s.vector = NULL;
key->u.s.vector_len = 0;
return STATUS_SUCCESS;
}
@ -122,32 +124,50 @@ static CCMode get_cryptor_mode( struct key *key )
}
}
NTSTATUS key_symmetric_set_params( struct key *key, UCHAR *iv, ULONG iv_len )
NTSTATUS key_symmetric_set_vector( struct key *key, UCHAR *vector, ULONG vector_len )
{
CCCryptorStatus status;
CCMode mode;
if (!(mode = get_cryptor_mode( key ))) return STATUS_NOT_SUPPORTED;
if (key->u.s.ref_encrypt)
if (key->u.s.ref_encrypt && (!is_zero_vector( vector, vector_len ) ||
!is_equal_vector( key->u.s.vector, key->u.s.vector_len, vector, vector_len )))
{
TRACE( "invalidating cryptor handles\n" );
CCCryptorRelease( key->u.s.ref_encrypt );
key->u.s.ref_encrypt = NULL;
}
if (key->u.s.ref_decrypt)
{
CCCryptorRelease( key->u.s.ref_decrypt );
key->u.s.ref_decrypt = NULL;
}
if ((status = CCCryptorCreateWithMode( kCCEncrypt, mode, kCCAlgorithmAES128, ccNoPadding, iv, key->u.s.secret,
key->u.s.secret_len, NULL, 0, 0, 0, &key->u.s.ref_encrypt )) != kCCSuccess)
heap_free( key->u.s.vector );
key->u.s.vector = NULL;
key->u.s.vector_len = 0;
if (vector)
{
if (!(key->u.s.vector = heap_alloc( vector_len ))) return STATUS_NO_MEMORY;
memcpy( key->u.s.vector, vector, vector_len );
key->u.s.vector_len = vector_len;
}
return STATUS_SUCCESS;
}
static NTSTATUS init_cryptor_handles( struct key *key )
{
CCCryptorStatus status;
CCMode mode;
if (key->u.s.ref_encrypt) return STATUS_SUCCESS;
if (!(mode = get_cryptor_mode( key ))) return STATUS_NOT_SUPPORTED;
if ((status = CCCryptorCreateWithMode( kCCEncrypt, mode, kCCAlgorithmAES128, ccNoPadding, key->u.s.vector,
key->u.s.secret, key->u.s.secret_len, NULL, 0, 0, 0,
&key->u.s.ref_encrypt )) != kCCSuccess)
{
WARN( "CCCryptorCreateWithMode failed %d\n", status );
return STATUS_INTERNAL_ERROR;
}
if ((status = CCCryptorCreateWithMode( kCCDecrypt, mode, kCCAlgorithmAES128, ccNoPadding, iv, key->u.s.secret,
key->u.s.secret_len, NULL, 0, 0, 0, &key->u.s.ref_decrypt )) != kCCSuccess)
if ((status = CCCryptorCreateWithMode( kCCDecrypt, mode, kCCAlgorithmAES128, ccNoPadding, key->u.s.vector,
key->u.s.secret, key->u.s.secret_len, NULL, 0, 0, 0,
&key->u.s.ref_decrypt )) != kCCSuccess)
{
WARN( "CCCryptorCreateWithMode failed %d\n", status );
CCCryptorRelease( key->u.s.ref_encrypt );
@ -167,6 +187,10 @@ NTSTATUS key_symmetric_set_auth_data( struct key *key, UCHAR *auth_data, ULONG l
NTSTATUS key_symmetric_encrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output, ULONG output_len )
{
CCCryptorStatus status;
NTSTATUS ret;
if ((ret = init_cryptor_handles( key ))) return ret;
if ((status = CCCryptorUpdate( key->u.s.ref_encrypt, input, input_len, output, output_len, NULL )) != kCCSuccess)
{
WARN( "CCCryptorUpdate failed %d\n", status );
@ -178,6 +202,10 @@ NTSTATUS key_symmetric_encrypt( struct key *key, const UCHAR *input, ULONG input
NTSTATUS key_symmetric_decrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output, ULONG output_len )
{
CCCryptorStatus status;
NTSTATUS ret;
if ((ret = init_cryptor_handles( key ))) return ret;
if ((status = CCCryptorUpdate( key->u.s.ref_decrypt, input, input_len, output, output_len, NULL )) != kCCSuccess)
{
WARN( "CCCryptorUpdate failed %d\n", status );
@ -235,6 +263,7 @@ NTSTATUS key_destroy( struct key *key )
{
if (key->u.s.ref_encrypt) CCCryptorRelease( key->u.s.ref_encrypt );
if (key->u.s.ref_decrypt) CCCryptorRelease( key->u.s.ref_decrypt );
heap_free( key->u.s.vector );
heap_free( key->u.s.secret );
heap_free( key );
return STATUS_SUCCESS;

View File

@ -2128,6 +2128,74 @@ static void test_BCryptEnumAlgorithms(void)
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 %08x\n", ret);
size = sizeof(BCRYPT_CHAIN_MODE_CBC);
ret = pBCryptSetProperty(alg, BCRYPT_CHAINING_MODE, (UCHAR *)BCRYPT_CHAIN_MODE_CBC, size, 0);
ok(!ret, "got %08x\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 || broken(ret == STATUS_INVALID_PARAMETER) /* vista */, "got %08x\n", ret);
if (ret == STATUS_INVALID_PARAMETER)
{
win_skip("broken BCryptImportKey\n");
pBCryptCloseAlgorithmProvider(alg, 0);
return;
}
/* 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 %08x\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 %08x\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 %08x\n", ret);
ok(size == 16, "got %u\n", size);
todo_wine ok(!memcmp(output, expect3, sizeof(expect3)), "wrong cipher text\n");
ret = pBCryptDestroyKey(key);
ok(!ret, "got %08x\n", ret);
ret = pBCryptCloseAlgorithmProvider(alg, 0);
ok(!ret, "got %08x\n", ret);
}
START_TEST(bcrypt)
{
HMODULE module;
@ -2186,6 +2254,7 @@ START_TEST(bcrypt)
test_BCryptEnumContextFunctions();
test_BCryptSignHash();
test_BCryptEnumAlgorithms();
test_aes_vector();
FreeLibrary(module);
}