Commit 9c123204 authored by Michael Müller's avatar Michael Müller Committed by Alexandre Julliard

bcrypt: Add builtin HMAC implementation.

parent 332d5921
...@@ -56,9 +56,6 @@ MAKE_FUNCPTR(gnutls_global_set_log_level); ...@@ -56,9 +56,6 @@ MAKE_FUNCPTR(gnutls_global_set_log_level);
MAKE_FUNCPTR(gnutls_hash); MAKE_FUNCPTR(gnutls_hash);
MAKE_FUNCPTR(gnutls_hash_deinit); MAKE_FUNCPTR(gnutls_hash_deinit);
MAKE_FUNCPTR(gnutls_hash_init); MAKE_FUNCPTR(gnutls_hash_init);
MAKE_FUNCPTR(gnutls_hmac);
MAKE_FUNCPTR(gnutls_hmac_deinit);
MAKE_FUNCPTR(gnutls_hmac_init);
MAKE_FUNCPTR(gnutls_perror); MAKE_FUNCPTR(gnutls_perror);
#undef MAKE_FUNCPTR #undef MAKE_FUNCPTR
...@@ -91,9 +88,6 @@ static BOOL gnutls_initialize(void) ...@@ -91,9 +88,6 @@ static BOOL gnutls_initialize(void)
LOAD_FUNCPTR(gnutls_hash); LOAD_FUNCPTR(gnutls_hash);
LOAD_FUNCPTR(gnutls_hash_deinit); LOAD_FUNCPTR(gnutls_hash_deinit);
LOAD_FUNCPTR(gnutls_hash_init); LOAD_FUNCPTR(gnutls_hash_init);
LOAD_FUNCPTR(gnutls_hmac);
LOAD_FUNCPTR(gnutls_hmac_deinit);
LOAD_FUNCPTR(gnutls_hmac_init);
LOAD_FUNCPTR(gnutls_perror) LOAD_FUNCPTR(gnutls_perror)
#undef LOAD_FUNCPTR #undef LOAD_FUNCPTR
...@@ -153,16 +147,20 @@ enum alg_id ...@@ -153,16 +147,20 @@ enum alg_id
ALG_ID_SHA512 ALG_ID_SHA512
}; };
#define MAX_HASH_OUTPUT_BYTES 64
#define MAX_HASH_BLOCK_BITS 1024
static const struct { static const struct {
ULONG hash_length; ULONG hash_length;
ULONG block_bits;
const WCHAR *alg_name; const WCHAR *alg_name;
} alg_props[] = { } alg_props[] = {
/* ALG_ID_MD5 */ { 16, BCRYPT_MD5_ALGORITHM }, /* ALG_ID_MD5 */ { 16, 512, BCRYPT_MD5_ALGORITHM },
/* ALG_ID_RNG */ { 0, BCRYPT_RNG_ALGORITHM }, /* ALG_ID_RNG */ { 0, 0, BCRYPT_RNG_ALGORITHM },
/* ALG_ID_SHA1 */ { 20, BCRYPT_SHA1_ALGORITHM }, /* ALG_ID_SHA1 */ { 20, 512, BCRYPT_SHA1_ALGORITHM },
/* ALG_ID_SHA256 */ { 32, BCRYPT_SHA256_ALGORITHM }, /* ALG_ID_SHA256 */ { 32, 512, BCRYPT_SHA256_ALGORITHM },
/* ALG_ID_SHA384 */ { 48, BCRYPT_SHA384_ALGORITHM }, /* ALG_ID_SHA384 */ { 48, 1024, BCRYPT_SHA384_ALGORITHM },
/* ALG_ID_SHA512 */ { 64, BCRYPT_SHA512_ALGORITHM } /* ALG_ID_SHA512 */ { 64, 1024, BCRYPT_SHA512_ALGORITHM }
}; };
struct algorithm struct algorithm
...@@ -278,24 +276,20 @@ NTSTATUS WINAPI BCryptGetFipsAlgorithmMode(BOOLEAN *enabled) ...@@ -278,24 +276,20 @@ NTSTATUS WINAPI BCryptGetFipsAlgorithmMode(BOOLEAN *enabled)
} }
#ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H #ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H
struct hash struct hash_impl
{ {
struct object hdr;
enum alg_id alg_id;
BOOL hmac;
union union
{ {
CC_MD5_CTX md5_ctx; CC_MD5_CTX md5_ctx;
CC_SHA1_CTX sha1_ctx; CC_SHA1_CTX sha1_ctx;
CC_SHA256_CTX sha256_ctx; CC_SHA256_CTX sha256_ctx;
CC_SHA512_CTX sha512_ctx; CC_SHA512_CTX sha512_ctx;
CCHmacContext hmac_ctx;
} u; } 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: case ALG_ID_MD5:
CC_MD5_Init( &hash->u.md5_ctx ); CC_MD5_Init( &hash->u.md5_ctx );
...@@ -318,50 +312,16 @@ static NTSTATUS hash_init( struct hash *hash ) ...@@ -318,50 +312,16 @@ static NTSTATUS hash_init( struct hash *hash )
break; break;
default: 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; return STATUS_NOT_IMPLEMENTED;
} }
CCHmacInit( &hash->u.hmac_ctx, cc_algorithm, key, key_size );
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
static NTSTATUS hash_update( struct hash_impl *hash, enum alg_id alg_id,
static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) UCHAR *input, ULONG size )
{ {
switch (hash->alg_id) switch (alg_id)
{ {
case ALG_ID_MD5: case ALG_ID_MD5:
CC_MD5_Update( &hash->u.md5_ctx, input, size ); CC_MD5_Update( &hash->u.md5_ctx, input, size );
...@@ -384,21 +344,16 @@ static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) ...@@ -384,21 +344,16 @@ static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size )
break; break;
default: default:
ERR( "unhandled id %u\n", hash->alg_id ); ERR( "unhandled id %u\n", alg_id );
return STATUS_NOT_IMPLEMENTED; return STATUS_NOT_IMPLEMENTED;
} }
return STATUS_SUCCESS; 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 ); switch (alg_id)
return STATUS_SUCCESS;
}
static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size )
{
switch (hash->alg_id)
{ {
case ALG_ID_MD5: case ALG_ID_MD5:
CC_MD5_Final( output, &hash->u.md5_ctx ); CC_MD5_Final( output, &hash->u.md5_ctx );
...@@ -421,42 +376,30 @@ static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) ...@@ -421,42 +376,30 @@ static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size )
break; break;
default: default:
ERR( "unhandled id %u\n", hash->alg_id ); ERR( "unhandled id %u\n", alg_id );
break; break;
} }
return STATUS_SUCCESS; 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) #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_hash_hd_t hash_handle;
gnutls_hmac_hd_t hmac_handle;
} u;
}; };
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; gnutls_digest_algorithm_t alg;
if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; if (!libgnutls_handle) return STATUS_INTERNAL_ERROR;
switch (hash->alg_id) switch (alg_id)
{ {
case ALG_ID_MD5: case ALG_ID_MD5:
alg = GNUTLS_DIG_MD5; alg = GNUTLS_DIG_MD5;
break; break;
case ALG_ID_SHA1: case ALG_ID_SHA1:
alg = GNUTLS_DIG_SHA1; alg = GNUTLS_DIG_SHA1;
break; break;
...@@ -474,117 +417,63 @@ static NTSTATUS hash_init( struct hash *hash ) ...@@ -474,117 +417,63 @@ static NTSTATUS hash_init( struct hash *hash )
break; break;
default: default:
ERR( "unhandled id %u\n", hash->alg_id ); ERR( "unhandled id %u\n", alg_id );
return STATUS_NOT_IMPLEMENTED; return STATUS_NOT_IMPLEMENTED;
} }
if (pgnutls_hash_init( &hash->u.hash_handle, alg )) return STATUS_INTERNAL_ERROR; if (pgnutls_hash_init( &hash->hash_handle, alg )) return STATUS_INTERNAL_ERROR;
return STATUS_SUCCESS; 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 )
{ {
gnutls_mac_algorithm_t alg; if (pgnutls_hash( hash->hash_handle, input, size )) return STATUS_INTERNAL_ERROR;
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 );
return STATUS_NOT_IMPLEMENTED;
}
if (pgnutls_hmac_init( &hash->u.hmac_handle, alg, key, key_size )) return STATUS_INTERNAL_ERROR;
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
static NTSTATUS hash_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_hash( hash->u.hash_handle, input, size )) return STATUS_INTERNAL_ERROR; pgnutls_hash_deinit( hash->hash_handle, output );
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size )
{
if (pgnutls_hmac( hash->u.hmac_handle, input, size )) return STATUS_INTERNAL_ERROR;
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 #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" ); ERR( "support for hashes not available at build time\n" );
return STATUS_NOT_IMPLEMENTED; 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" ); ERR( "support for hashes not available at build time\n" );
return STATUS_NOT_IMPLEMENTED; 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" ); ERR( "support for hashes not available at build time\n" );
return STATUS_NOT_IMPLEMENTED; 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" ); struct object hdr;
return STATUS_NOT_IMPLEMENTED; enum alg_id alg_id;
} BOOL hmac;
#endif struct hash_impl outer;
struct hash_impl inner;
};
#define OBJECT_LENGTH_MD5 274 #define OBJECT_LENGTH_MD5 274
#define OBJECT_LENGTH_SHA1 278 #define OBJECT_LENGTH_SHA1 278
...@@ -735,8 +624,11 @@ NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDL ...@@ -735,8 +624,11 @@ NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDL
UCHAR *secret, ULONG secretlen, ULONG flags ) UCHAR *secret, ULONG secretlen, ULONG flags )
{ {
struct algorithm *alg = algorithm; struct algorithm *alg = algorithm;
UCHAR buffer[MAX_HASH_BLOCK_BITS / 8] = {0};
struct hash *hash; struct hash *hash;
int block_bytes;
NTSTATUS status; NTSTATUS status;
int i;
TRACE( "%p, %p, %p, %u, %p, %u, %08x - stub\n", algorithm, handle, object, objectlen, TRACE( "%p, %p, %p, %u, %p, %u, %08x - stub\n", algorithm, handle, object, objectlen,
secret, secretlen, flags ); secret, secretlen, flags );
...@@ -754,17 +646,34 @@ NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDL ...@@ -754,17 +646,34 @@ NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDL
hash->alg_id = alg->id; hash->alg_id = alg->id;
hash->hmac = alg->hmac; 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 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) if (status != STATUS_SUCCESS)
{ {
/* FIXME: call hash_finish to release resources */
HeapFree( GetProcessHeap(), 0, hash ); HeapFree( GetProcessHeap(), 0, hash );
return status; return status;
} }
...@@ -793,33 +702,28 @@ NTSTATUS WINAPI BCryptHashData( BCRYPT_HASH_HANDLE handle, UCHAR *input, ULONG s ...@@ -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 (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE;
if (!input) return STATUS_SUCCESS; if (!input) return STATUS_SUCCESS;
if (hash->hmac) return hash_update( &hash->inner, hash->alg_id, input, size );
{
return hmac_update( hash, input, size );
}
else
{
return hash_update( hash, input, size );
}
} }
NTSTATUS WINAPI BCryptFinishHash( BCRYPT_HASH_HANDLE handle, UCHAR *output, ULONG size, ULONG flags ) NTSTATUS WINAPI BCryptFinishHash( BCRYPT_HASH_HANDLE handle, UCHAR *output, ULONG size, ULONG flags )
{ {
UCHAR buffer[MAX_HASH_OUTPUT_BYTES];
struct hash *hash = handle; struct hash *hash = handle;
NTSTATUS status;
int hash_length;
TRACE( "%p, %p, %u, %08x\n", handle, output, size, flags ); TRACE( "%p, %p, %u, %08x\n", handle, output, size, flags );
if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE; if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE;
if (!output) return STATUS_INVALID_PARAMETER; if (!output) return STATUS_INVALID_PARAMETER;
if (hash->hmac) if (!hash->hmac)
{ return hash_finish( &hash->inner, hash->alg_id, output, size );
return hmac_finish( hash, output, size );
} hash_length = alg_props[hash->alg_id].hash_length;
else 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, output, size ); return hash_finish( &hash->outer, hash->alg_id, output, size );
}
} }
NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE algorithm, UCHAR *secret, ULONG secretlen, NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE algorithm, UCHAR *secret, ULONG secretlen,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment