From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/bcrypt/Makefile.in | 1 + dlls/bcrypt/aes-unwrap.c | 97 +++++++++++++++++++++++++++++++++++ dlls/bcrypt/bcrypt_internal.h | 1 + dlls/bcrypt/bcrypt_main.c | 48 ++++++++++++++--- dlls/bcrypt/tests/bcrypt.c | 5 -- 5 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 dlls/bcrypt/aes-unwrap.c
diff --git a/dlls/bcrypt/Makefile.in b/dlls/bcrypt/Makefile.in index 7fde522f32b..905958749b0 100644 --- a/dlls/bcrypt/Makefile.in +++ b/dlls/bcrypt/Makefile.in @@ -7,6 +7,7 @@ UNIX_CFLAGS = $(GNUTLS_CFLAGS)
SOURCES = \ aes-wrap.c \ + aes-unwrap.c \ bcrypt_main.c \ gnutls.c \ version.rc diff --git a/dlls/bcrypt/aes-unwrap.c b/dlls/bcrypt/aes-unwrap.c new file mode 100644 index 00000000000..20d1f8a5243 --- /dev/null +++ b/dlls/bcrypt/aes-unwrap.c @@ -0,0 +1,97 @@ +/* + * AES key unwrap (RFC3394) + * + * Copyright (c) 2003-2007, Jouni Malinen j@w1.fi + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include <stdarg.h> +#include <stdlib.h> + +#include "tomcrypt.h" + +static void *aes_decrypt_init(const unsigned char *key, size_t key_len) +{ + symmetric_key *skey = malloc(sizeof(*skey)); + if (skey) aes_setup(key, key_len, 0, skey); + return skey; +} + +static void aes_decrypt_deinit(void *ctx) +{ + aes_done(ctx); + free(ctx); +} + +static void aes_decrypt(void *ctx, const unsigned char *plain, unsigned char *cipher) +{ + aes_ecb_decrypt(plain, cipher, ctx); +} + +/** + * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (RFC3394) + * @kek: Key encryption key (KEK) + * @kek_len: Length of KEK in octets + * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 + * bytes + * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits + * @plain: Plaintext key, n * 64 bits + * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) + */ +int aes_unwrap(const unsigned char *kek, size_t kek_len, int n, const unsigned char *cipher, + unsigned char *plain) +{ + unsigned char a[8], *r, b[16]; + int i, j; + void *ctx; + unsigned int t; + + /* 1) Initialize variables. */ + memcpy(a, cipher, 8); + r = plain; + memcpy(r, cipher + 8, 8 * n); + + ctx = aes_decrypt_init(kek, kek_len); + if (ctx == NULL) + return -1; + + /* 2) Compute intermediate values. + * For j = 5 to 0 + * For i = n to 1 + * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + * A = MSB(64, B) + * R[i] = LSB(64, B) + */ + 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); + aes_decrypt(ctx, b, b); + memcpy(a, b, 8); + memcpy(r, b + 8, 8); + r -= 8; + } + } + aes_decrypt_deinit(ctx); + + /* 3) Output results. + * + * These are already in @plain due to the location of temporary + * variables. Just verify that the IV matches with the expected value. + */ + for (i = 0; i < 8; i++) { + if (a[i] != 0xa6) + return -1; + } + + return 0; +} diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index 62e31bcae32..2698add38cc 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -275,5 +275,6 @@ enum key_funcs };
int aes_wrap(const UCHAR *kek, size_t kek_len, int n, const UCHAR *plain, UCHAR *cipher); +int aes_unwrap(const unsigned char *kek, size_t kek_len, int n, const unsigned char *cipher, unsigned char *plain);
#endif /* __BCRYPT_INTERNAL_H */ diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index abbfbe5d6bf..08cfd714c23 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1198,8 +1198,8 @@ static NTSTATUS generate_symmetric_key( 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_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;
@@ -1207,6 +1207,12 @@ static NTSTATUS key_import( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY { BCRYPT_KEY_DATA_BLOB_HEADER *header = (BCRYPT_KEY_DATA_BLOB_HEADER *)input;
+ if (decrypt_key) + { + FIXME( "decryption of key not yet supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + 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) @@ -1221,12 +1227,39 @@ static NTSTATUS key_import( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY } else if (!wcscmp( type, BCRYPT_OPAQUE_KEY_BLOB )) { + if (decrypt_key) + { + FIXME( "decryption of key not yet supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + if (input_len < sizeof(len)) return STATUS_BUFFER_TOO_SMALL; len = *(ULONG *)input; if (len + sizeof(len) > input_len) return STATUS_INVALID_PARAMETER;
return generate_symmetric_key( alg, key, input + sizeof(len), len ); } + else if (!wcscmp( type, BCRYPT_AES_WRAP_KEY_BLOB )) + { + UCHAR output[256 / 8]; + + if (!decrypt_key) + return STATUS_INVALID_PARAMETER; + + if (input_len < 8) + return STATUS_INVALID_PARAMETER; + + len = input_len - 8; + + if (len < 128 / 8 || (len & (BLOCK_LENGTH_AES - 1)) || len > sizeof(output)) + return STATUS_INVALID_PARAMETER; + + if (aes_unwrap( decrypt_key->u.s.secret, decrypt_key->u.s.secret_len, len / 8, input, output )) + return STATUS_NO_MEMORY; + + return generate_symmetric_key( alg, key, output, len ); + } +
FIXME( "unsupported key type %s\n", debugstr_w(type) ); return STATUS_NOT_IMPLEMENTED; @@ -1962,11 +1995,12 @@ 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, @@ -1974,13 +2008,13 @@ NTSTATUS WINAPI BCryptImportKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE dec
if (!alg) return STATUS_INVALID_HANDLE; if (!ret_handle || !type || !input) return STATUS_INVALID_PARAMETER; - if (decrypt_key) + if (decrypt_key_handle) { - FIXME( "decryption of key not yet supported\n" ); - return STATUS_NOT_IMPLEMENTED; + decrypt_key = get_key_object( decrypt_key_handle ); + if (!decrypt_key) 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);