diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index c6f5aebe1c7..d026dab7296 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -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; diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index c1a0a56e99c..2ac36d3db01 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -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; diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 41bba0b8216..a6a07fff19e 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -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 diff --git a/dlls/bcrypt/macos.c b/dlls/bcrypt/macos.c index fbd03210216..9ff9772c15a 100644 --- a/dlls/bcrypt/macos.c +++ b/dlls/bcrypt/macos.c @@ -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; diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 5ae847ca6e1..d125e0c89d8 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -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); }