[1/5] bcrypt: Add builtin HMAC implementation.

Sebastian Lackner sebastian at fds-team.de
Tue Mar 7 16:13:51 CST 2017


From: Michael Müller <michael at fds-team.de>

Signed-off-by: Michael Müller <michael at fds-team.de>
Signed-off-by: Sebastian Lackner <sebastian at fds-team.de>
---

Please blame gnutls for this series. Some applications want to duplicate
the internal hash state, but unfortunately gnutls doesn't expose this
functionality. This series replaces the internal implementation with a
builtin one. As a nice side effect, we could later also use the builtin
sha256/sha512 implementation to verify our Gecko/Mono downloads. :)

This first patch implements a builtin HMAC algorithm.

 dlls/bcrypt/bcrypt_main.c |  264 ++++++++++++++--------------------------------
 1 file changed, 84 insertions(+), 180 deletions(-)

diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c
index 6023c942e49..849dc3e5c35 100644
--- a/dlls/bcrypt/bcrypt_main.c
+++ b/dlls/bcrypt/bcrypt_main.c
@@ -56,9 +56,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
 
@@ -91,9 +88,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
 
@@ -153,16 +147,20 @@ enum alg_id
     ALG_ID_SHA512
 };
 
+#define MAX_HASH_OUTPUT_BYTES 64
+#define MAX_HASH_BLOCK_BITS 1024
+
 static const struct {
     ULONG hash_length;
+    ULONG block_bits;
     const WCHAR *alg_name;
 } alg_props[] = {
-    /* ALG_ID_MD5    */ { 16, BCRYPT_MD5_ALGORITHM },
-    /* ALG_ID_RNG    */ {  0, BCRYPT_RNG_ALGORITHM },
-    /* ALG_ID_SHA1   */ { 20, BCRYPT_SHA1_ALGORITHM },
-    /* ALG_ID_SHA256 */ { 32, BCRYPT_SHA256_ALGORITHM },
-    /* ALG_ID_SHA384 */ { 48, BCRYPT_SHA384_ALGORITHM },
-    /* ALG_ID_SHA512 */ { 64, BCRYPT_SHA512_ALGORITHM }
+    /* ALG_ID_MD5    */ { 16,  512, BCRYPT_MD5_ALGORITHM },
+    /* ALG_ID_RNG    */ {  0,    0, BCRYPT_RNG_ALGORITHM },
+    /* ALG_ID_SHA1   */ { 20,  512, BCRYPT_SHA1_ALGORITHM },
+    /* ALG_ID_SHA256 */ { 32,  512, BCRYPT_SHA256_ALGORITHM },
+    /* ALG_ID_SHA384 */ { 48, 1024, BCRYPT_SHA384_ALGORITHM },
+    /* ALG_ID_SHA512 */ { 64, 1024, BCRYPT_SHA512_ALGORITHM }
 };
 
 struct algorithm
@@ -278,24 +276,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 );
@@ -318,50 +312,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 )
-{
-    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 )
+static NTSTATUS hash_update( struct hash_impl *hash, enum alg_id alg_id,
+                             UCHAR *input, ULONG size )
 {
-    switch (hash->alg_id)
+    switch (alg_id)
     {
     case ALG_ID_MD5:
         CC_MD5_Update( &hash->u.md5_ctx, input, size );
@@ -384,21 +344,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 )
+static NTSTATUS hash_finish( struct hash_impl *hash, enum alg_id alg_id,
+                             UCHAR *output, ULONG size )
 {
-    CCHmacUpdate( &hash->u.hmac_ctx, input, size );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size )
-{
-    switch (hash->alg_id)
+    switch (alg_id)
     {
     case ALG_ID_MD5:
         CC_MD5_Final( output, &hash->u.md5_ctx );
@@ -421,42 +376,30 @@ 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;
         break;
+
     case ALG_ID_SHA1:
         alg = GNUTLS_DIG_SHA1;
         break;
@@ -474,117 +417,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;
+    if (pgnutls_hash_init( &hash->hash_handle, alg )) return STATUS_INTERNAL_ERROR;
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS hash_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_hash( hash->u.hash_handle, input, size )) return STATUS_INTERNAL_ERROR;
+    if (pgnutls_hash( hash->hash_handle, input, size )) return STATUS_INTERNAL_ERROR;
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size )
+static NTSTATUS hash_finish( struct hash_impl *hash, enum alg_id alg_id,
+                             UCHAR *output, ULONG size )
 {
-    if (pgnutls_hmac( hash->u.hmac_handle, input, size )) return STATUS_INTERNAL_ERROR;
+    pgnutls_hash_deinit( hash->hash_handle, output );
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size )
-{
-    pgnutls_hash_deinit( hash->u.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;
+};
 
 #define OBJECT_LENGTH_MD5       274
 #define OBJECT_LENGTH_SHA1      278
@@ -735,8 +624,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] = {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 );
@@ -754,17 +646,34 @@ NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDL
     hash->alg_id    = alg->id;
     hash->hmac      = alg->hmac;
 
-    if (hash->hmac)
+    /* 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)
     {
-        status = hmac_init( hash, secret, 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;
     }
     else
     {
-        status = hash_init( hash );
+        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 );
 
+end:
     if (status != STATUS_SUCCESS)
     {
+        /* FIXME: call hash_finish to release resources */
         HeapFree( GetProcessHeap(), 0, hash );
         return status;
     }
@@ -793,33 +702,28 @@ 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_length;
 
     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_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 );
 }
 
 NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE algorithm, UCHAR *secret, ULONG secretlen,
-- 
2.11.0



More information about the wine-patches mailing list