#include "HMACSHA1.h"
#include <MiscCSPAlgs/SHA1.h>
#include <MiscCSPAlgs/MD5.h>
#include <string.h>
#include <stdlib.h> // for malloc - maybe we should use CssmAllocator?
#include <Security/cssmerr.h>
#pragma mark --- Common digest class ---
typedef struct {
union {
sha1Obj sha1Context; struct MD5Context md5Context;
} dig;
CSSM_BOOL isSha1;
} DigestCtx;
static CSSM_RETURN DigestCtxInit(
DigestCtx *ctx,
CSSM_BOOL isSha1)
{
if(isSha1) {
if(ctx->dig.sha1Context == NULL) {
ctx->dig.sha1Context = sha1Alloc();
if(ctx->dig.sha1Context == NULL) {
return CSSMERR_CSP_MEMORY_ERROR;
}
}
else {
sha1Reinit(ctx->dig.sha1Context);
}
}
else {
MD5Init(&ctx->dig.md5Context);
}
ctx->isSha1 = isSha1;
return CSSM_OK;
}
static void DigestCtxFree(
DigestCtx *ctx)
{
if(ctx->isSha1) {
sha1Free(ctx->dig.sha1Context);
}
memset(ctx, 0, sizeof(DigestCtx));
}
static void DigestCtxUpdate(
DigestCtx *ctx,
const void *textPtr,
UInt32 textLen)
{
if(ctx->isSha1) {
sha1AddData(ctx->dig.sha1Context, (unsigned char *)textPtr, textLen);
}
else {
MD5Update(&ctx->dig.md5Context, (unsigned char *)textPtr, textLen);
}
}
static void DigestCtxFinal(
DigestCtx *ctx,
void *digest)
{
if(ctx->isSha1) {
sha1GetDigest(ctx->dig.sha1Context, (unsigned char *)digest);
}
else {
MD5Final(&ctx->dig.md5Context, (unsigned char *)digest);
}
}
#pragma mark --- HMAC class ---
struct hmacContext {
DigestCtx digest;
UInt8 k_opad[kSHA1BlockSize];
};
hmacContextRef hmacAlloc()
{
hmacContextRef hmac = (hmacContextRef)malloc(sizeof(struct hmacContext));
memset(hmac, 0, sizeof(struct hmacContext));
return hmac;
}
void hmacFree(
hmacContextRef hmac)
{
if(hmac != NULL) {
DigestCtxFree(&hmac->digest);
memset(hmac, 0, sizeof(struct hmacContext));
free(hmac);
}
}
CSSM_RETURN hmacInit(
hmacContextRef hmac,
const void *keyPtr,
UInt32 keyLen,
CSSM_BOOL isSha1) {
UInt8 tk[kSHA1DigestSize];
UInt8 *key;
UInt32 byte;
UInt8 k_ipad[kSHA1BlockSize];
UInt32 digestSize = sha1Digest ? kSHA1DigestSize : MD5_DIGEST_SIZE;
DigestCtxInit(&hmac->digest, isSha1);
if (keyLen <= kSHA1BlockSize)
key = (UInt8*)keyPtr;
else {
DigestCtxUpdate(&hmac->digest, (UInt8*)keyPtr, keyLen);
DigestCtxFinal(&hmac->digest, tk);
key = tk;
keyLen = digestSize;
DigestCtxInit(&hmac->digest, isSha1);
}
for (byte = 0; byte < keyLen; byte++)
{
k_ipad[byte] = key[byte] ^ 0x36;
hmac->k_opad[byte] = key[byte] ^ 0x5c;
}
if (keyLen < kSHA1BlockSize)
{
memset (k_ipad + keyLen, 0x36, kSHA1BlockSize - keyLen);
memset (hmac->k_opad + keyLen, 0x5c, kSHA1BlockSize - keyLen);
}
DigestCtxUpdate(&hmac->digest, k_ipad, kSHA1BlockSize);
return CSSM_OK;
}
CSSM_RETURN hmacUpdate(
hmacContextRef hmac,
const void *textPtr,
UInt32 textLen)
{
DigestCtxUpdate(&hmac->digest, textPtr, textLen);
return CSSM_OK;
}
CSSM_RETURN hmacFinal(
hmacContextRef hmac,
void *resultPtr) {
UInt32 digestSize = hmac->digest.isSha1 ? kSHA1DigestSize : kHMACMD5DigestSize;
DigestCtxFinal(&hmac->digest, resultPtr);
DigestCtxInit(&hmac->digest, hmac->digest.isSha1);
DigestCtxUpdate(&hmac->digest, hmac->k_opad, kSHA1BlockSize);
DigestCtxUpdate(&hmac->digest, resultPtr, digestSize);
DigestCtxFinal(&hmac->digest, resultPtr);
return CSSM_OK;
}
void
hmacsha1 (const void *keyPtr, UInt32 keyLen,
const void *textPtr, UInt32 textLen,
void *resultPtr)
{
hmacContextRef hmac = hmacAlloc();
hmacInit(hmac, keyPtr, keyLen, CSSM_TRUE);
hmacUpdate(hmac, textPtr, textLen);
hmacFinal(hmac, resultPtr);
hmacFree(hmac);
}