From f527689b793100c79654ac5d6c1376d128ca3175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20M=C3=BCller?= Date: Mon, 19 Dec 2016 23:58:52 +0100 Subject: bcrypt: Directly implement hmac computation. --- dlls/bcrypt/bcrypt_main.c | 277 +++++++++++++++++----------------------------- 1 file changed, 104 insertions(+), 173 deletions(-) diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 937bdf7..af2314a 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -60,9 +60,6 @@ MAKE_FUNCPTR(gnutls_global_set_log_level); MAKE_FUNCPTR(gnutls_hash); MAKE_FUNCPTR(gnutls_hash_deinit); MAKE_FUNCPTR(gnutls_hash_init); -MAKE_FUNCPTR(gnutls_hmac); -MAKE_FUNCPTR(gnutls_hmac_deinit); -MAKE_FUNCPTR(gnutls_hmac_init); MAKE_FUNCPTR(gnutls_perror); #undef MAKE_FUNCPTR @@ -99,9 +96,6 @@ static BOOL gnutls_initialize(void) LOAD_FUNCPTR(gnutls_hash); LOAD_FUNCPTR(gnutls_hash_deinit); LOAD_FUNCPTR(gnutls_hash_init); - LOAD_FUNCPTR(gnutls_hmac); - LOAD_FUNCPTR(gnutls_hmac_deinit); - LOAD_FUNCPTR(gnutls_hmac_init); LOAD_FUNCPTR(gnutls_perror) #undef LOAD_FUNCPTR @@ -163,6 +157,8 @@ enum alg_id ALG_ID_SHA512 }; +#define MAX_HASH_OUTPUT_BYTES 64 + static const struct { ULONG hash_length; const WCHAR *alg_name; @@ -183,6 +179,19 @@ struct algorithm BOOL hmac; }; +#define MAX_HASH_BLOCK_BITS 1024 + +int alg_block_bits[] = +{ + /* ALG_ID_AES */ 0, + /* ALG_ID_MD5 */ 512, + /* ALG_ID_RNG */ 0, + /* ALG_ID_SHA1 */ 512, + /* ALG_ID_SHA256 */ 512, + /* ALG_ID_SHA384 */ 1024, + /* ALG_ID_SHA512 */ 1024 +}; + NTSTATUS WINAPI BCryptGenRandom(BCRYPT_ALG_HANDLE handle, UCHAR *buffer, ULONG count, ULONG flags) { const DWORD supported_flags = BCRYPT_USE_SYSTEM_PREFERRED_RNG; @@ -289,24 +298,20 @@ NTSTATUS WINAPI BCryptGetFipsAlgorithmMode(BOOLEAN *enabled) } #ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H -struct hash +struct hash_impl { - struct object hdr; - enum alg_id alg_id; - BOOL hmac; union { CC_MD5_CTX md5_ctx; CC_SHA1_CTX sha1_ctx; CC_SHA256_CTX sha256_ctx; CC_SHA512_CTX sha512_ctx; - CCHmacContext hmac_ctx; } u; }; -static NTSTATUS hash_init( struct hash *hash ) +static NTSTATUS hash_init( struct hash_impl *hash, enum alg_id alg_id ) { - switch (hash->alg_id) + switch (alg_id) { case ALG_ID_MD5: CC_MD5_Init( &hash->u.md5_ctx ); @@ -329,50 +334,16 @@ static NTSTATUS hash_init( struct hash *hash ) break; default: - ERR( "unhandled id %u\n", hash->alg_id ); + ERR( "unhandled id %u\n", alg_id ); return STATUS_NOT_IMPLEMENTED; } return STATUS_SUCCESS; } -static NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) +static NTSTATUS hash_update( struct hash_impl *hash, enum alg_id alg_id, + UCHAR *input, ULONG size ) { - CCHmacAlgorithm cc_algorithm; - switch (hash->alg_id) - { - case ALG_ID_MD5: - cc_algorithm = kCCHmacAlgMD5; - break; - - case ALG_ID_SHA1: - cc_algorithm = kCCHmacAlgSHA1; - break; - - case ALG_ID_SHA256: - cc_algorithm = kCCHmacAlgSHA256; - break; - - case ALG_ID_SHA384: - cc_algorithm = kCCHmacAlgSHA384; - break; - - case ALG_ID_SHA512: - cc_algorithm = kCCHmacAlgSHA512; - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); - return STATUS_NOT_IMPLEMENTED; - } - - CCHmacInit( &hash->u.hmac_ctx, cc_algorithm, key, key_size ); - return STATUS_SUCCESS; -} - - -static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) -{ - switch (hash->alg_id) + switch (alg_id) { case ALG_ID_MD5: CC_MD5_Update( &hash->u.md5_ctx, input, size ); @@ -395,21 +366,16 @@ static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) break; default: - ERR( "unhandled id %u\n", hash->alg_id ); + ERR( "unhandled id %u\n", alg_id ); return STATUS_NOT_IMPLEMENTED; } return STATUS_SUCCESS; } -static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) -{ - CCHmacUpdate( &hash->u.hmac_ctx, input, size ); - return STATUS_SUCCESS; -} - -static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) +static NTSTATUS hash_finish( struct hash_impl *hash, enum alg_id alg_id, + UCHAR *output, ULONG size ) { - switch (hash->alg_id) + switch (alg_id) { case ALG_ID_MD5: CC_MD5_Final( output, &hash->u.md5_ctx ); @@ -432,37 +398,25 @@ static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) break; default: - ERR( "unhandled id %u\n", hash->alg_id ); + ERR( "unhandled id %u\n", alg_id ); break; } return STATUS_SUCCESS; } -static NTSTATUS hmac_finish( struct hash *hash, UCHAR *output, ULONG size ) -{ - CCHmacFinal( &hash->u.hmac_ctx, output ); - return STATUS_SUCCESS; -} #elif defined(HAVE_GNUTLS_HASH) -struct hash +struct hash_impl { - struct object hdr; - enum alg_id alg_id; - BOOL hmac; - union - { - gnutls_hash_hd_t hash_handle; - gnutls_hmac_hd_t hmac_handle; - } u; + gnutls_hash_hd_t hash_handle; }; -static NTSTATUS hash_init( struct hash *hash ) +static NTSTATUS hash_init( struct hash_impl *hash, enum alg_id alg_id ) { gnutls_digest_algorithm_t alg; if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; - switch (hash->alg_id) + switch (alg_id) { case ALG_ID_MD5: alg = GNUTLS_DIG_MD5; @@ -484,117 +438,63 @@ static NTSTATUS hash_init( struct hash *hash ) break; default: - ERR( "unhandled id %u\n", hash->alg_id ); - return STATUS_NOT_IMPLEMENTED; - } - - if (pgnutls_hash_init( &hash->u.hash_handle, alg )) return STATUS_INTERNAL_ERROR; - return STATUS_SUCCESS; -} - -static NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) -{ - gnutls_mac_algorithm_t alg; - - if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; - - switch (hash->alg_id) - { - case ALG_ID_MD5: - alg = GNUTLS_MAC_MD5; - break; - case ALG_ID_SHA1: - alg = GNUTLS_MAC_SHA1; - break; - - case ALG_ID_SHA256: - alg = GNUTLS_MAC_SHA256; - break; - - case ALG_ID_SHA384: - alg = GNUTLS_MAC_SHA384; - break; - - case ALG_ID_SHA512: - alg = GNUTLS_MAC_SHA512; - break; - - default: - ERR( "unhandled id %u\n", hash->alg_id ); + ERR( "unhandled id %u\n", alg_id ); return STATUS_NOT_IMPLEMENTED; } - if (pgnutls_hmac_init( &hash->u.hmac_handle, alg, key, key_size )) return STATUS_INTERNAL_ERROR; - return STATUS_SUCCESS; -} - -static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) -{ - if (pgnutls_hash( hash->u.hash_handle, input, size )) return STATUS_INTERNAL_ERROR; + if (pgnutls_hash_init( &hash->hash_handle, alg )) return STATUS_INTERNAL_ERROR; return STATUS_SUCCESS; } -static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) +static NTSTATUS hash_update( struct hash_impl *hash, enum alg_id alg_id, + UCHAR *input, ULONG size ) { - if (pgnutls_hmac( hash->u.hmac_handle, input, size )) return STATUS_INTERNAL_ERROR; + if (pgnutls_hash( hash->hash_handle, input, size )) return STATUS_INTERNAL_ERROR; return STATUS_SUCCESS; } -static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) +static NTSTATUS hash_finish( struct hash_impl *hash, enum alg_id alg_id, + UCHAR *output, ULONG size ) { - pgnutls_hash_deinit( hash->u.hash_handle, output ); + pgnutls_hash_deinit( hash->hash_handle, output ); return STATUS_SUCCESS; } -static NTSTATUS hmac_finish( struct hash *hash, UCHAR *output, ULONG size ) -{ - pgnutls_hmac_deinit( hash->u.hmac_handle, output ); - return STATUS_SUCCESS; -} #else -struct hash +struct hash_impl { - struct object hdr; - BOOL hmac; - enum alg_id alg_id; -}; -static NTSTATUS hash_init( struct hash *hash ) -{ - ERR( "support for hashes not available at build time\n" ); - return STATUS_NOT_IMPLEMENTED; -} - -static NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) -{ - ERR( "support for hashes not available at build time\n" ); - return STATUS_NOT_IMPLEMENTED; -} +}; -static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) +static NTSTATUS hash_init( struct hash_impl *hash, enum alg_id alg_id ) { ERR( "support for hashes not available at build time\n" ); return STATUS_NOT_IMPLEMENTED; } -static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) +static NTSTATUS hash_update( struct hash_impl *hash, enum alg_id alg_id, + UCHAR *input, ULONG size ) { ERR( "support for hashes not available at build time\n" ); return STATUS_NOT_IMPLEMENTED; } -static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) +static NTSTATUS hash_finish( struct hash_impl *hash, enum alg_id alg_id, + UCHAR *output, ULONG size ) { ERR( "support for hashes not available at build time\n" ); return STATUS_NOT_IMPLEMENTED; } +#endif -static NTSTATUS hmac_finish( struct hash *hash, UCHAR *output, ULONG size ) +struct hash { - ERR( "support for hashes not available at build time\n" ); - return STATUS_NOT_IMPLEMENTED; -} -#endif + struct object hdr; + enum alg_id alg_id; + BOOL hmac; + struct hash_impl outer; + struct hash_impl inner; +}; #ifdef _WIN64 #define OBJECT_LENGTH_AES 654 @@ -787,8 +687,11 @@ NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDL UCHAR *secret, ULONG secretlen, ULONG flags ) { struct algorithm *alg = algorithm; + UCHAR buffer[MAX_HASH_BLOCK_BITS / 8]; 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 ); @@ -806,17 +709,45 @@ NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDL hash->alg_id = alg->id; hash->hmac = alg->hmac; - if (hash->hmac) + status = hash_init( &hash->inner, hash->alg_id ); + if (status || !hash->hmac) goto end; + status = hash_init( &hash->outer, hash->alg_id ); + if (status) goto end; + + /* reduce key size if too big */ + block_bytes = alg_block_bits[hash->alg_id] / 8; + if (secretlen > block_bytes) { - status = hmac_init( hash, secret, secretlen ); + struct hash_impl temp; + status = hash_init( &temp, hash->alg_id ); + if (status) goto end; + status = hash_update( &temp, hash->alg_id, secret, secretlen ); + if (status) goto end; + memset( buffer, 0, block_bytes ); + status = hash_finish( &temp, hash->alg_id, buffer, alg_props[hash->alg_id].hash_length ); + if (status) goto end; } else { - status = hash_init( hash ); + memset( buffer, 0, block_bytes ); + memcpy( buffer, secret, secretlen ); } + /* initialize outer hash */ + for (i = 0; i < block_bytes; i++) + buffer[i] ^= 0x5c; + status = hash_update( &hash->outer, hash->alg_id, buffer, block_bytes ); + if (status) goto end; + + /* initialize inner hash */ + for (i = 0; i < block_bytes; i++) + buffer[i] ^= (0x5c ^ 0x36); + status = hash_update( &hash->inner, hash->alg_id, buffer, block_bytes ); + +end: if (status != STATUS_SUCCESS) { + /* FIXME: call hash_finish to release resources */ HeapFree( GetProcessHeap(), 0, hash ); return status; } @@ -845,33 +776,33 @@ NTSTATUS WINAPI BCryptHashData( BCRYPT_HASH_HANDLE handle, UCHAR *input, ULONG s if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE; if (!input) return STATUS_SUCCESS; - if (hash->hmac) - { - return hmac_update( hash, input, size ); - } - else - { - return hash_update( hash, input, size ); - } + return hash_update( &hash->inner, hash->alg_id, input, size ); } NTSTATUS WINAPI BCryptFinishHash( BCRYPT_HASH_HANDLE handle, UCHAR *output, ULONG size, ULONG flags ) { + UCHAR buffer[MAX_HASH_OUTPUT_BYTES]; struct hash *hash = handle; + NTSTATUS status; + int hash_size; TRACE( "%p, %p, %u, %08x\n", handle, output, size, flags ); if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE; if (!output) return STATUS_INVALID_PARAMETER; - if (hash->hmac) - { - return hmac_finish( hash, output, size ); - } - else - { - return hash_finish( hash, output, size ); - } + if (!hash->hmac) + return hash_finish( &hash->inner, hash->alg_id, output, size ); + + hash_size = alg_props[hash->alg_id].hash_length; + + status = hash_finish( &hash->inner, hash->alg_id, buffer, hash_size); + if (status) return status; + + status = hash_update( &hash->outer, hash->alg_id, buffer, hash_size); + if (status) return status; + + return hash_finish( &hash->outer, hash->alg_id, output, size); } NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE algorithm, UCHAR *secret, ULONG secretlen, -- 2.9.0