From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/bcrypt/Makefile.in | 1 + dlls/bcrypt/aes-wrap.c | 93 +++++++++++++++++++++++++++++++++++ dlls/bcrypt/bcrypt_internal.h | 2 + dlls/bcrypt/bcrypt_main.c | 56 +++++++++++++++++++-- dlls/bcrypt/tests/bcrypt.c | 4 -- 5 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 dlls/bcrypt/aes-wrap.c
diff --git a/dlls/bcrypt/Makefile.in b/dlls/bcrypt/Makefile.in index a672ac8a3c9..7fde522f32b 100644 --- a/dlls/bcrypt/Makefile.in +++ b/dlls/bcrypt/Makefile.in @@ -6,6 +6,7 @@ UNIXLIB = bcrypt.so UNIX_CFLAGS = $(GNUTLS_CFLAGS)
SOURCES = \ + aes-wrap.c \ bcrypt_main.c \ gnutls.c \ version.rc diff --git a/dlls/bcrypt/aes-wrap.c b/dlls/bcrypt/aes-wrap.c new file mode 100644 index 00000000000..4f12be7f71a --- /dev/null +++ b/dlls/bcrypt/aes-wrap.c @@ -0,0 +1,93 @@ +/* + * AES Key Wrap Algorithm (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_encrypt_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_encrypt_deinit(void *ctx) +{ + aes_done(ctx); + free(ctx); +} + +static void aes_encrypt(void *ctx, const unsigned char *plain, unsigned char *cipher) +{ + aes_ecb_encrypt(plain, cipher, ctx); +} + +/** + * aes_wrap - Wrap keys 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 + * @plain: Plaintext key to be wrapped, n * 64 bits + * @cipher: Wrapped key, (n + 1) * 64 bits + * Returns: 0 on success, -1 on failure + */ +int aes_wrap(const unsigned char *kek, size_t kek_len, int n, const unsigned char *plain, unsigned char *cipher) +{ + unsigned char *a, *r, b[16]; + int i, j; + void *ctx; + unsigned int t; + + a = cipher; + r = cipher + 8; + + /* 1) Initialize variables. */ + memset(a, 0xa6, 8); + memcpy(r, plain, 8 * n); + + ctx = aes_encrypt_init(kek, kek_len); + if (ctx == NULL) + return -1; + + /* 2) Calculate intermediate values. + * For j = 0 to 5 + * For i=1 to n + * B = AES(K, A | R[i]) + * A = MSB(64, B) ^ t where t = (n*j)+i + * R[i] = LSB(64, B) + */ + for (j = 0; j <= 5; j++) { + r = cipher + 8; + for (i = 1; i <= n; i++) { + memcpy(b, a, 8); + memcpy(b + 8, r, 8); + aes_encrypt(ctx, b, b); + 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; + } + } + aes_encrypt_deinit(ctx); + + /* 3) Output the results. + * + * These are already in @cipher due to the location of temporary + * variables. + */ + + return 0; +} diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index 20c9a2912a5..62e31bcae32 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -274,4 +274,6 @@ enum key_funcs unix_funcs_count, };
+int aes_wrap(const UCHAR *kek, size_t kek_len, int n, const UCHAR *plain, UCHAR *cipher); + #endif /* __BCRYPT_INTERNAL_H */ diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index ec1004760f0..abbfbe5d6bf 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1232,7 +1232,7 @@ 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 ) +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;
@@ -1241,6 +1241,12 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U 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;
+ if (encrypt_key) + { + FIXME( "encryption of key not yet supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + *size = req_size; if (output_len < req_size) return STATUS_BUFFER_TOO_SMALL; if (output) @@ -1256,6 +1262,12 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U { ULONG len, req_size = sizeof(len) + key->u.s.secret_len;
+ if (encrypt_key) + { + FIXME( "encryption of key not yet supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + *size = req_size; if (output_len < req_size) return STATUS_BUFFER_TOO_SMALL; if (output) @@ -1268,6 +1280,12 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U 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 )) { + if (encrypt_key) + { + FIXME( "encryption of key not yet supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + params.key = key; params.flags = 0; params.buf = output; @@ -1277,6 +1295,12 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U } else if (!wcscmp( type, BCRYPT_RSAPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_RSAFULLPRIVATE_BLOB )) { + if (encrypt_key) + { + FIXME( "encryption of key not yet supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + params.key = key; params.flags = (wcscmp( type, BCRYPT_RSAPRIVATE_BLOB )) ? KEY_EXPORT_FLAG_RSA_FULL : 0; params.buf = output; @@ -1288,6 +1312,12 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U !wcscmp( type, BCRYPT_ECCPUBLIC_BLOB ) || !wcscmp( type, BCRYPT_RSAPUBLIC_BLOB ) || !wcscmp( type, BCRYPT_DH_PUBLIC_BLOB )) { + if (encrypt_key) + { + FIXME( "encryption of key not yet supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } + params.key = key; params.flags = KEY_EXPORT_FLAG_PUBLIC; params.buf = output; @@ -1295,6 +1325,23 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U 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 (aes_wrap( encrypt_key->u.s.secret, encrypt_key->u.s.secret_len, key->u.s.secret_len / 8, key->u.s.secret, output ) != 0) + return STATUS_NO_MEMORY; + } + return STATUS_SUCCESS; + }
FIXME( "unsupported key type %s\n", debugstr_w(type) ); return STATUS_NOT_IMPLEMENTED; @@ -1942,6 +1989,7 @@ 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 ); @@ -1950,11 +1998,11 @@ NTSTATUS WINAPI BCryptExportKey( BCRYPT_KEY_HANDLE export_key_handle, BCRYPT_KEY if (!type || !size) return STATUS_INVALID_PARAMETER; if (encrypt_key_handle) { - FIXME( "encryption of key not yet supported\n" ); - return STATUS_NOT_IMPLEMENTED; + encrypt_key = get_key_object( encrypt_key_handle ); + if (!encrypt_key) 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;