From: Hans Leidekker hans@codeweavers.com
And rename the functions for consistency. --- dlls/bcrypt/bcrypt_main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index ec1004760f0..e1c971c1485 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1129,7 +1129,7 @@ static NTSTATUS key_symmetric_set_vector( struct key *key, UCHAR *vector, ULONG return STATUS_SUCCESS; }
-static struct key *create_symmetric_key( enum alg_id alg, enum chain_mode mode, ULONG block_size, UCHAR *secret, +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; @@ -1160,7 +1160,7 @@ static ULONG get_block_size( struct algorithm *alg ) return ret; }
-static NTSTATUS generate_symmetric_key( struct algorithm *alg, BCRYPT_KEY_HANDLE *ret_handle, UCHAR *secret, +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; @@ -1188,7 +1188,7 @@ static NTSTATUS generate_symmetric_key( struct algorithm *alg, BCRYPT_KEY_HANDLE } }
- if (!(key = create_symmetric_key( alg->id, alg->mode, block_size, secret, secret_len ))) status = STATUS_NO_MEMORY; + if (!(key = key_symmetric_create( alg->id, alg->mode, block_size, secret, secret_len ))) status = STATUS_NO_MEMORY; else { *ret_handle = key; @@ -1217,7 +1217,7 @@ static NTSTATUS key_import( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY len = header->cbKeyData; if (len + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) > input_len) return STATUS_INVALID_PARAMETER;
- return generate_symmetric_key( alg, key, (UCHAR *)&header[1], len ); + return key_symmetric_generate( alg, key, (UCHAR *)&header[1], len ); } else if (!wcscmp( type, BCRYPT_OPAQUE_KEY_BLOB )) { @@ -1225,7 +1225,7 @@ static NTSTATUS key_import( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY len = *(ULONG *)input; if (len + sizeof(len) > input_len) return STATUS_INVALID_PARAMETER;
- return generate_symmetric_key( alg, key, input + sizeof(len), len ); + return key_symmetric_generate( alg, key, input + sizeof(len), len ); }
FIXME( "unsupported key type %s\n", debugstr_w(type) ); @@ -1879,7 +1879,7 @@ NTSTATUS WINAPI BCryptGenerateSymmetricKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY }
if (!alg) return STATUS_INVALID_HANDLE; - if ((status = generate_symmetric_key( alg, ret_handle, secret, secret_len ))) return status; + if ((status = key_symmetric_generate( alg, ret_handle, secret, secret_len ))) return status; TRACE( "returning handle %p\n", *ret_handle ); return STATUS_SUCCESS; }
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/bcrypt_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index e1c971c1485..31d2de00e7b 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1392,7 +1392,8 @@ static NTSTATUS key_symmetric_encrypt( struct key *key, UCHAR *input, ULONG inp 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 FIXME( "Unexpected vector len %lu, *ret_len %lu.\n", key->u.s.vector_len, *ret_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;
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/bcrypt_main.c | 236 +++++++++++++++++++++++-------------- dlls/bcrypt/tests/bcrypt.c | 4 - 2 files changed, 147 insertions(+), 93 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 31d2de00e7b..aeea3a1b828 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1153,6 +1153,21 @@ static struct key *key_symmetric_create( enum alg_id alg, enum chain_mode mode, return ret; }
+static void key_destroy( struct key *key ) +{ + if (is_symmetric_key( key )) + { + UNIX_CALL( key_symmetric_destroy, key ); + free( key->u.s.vector ); + free( key->u.s.secret ); + DeleteCriticalSection( &key->u.s.cs ); + } + else + UNIX_CALL( key_asymmetric_destroy, key ); + + destroy_object( &key->hdr ); +} + static ULONG get_block_size( struct algorithm *alg ) { ULONG ret = 0, size = sizeof(ret); @@ -1232,74 +1247,6 @@ static NTSTATUS key_import( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY return STATUS_NOT_IMPLEMENTED; }
-static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, ULONG output_len, ULONG *size ) -{ - struct key_asymmetric_export_params params; - - 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 UNIX_CALL( 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 UNIX_CALL( 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 UNIX_CALL( key_asymmetric_export, ¶ms ); - } - - 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 ) { @@ -1399,6 +1346,135 @@ static NTSTATUS key_symmetric_encrypt( struct key *key, UCHAR *input, ULONG inp return status; }
+/* AES Key Wrap Algorithm (RFC3394) */ +static NTSTATUS aes_wrap( const UCHAR *secret, ULONG secret_len, const UCHAR *plain, UCHAR *cipher ) +{ + UCHAR *a, *r, b[16]; + ULONG len, t, i, j, n = secret_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 UNIX_CALL( 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 UNIX_CALL( 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 UNIX_CALL( 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, output ))) + return status; + } + return STATUS_SUCCESS; + } + + FIXME( "unsupported key type %s\n", debugstr_w(type) ); + return STATUS_NOT_IMPLEMENTED; +} + 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 ) { @@ -1506,21 +1582,6 @@ static NTSTATUS key_symmetric_decrypt( struct key *key, UCHAR *input, ULONG inpu return status; }
-static void key_destroy( struct key *key ) -{ - if (is_symmetric_key( key )) - { - UNIX_CALL( key_symmetric_destroy, key ); - free( key->u.s.vector ); - free( key->u.s.secret ); - DeleteCriticalSection( &key->u.s.cs ); - } - else - UNIX_CALL( key_asymmetric_destroy, key ); - - destroy_object( &key->hdr ); -} - static NTSTATUS convert_legacy_rsaprivate_blob( struct algorithm *alg, BCRYPT_RSAKEY_BLOB **rsa_data, ULONG *rsa_len, UCHAR *input, ULONG input_len ) { @@ -1943,19 +2004,16 @@ NTSTATUS WINAPI BCryptExportKey( BCRYPT_KEY_HANDLE export_key_handle, BCRYPT_KEY 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) - { - FIXME( "encryption of key not yet supported\n" ); - return STATUS_NOT_IMPLEMENTED; - } + if (encrypt_key_handle && !(encrypt_key = get_key_object( encrypt_key_handle ))) return STATUS_INVALID_HANDLE;
- return key_export( key, type, output, output_len, size ); + return key_export( key, encrypt_key, type, output, output_len, size ); }
static NTSTATUS key_duplicate( struct key *key_orig, struct key **ret_key ) diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 7e33dcf201c..bf3221a7943 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -2111,15 +2111,11 @@ static void test_key_import_export(void)
size = 0; ret = BCryptExportKey(key, key2, BCRYPT_AES_WRAP_KEY_BLOB, NULL, 0, &size, 0); - todo_wine ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); - todo_wine ok(size == sizeof(buffer3), "got %lu\n", size);
ret = BCryptExportKey(key, key2, BCRYPT_AES_WRAP_KEY_BLOB, buffer3, size, &size, 0); - todo_wine ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); - todo_wine ok(!memcmp(buffer3, encrypted_blob, sizeof(encrypted_blob)), "blobs didn't match\n");
key3 = NULL;
From: Hans Leidekker hans@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=57492 --- dlls/bcrypt/bcrypt_main.c | 296 ++++++++++++++++++++++--------------- dlls/bcrypt/tests/bcrypt.c | 5 - 2 files changed, 178 insertions(+), 123 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index aeea3a1b828..51fb35de379 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1213,10 +1213,165 @@ static NTSTATUS key_symmetric_generate( struct algorithm *alg, BCRYPT_KEY_HANDLE return status; }
-static NTSTATUS key_import( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY_HANDLE *key, UCHAR *object, - ULONG object_len, UCHAR *input, ULONG input_len ) +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; + UCHAR tag[16]; + + 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; + if ((status = UNIX_CALL( 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 = UNIX_CALL( key_symmetric_decrypt, &decrypt_params ))) return status; + + tag_params.key = key; + tag_params.tag = tag; + tag_params.len = sizeof(tag); + if ((status = UNIX_CALL( key_symmetric_get_tag, &tag_params ))) return status; + if (memcmp( tag, auth_info->pbTag, auth_info->cbTag )) return STATUS_AUTH_TAG_MISMATCH; + + 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 = UNIX_CALL( key_symmetric_decrypt, &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 = UNIX_CALL( key_symmetric_decrypt, &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, UCHAR *plain ) +{ + UCHAR a[8], *r, b[16]; + ULONG len, t, i, n = secret_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 )) { @@ -1242,6 +1397,21 @@ static NTSTATUS key_import( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY
return key_symmetric_generate( alg, key, input + sizeof(len), len ); } + else if (!wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB )) + { + UCHAR output[BLOCK_LENGTH_AES]; + + if (!decrypt_key || input_len < 8) return STATUS_INVALID_PARAMETER; + + len = input_len - 8; + if (len < BLOCK_LENGTH_AES || len & (BLOCK_LENGTH_AES - 1) || len > sizeof(output)) + return STATUS_INVALID_PARAMETER; + + if ((status = aes_unwrap( decrypt_key->u.s.secret, decrypt_key->u.s.secret_len, input, output ))) + return status; + + return key_symmetric_generate( alg, key, output, len ); + }
FIXME( "unsupported key type %s\n", debugstr_w(type) ); return STATUS_NOT_IMPLEMENTED; @@ -1475,113 +1645,6 @@ static NTSTATUS key_export( struct key *key, struct key *encrypt_key, const WCHA return STATUS_NOT_IMPLEMENTED; }
-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; - UCHAR tag[16]; - - 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; - if ((status = UNIX_CALL( 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 = UNIX_CALL( key_symmetric_decrypt, &decrypt_params ))) return status; - - tag_params.key = key; - tag_params.tag = tag; - tag_params.len = sizeof(tag); - if ((status = UNIX_CALL( key_symmetric_get_tag, &tag_params ))) return status; - if (memcmp( tag, auth_info->pbTag, auth_info->cbTag )) return STATUS_AUTH_TAG_MISMATCH; - - 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 = UNIX_CALL( key_symmetric_decrypt, &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 = UNIX_CALL( key_symmetric_decrypt, &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 FIXME( "Unexpected vector len %lu, *ret_len %lu.\n", key->u.s.vector_len, *ret_len ); - } - - return status; -} - static NTSTATUS convert_legacy_rsaprivate_blob( struct algorithm *alg, BCRYPT_RSAKEY_BLOB **rsa_data, ULONG *rsa_len, UCHAR *input, ULONG input_len ) { @@ -1977,25 +2040,22 @@ NTSTATUS WINAPI BCryptFinalizeKeyPair( BCRYPT_KEY_HANDLE handle, ULONG flags ) return ret; }
-NTSTATUS WINAPI BCryptImportKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE decrypt_key, const WCHAR *type, +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, debugstr_w(type), ret_handle, object, - object_len, input, input_len, flags ); + 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) - { - FIXME( "decryption of key not yet supported\n" ); - return STATUS_NOT_IMPLEMENTED; - } + if (decrypt_key_handle && !(decrypt_key = get_key_object( decrypt_key_handle ))) return STATUS_INVALID_HANDLE;
- if ((status = key_import( alg, type, ret_handle, object, object_len, input, input_len ))) return status; + 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; } diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index bf3221a7943..abb19087a7d 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -2120,19 +2120,14 @@ static void test_key_import_export(void)
key3 = NULL; ret = BCryptImportKey(aes, key2, BCRYPT_AES_WRAP_KEY_BLOB, &key3, NULL, 0, buffer3, sizeof(buffer3), 0); - todo_wine ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); - todo_wine ok(key3 != NULL, "key not set\n");
size = 0; memset(buffer2, 0xff, sizeof(buffer2)); ret = BCryptExportKey(key3, NULL, BCRYPT_KEY_DATA_BLOB, buffer2, sizeof(buffer2), &size, 0); - todo_wine ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); - todo_wine ok(size == sizeof(buffer2), "Got %lu\n", size); - todo_wine ok(!memcmp(buffer1, buffer2, sizeof(buffer1)), "Expected exported key to match imported key\n");
BCryptDestroyKey(key3);
Dmitry Timoshkov (@dmitry) commented about dlls/bcrypt/bcrypt_main.c:
return key_symmetric_generate( alg, key, input + sizeof(len), len ); }
- else if (!wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB ))
- {
UCHAR output[BLOCK_LENGTH_AES];
BLOCK_LENGTH_AES is defined as 16 however maximum AES key length is 256 bit (32 bytes).
On Thu Apr 3 08:57:06 2025 +0000, Dmitry Timoshkov wrote:
BLOCK_LENGTH_AES is defined as 16 however maximum AES key length is 256 bit (32 bytes).
16 is the block size for AES reported by native.
Dmitry Timoshkov (@dmitry) commented about dlls/bcrypt/bcrypt_main.c:
return key_symmetric_generate( alg, key, input + sizeof(len), len ); }
- else if (!wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB ))
- {
UCHAR output[BLOCK_LENGTH_AES];
if (!decrypt_key || input_len < 8) return STATUS_INVALID_PARAMETER;
len = input_len - 8;
if (len < BLOCK_LENGTH_AES || len & (BLOCK_LENGTH_AES - 1) || len > sizeof(output))
return STATUS_INVALID_PARAMETER;
if ((status = aes_unwrap( decrypt_key->u.s.secret, decrypt_key->u.s.secret_len, input, output )))
aes_unwrap() should be able to decrypt all possible AES key lengths (128, 192, 256 bits), to do so it needs to know the wrapped blob length. Your implementation uses the decryption key length instead, which is incorrect.
On Thu Apr 3 09:02:18 2025 +0000, Hans Leidekker wrote:
16 is the block size for AES reported by native.
Sure, but output is supposed to hold the decrypted key, which could be 32 bytes for a 256 bit key.
On Thu Apr 3 09:06:42 2025 +0000, Dmitry Timoshkov wrote:
aes_unwrap() should be able to decrypt all possible AES key lengths (128, 192, 256 bits), to do so it needs to know the wrapped blob length. Your implementation uses the decryption key length instead, which is incorrect.
Yes I'm assuming that they have the same length, the only one currently supported. I'll add fixme for unsupported block lengths.
On Thu Apr 3 09:27:53 2025 +0000, Hans Leidekker wrote:
Yes I'm assuming that they have the same length, the only one currently supported. I'll add fixme for unsupported block lengths.
Why not support all possible lengths from the start?
On Thu Apr 3 09:36:09 2025 +0000, Dmitry Timoshkov wrote:
Why not support all possible lengths from the start?
I see that your aes_wrap() implementation has the same bug.
On Thu Apr 3 09:38:09 2025 +0000, Dmitry Timoshkov wrote:
I see that your aes_wrap() implementation has the same bug.
I think it can wait until we see larger keys in the wild.
On Thu Apr 3 09:46:27 2025 +0000, Hans Leidekker wrote:
I think it can wait until we see larger keys in the wild.
Implementations in my MR didn't have such an artificial limitation.
It's probably also worth to note the lengths of the encryption and being encrypted keys could differ.