diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 1b84ace8abe..b9c51a1f9fe 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -366,13 +366,17 @@ static NTSTATUS hash_finish( struct hash_impl *hash, enum alg_id alg_id, return STATUS_SUCCESS; } +#define HASH_FLAG_HMAC 0x01 +#define HASH_FLAG_REUSABLE 0x02 struct hash { - struct object hdr; - enum alg_id alg_id; - BOOL hmac; - struct hash_impl outer; - struct hash_impl inner; + struct object hdr; + enum alg_id alg_id; + ULONG flags; + UCHAR *secret; + ULONG secret_len; + struct hash_impl outer; + struct hash_impl inner; }; #define BLOCK_LENGTH_AES 16 @@ -587,19 +591,45 @@ NTSTATUS WINAPI BCryptGetProperty( BCRYPT_HANDLE handle, LPCWSTR prop, UCHAR *bu } } +static NTSTATUS prepare_hash( struct hash *hash ) +{ + UCHAR buffer[MAX_HASH_BLOCK_BITS / 8] = {0}; + int block_bytes, i; + NTSTATUS status; + + /* initialize hash */ + if ((status = hash_init( &hash->inner, hash->alg_id ))) return status; + if (!(hash->flags & HASH_FLAG_HMAC)) return STATUS_SUCCESS; + + /* initialize hmac */ + if ((status = hash_init( &hash->outer, hash->alg_id ))) return status; + block_bytes = alg_props[hash->alg_id].block_bits / 8; + if (hash->secret_len > block_bytes) + { + struct hash_impl temp; + if ((status = hash_init( &temp, hash->alg_id ))) return status; + if ((status = hash_update( &temp, hash->alg_id, hash->secret, hash->secret_len ))) return status; + if ((status = hash_finish( &temp, hash->alg_id, buffer, + alg_props[hash->alg_id].hash_length ))) return status; + } + else memcpy( buffer, hash->secret, hash->secret_len ); + + for (i = 0; i < block_bytes; i++) buffer[i] ^= 0x5c; + if ((status = hash_update( &hash->outer, hash->alg_id, buffer, block_bytes ))) return status; + for (i = 0; i < block_bytes; i++) buffer[i] ^= (0x5c ^ 0x36); + return hash_update( &hash->inner, hash->alg_id, buffer, block_bytes ); +} + NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDLE *handle, UCHAR *object, ULONG objectlen, UCHAR *secret, ULONG secretlen, ULONG flags ) { struct algorithm *alg = algorithm; - UCHAR buffer[MAX_HASH_BLOCK_BITS / 8] = {0}; struct hash *hash; - int block_bytes; NTSTATUS status; - int i; TRACE( "%p, %p, %p, %u, %p, %u, %08x - stub\n", algorithm, handle, object, objectlen, secret, secretlen, flags ); - if (flags) + if (flags & ~BCRYPT_HASH_REUSABLE_FLAG) { FIXME( "unimplemented flags %08x\n", flags ); return STATUS_NOT_IMPLEMENTED; @@ -608,38 +638,23 @@ NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDL if (!alg || alg->hdr.magic != MAGIC_ALG) return STATUS_INVALID_HANDLE; if (object) FIXME( "ignoring object buffer\n" ); - if (!(hash = heap_alloc( sizeof(*hash) ))) return STATUS_NO_MEMORY; + if (!(hash = heap_alloc_zero( sizeof(*hash) ))) return STATUS_NO_MEMORY; hash->hdr.magic = MAGIC_HASH; hash->alg_id = alg->id; - hash->hmac = alg->hmac; + if (alg->hmac) hash->flags = HASH_FLAG_HMAC; + if (flags & BCRYPT_HASH_REUSABLE_FLAG) hash->flags |= HASH_FLAG_REUSABLE; - /* initialize hash */ - if ((status = hash_init( &hash->inner, hash->alg_id ))) goto end; - if (!hash->hmac) goto end; - - /* initialize hmac */ - if ((status = hash_init( &hash->outer, hash->alg_id ))) goto end; - block_bytes = alg_props[hash->alg_id].block_bits / 8; - if (secretlen > block_bytes) + if (secretlen && !(hash->secret = heap_alloc( secretlen ))) { - struct hash_impl temp; - if ((status = hash_init( &temp, hash->alg_id ))) goto end; - if ((status = hash_update( &temp, hash->alg_id, secret, secretlen ))) goto end; - if ((status = hash_finish( &temp, hash->alg_id, buffer, - alg_props[hash->alg_id].hash_length ))) goto end; + heap_free( hash ); + return STATUS_NO_MEMORY; } - else - { - memcpy( buffer, secret, secretlen ); - } - for (i = 0; i < block_bytes; i++) buffer[i] ^= 0x5c; - if ((status = hash_update( &hash->outer, hash->alg_id, buffer, block_bytes ))) goto end; - for (i = 0; i < block_bytes; i++) buffer[i] ^= (0x5c ^ 0x36); - status = hash_update( &hash->inner, hash->alg_id, buffer, block_bytes ); + memcpy( hash->secret, secret, secretlen ); + hash->secret_len = secretlen; -end: - if (status != STATUS_SUCCESS) + if ((status = prepare_hash( hash )) != STATUS_SUCCESS) { + heap_free( hash->secret ); heap_free( hash ); return status; } @@ -664,6 +679,12 @@ NTSTATUS WINAPI BCryptDuplicateHash( BCRYPT_HASH_HANDLE handle, BCRYPT_HASH_HAND return STATUS_NO_MEMORY; memcpy( hash_copy, hash_orig, sizeof(*hash_orig) ); + if (hash_orig->secret && !(hash_copy->secret = heap_alloc( hash_orig->secret_len ))) + { + heap_free( hash_copy ); + return STATUS_NO_MEMORY; + } + memcpy( hash_copy->secret, hash_orig->secret, hash_orig->secret_len ); *handle_copy = hash_copy; return STATUS_SUCCESS; @@ -677,6 +698,7 @@ NTSTATUS WINAPI BCryptDestroyHash( BCRYPT_HASH_HANDLE handle ) if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_PARAMETER; hash->hdr.magic = 0; + heap_free( hash->secret ); heap_free( hash ); return STATUS_SUCCESS; } @@ -705,13 +727,19 @@ NTSTATUS WINAPI BCryptFinishHash( BCRYPT_HASH_HANDLE handle, UCHAR *output, ULON if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE; if (!output) return STATUS_INVALID_PARAMETER; - if (!hash->hmac) - return hash_finish( &hash->inner, hash->alg_id, output, size ); + if (!(hash->flags & HASH_FLAG_HMAC)) + { + if ((status = hash_finish( &hash->inner, hash->alg_id, output, size ))) return status; + if (hash->flags & HASH_FLAG_REUSABLE) return prepare_hash( hash ); + return STATUS_SUCCESS; + } hash_length = alg_props[hash->alg_id].hash_length; if ((status = hash_finish( &hash->inner, hash->alg_id, buffer, hash_length ))) return status; if ((status = hash_update( &hash->outer, hash->alg_id, buffer, hash_length ))) return status; - return hash_finish( &hash->outer, hash->alg_id, output, size ); + if ((status = hash_finish( &hash->outer, hash->alg_id, output, size ))) return status; + if (hash->flags & HASH_FLAG_REUSABLE) return prepare_hash( hash ); + return STATUS_SUCCESS; } NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE algorithm, UCHAR *secret, ULONG secretlen, diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 5c1a9dc8340..a8bc43ce3db 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -221,7 +221,9 @@ struct hash_test const char *alg; unsigned hash_size; const char *hash; + const char *hash2; const char *hmac_hash; + const char *hmac_hash2; }; static void test_hash(const struct hash_test *test) @@ -269,6 +271,35 @@ static void test_hash(const struct hash_test *test) ret = pBCryptDestroyHash(hash); ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + hash = NULL; + len = sizeof(buf); + ret = pBCryptCreateHash(alg, &hash, buf, len, NULL, 0, BCRYPT_HASH_REUSABLE_FLAG); + ok(ret == STATUS_SUCCESS || broken(ret == STATUS_INVALID_PARAMETER) /* < win8 */, "got %08x\n", ret); + if (ret == STATUS_SUCCESS) + { + ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + + memset(hash_buf, 0, sizeof(hash_buf)); + ret = pBCryptFinishHash(hash, hash_buf, test->hash_size, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + format_hash( hash_buf, test->hash_size, str ); + ok(!strcmp(str, test->hash), "got %s\n", str); + + /* reuse it */ + ret = pBCryptHashData(hash, (UCHAR *)"tset", sizeof("tset"), 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + + memset(hash_buf, 0, sizeof(hash_buf)); + ret = pBCryptFinishHash(hash, hash_buf, test->hash_size, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + format_hash( hash_buf, test->hash_size, str ); + ok(!strcmp(str, test->hash2), "got %s\n", str); + + ret = pBCryptDestroyHash(hash); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + } + ret = pBCryptCloseAlgorithmProvider(alg, 0); ok(ret == STATUS_SUCCESS, "got %08x\n", ret); @@ -298,6 +329,35 @@ static void test_hash(const struct hash_test *test) ret = pBCryptDestroyHash(hash); ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + hash = NULL; + len = sizeof(buf_hmac); + ret = pBCryptCreateHash(alg, &hash, buf_hmac, len, (UCHAR *)"key", sizeof("key"), BCRYPT_HASH_REUSABLE_FLAG); + ok(ret == STATUS_SUCCESS || broken(ret == STATUS_INVALID_PARAMETER) /* < win8 */, "got %08x\n", ret); + if (ret == STATUS_SUCCESS) + { + ret = pBCryptHashData(hash, (UCHAR *)"test", sizeof("test"), 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + + memset(hmac_hash, 0, sizeof(hmac_hash)); + ret = pBCryptFinishHash(hash, hmac_hash, test->hash_size, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + format_hash( hmac_hash, test->hash_size, str ); + ok(!strcmp(str, test->hmac_hash), "got %s\n", str); + + /* reuse it */ + ret = pBCryptHashData(hash, (UCHAR *)"tset", sizeof("tset"), 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + + memset(hmac_hash, 0, sizeof(hmac_hash)); + ret = pBCryptFinishHash(hash, hmac_hash, test->hash_size, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + format_hash( hmac_hash, test->hash_size, str ); + ok(!strcmp(str, test->hmac_hash2), "got %s\n", str); + + ret = pBCryptDestroyHash(hash); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + } + ret = pBCryptDestroyHash(hash); ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); @@ -310,52 +370,57 @@ static void test_hash(const struct hash_test *test) static void test_hashes(void) { - static const struct hash_test tests[] = { - { - "SHA1", - 20, - "961fa64958818f767707072755d7018dcd278e94", - "2472cf65d0e090618d769d3e46f0d9446cf212da" + static const struct hash_test tests[] = + { + { "SHA1", 20, + "961fa64958818f767707072755d7018dcd278e94", + "9314f62ff64197143c91fc86de37e9ae776a3fb8", + "2472cf65d0e090618d769d3e46f0d9446cf212da", + "b2d2ba8cfd714d474cf0d9622cc5d15e1f53d53f", }, - { - "SHA256", - 32, - "ceb73749c899693706ede1e30c9929b3fd5dd926163831c2fb8bd41e6efb1126", - "34c1aa473a4468a91d06e7cdbc75bc4f93b830ccfc2a47ffd74e8e6ed29e4c72" + { "SHA256", 32, + "ceb73749c899693706ede1e30c9929b3fd5dd926163831c2fb8bd41e6efb1126", + "ea0938c118a7b15954f41b85195f2b42aec3a9429c63f593cfa65c137ffaa986", + "34c1aa473a4468a91d06e7cdbc75bc4f93b830ccfc2a47ffd74e8e6ed29e4c72", + "55feb7052060bd99e33f36eb0982c7f4856eb6a84fbefe19a1afd9faafc3af6f", }, - { - "SHA384", - 48, - "62b21e90c9022b101671ba1f808f8631a8149f0f12904055839a35c1ca78ae53" - "63eed1e743a692d70e0504b0cfd12ef9", - "4b3e6d6ff2da121790ab7e7b9247583e3a7eed2db5bd4dabc680303b1608f37d" - "fdc836d96a704c03283bc05b4f6c5eb8" + { "SHA384", 48, + "62b21e90c9022b101671ba1f808f8631a8149f0f12904055839a35c1ca78ae53" + "63eed1e743a692d70e0504b0cfd12ef9", + "724db7c0bbc51ef1ac3fc793083fc54c0e5c423faec9b11378c01c236b19aaaf" + "a45177ad055feaf003968cc40ece44c7", + "4b3e6d6ff2da121790ab7e7b9247583e3a7eed2db5bd4dabc680303b1608f37d" + "fdc836d96a704c03283bc05b4f6c5eb8", + "03e1818e5c165a0e54619e513acb06c393e1a6cb0ddbb4036b5f29617b334642" + "e6e0be8b214d8508595b17a8c4b4e7db", }, - { - "SHA512", - 64, - "d55ced17163bf5386f2cd9ff21d6fd7fe576a915065c24744d09cfae4ec84ee1" - "ef6ef11bfbc5acce3639bab725b50a1fe2c204f8c820d6d7db0df0ecbc49c5ca", - "415fb6b10018ca03b38a1b1399c42ac0be5e8aceddb9a73103f5e543bf2d888f" - "2eecf91373941f9315dd730a77937fa92444450fbece86f409d9cb5ec48c6513" + { "SHA512", 64, + "d55ced17163bf5386f2cd9ff21d6fd7fe576a915065c24744d09cfae4ec84ee1" + "ef6ef11bfbc5acce3639bab725b50a1fe2c204f8c820d6d7db0df0ecbc49c5ca", + "7752d707b54d2b00e7d1c09120d189475b0fd2e31ebb988cf0a01fc8492ddc0b" + "3ca9c9ca61d9d7d1fb65ca7665e87f043c1d5bc9f786f8345e951c2d91ac594f", + "415fb6b10018ca03b38a1b1399c42ac0be5e8aceddb9a73103f5e543bf2d888f" + "2eecf91373941f9315dd730a77937fa92444450fbece86f409d9cb5ec48c6513", + "1487bcecba46ae677622fa499e4cb2f0fdf92f6f3427cba76382d537a06e49c3" + "3e70a2fc1fc730092bf21128c3704cc6387f6dfbf7e2f9f315bbb894505a1205", }, - { - "MD2", - 16, - "1bb33606ba908912a84221109d29cd7e", - "7f05b0638d77f4a27f3a9c4d353cd648" + { "MD2", 16, + "1bb33606ba908912a84221109d29cd7e", + "b9a6ad9323b17e2d0cd389dddd6ef78a", + "7f05b0638d77f4a27f3a9c4d353cd648", + "05980873e6bfdd05dd7b30078de7e42a", }, - { - "MD4", - 16, - "74b5db93c0b41e36ca7074338fc0b637", - "bc2e8ac4d8248ed21b8d26227a30ea3a" + { "MD4", 16, + "74b5db93c0b41e36ca7074338fc0b637", + "a14a9ff2059a8c28f47b01e6bc48a1bf", + "bc2e8ac4d8248ed21b8d26227a30ea3a", + "b609db0eb4b8669db74f2c20099701e4", }, - { - "MD5", - 16, - "e2a3e68d23ce348b8f68b3079de3d4c9", - "7bda029b93fa8d817fcc9e13d6bdf092" + { "MD5", 16, + "e2a3e68d23ce348b8f68b3079de3d4c9", + "bcdd7ca574342aa9db0e212348eacb16", + "7bda029b93fa8d817fcc9e13d6bdf092", + "dd636ab8e9592c5088e57c37d44c5bb3", } }; unsigned i; diff --git a/include/bcrypt.h b/include/bcrypt.h index ba78c1d9d0b..a0b0d524956 100644 --- a/include/bcrypt.h +++ b/include/bcrypt.h @@ -222,6 +222,9 @@ typedef PVOID BCRYPT_HASH_HANDLE; /* Flags for BCryptEncrypt/BCryptDecrypt */ #define BCRYPT_BLOCK_PADDING 0x00000001 +/* Flags for BCryptCreateHash */ +#define BCRYPT_HASH_REUSABLE_FLAG 0x00000020 + NTSTATUS WINAPI BCryptCloseAlgorithmProvider(BCRYPT_ALG_HANDLE, ULONG); NTSTATUS WINAPI BCryptCreateHash(BCRYPT_ALG_HANDLE, BCRYPT_HASH_HANDLE *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG); NTSTATUS WINAPI BCryptDecrypt(BCRYPT_KEY_HANDLE, PUCHAR, ULONG, VOID *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG *, ULONG);