446 lines
11 KiB
C
446 lines
11 KiB
C
/*
|
|
*****************************************************************************
|
|
*
|
|
* File: fko_hmac.c
|
|
*
|
|
* Author: Michael Rash
|
|
*
|
|
* Purpose: Provide HMAC support to SPA communications
|
|
*
|
|
* Copyright 2012 Michael Rash (mbr@cipherdyne.org)
|
|
*
|
|
* License (GNU Public License):
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
* USA
|
|
*
|
|
*****************************************************************************
|
|
*/
|
|
|
|
#include "hmac.h"
|
|
|
|
typedef struct {
|
|
MD5Context ctx_inside;
|
|
MD5Context ctx_outside;
|
|
|
|
unsigned char block_inner_pad[MAX_DIGEST_BLOCK_LEN];
|
|
unsigned char block_outer_pad[MAX_DIGEST_BLOCK_LEN];
|
|
} hmac_md5_ctx;
|
|
|
|
typedef struct {
|
|
SHA1_INFO ctx_inside;
|
|
SHA1_INFO ctx_outside;
|
|
|
|
unsigned char block_inner_pad[MAX_DIGEST_BLOCK_LEN];
|
|
unsigned char block_outer_pad[MAX_DIGEST_BLOCK_LEN];
|
|
} hmac_sha1_ctx;
|
|
|
|
typedef struct {
|
|
SHA256_CTX ctx_inside;
|
|
SHA256_CTX ctx_outside;
|
|
|
|
unsigned char block_inner_pad[MAX_DIGEST_BLOCK_LEN];
|
|
unsigned char block_outer_pad[MAX_DIGEST_BLOCK_LEN];
|
|
} hmac_sha256_ctx;
|
|
|
|
typedef struct {
|
|
SHA384_CTX ctx_inside;
|
|
SHA384_CTX ctx_outside;
|
|
|
|
unsigned char block_inner_pad[MAX_DIGEST_BLOCK_LEN];
|
|
unsigned char block_outer_pad[MAX_DIGEST_BLOCK_LEN];
|
|
} hmac_sha384_ctx;
|
|
|
|
typedef struct {
|
|
SHA512_CTX ctx_inside;
|
|
SHA512_CTX ctx_outside;
|
|
|
|
unsigned char block_inner_pad[MAX_DIGEST_BLOCK_LEN];
|
|
unsigned char block_outer_pad[MAX_DIGEST_BLOCK_LEN];
|
|
} hmac_sha512_ctx;
|
|
|
|
static void
|
|
pad_init(unsigned char *inner_pad, unsigned char *outer_pad,
|
|
const unsigned char * const key, const int key_len)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i=0; i < MAX_DIGEST_BLOCK_LEN && i < key_len; i++) {
|
|
inner_pad[i] = key[i] ^ 0x36;
|
|
outer_pad[i] = key[i] ^ 0x5c;
|
|
}
|
|
|
|
if(i < MAX_DIGEST_BLOCK_LEN)
|
|
{
|
|
while(i < MAX_DIGEST_BLOCK_LEN)
|
|
{
|
|
inner_pad[i] = 0x36;
|
|
outer_pad[i] = 0x5c;
|
|
i++;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Begin MD5 HMAC functions
|
|
*/
|
|
static void
|
|
hmac_md5_init(hmac_md5_ctx *ctx, const char *key, const int key_len)
|
|
{
|
|
unsigned char final_key[MAX_DIGEST_BLOCK_LEN] = {0};
|
|
unsigned char init_key[MAX_DIGEST_BLOCK_LEN] = {0};
|
|
int final_len = key_len;
|
|
|
|
memset(final_key, 0x00, MAX_DIGEST_BLOCK_LEN);
|
|
memset(init_key, 0x00, MAX_DIGEST_BLOCK_LEN);
|
|
|
|
if(key_len > MAX_DIGEST_BLOCK_LEN)
|
|
final_len = MAX_DIGEST_BLOCK_LEN;
|
|
|
|
memcpy(init_key, key, final_len);
|
|
|
|
if(MD5_BLOCK_LEN < key_len)
|
|
{
|
|
/* Calculate the digest of the key
|
|
*/
|
|
md5(final_key, init_key, final_len);
|
|
}
|
|
else
|
|
{
|
|
memcpy(final_key, init_key, key_len);
|
|
}
|
|
|
|
pad_init(ctx->block_inner_pad, ctx->block_outer_pad, final_key, final_len);
|
|
|
|
MD5Init(&ctx->ctx_inside);
|
|
MD5Update(&ctx->ctx_inside, ctx->block_inner_pad, MD5_BLOCK_LEN);
|
|
|
|
MD5Init(&ctx->ctx_outside);
|
|
MD5Update(&ctx->ctx_outside, ctx->block_outer_pad, MD5_BLOCK_LEN);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
hmac_md5_update(hmac_md5_ctx *ctx, const char *msg,
|
|
unsigned int msg_len)
|
|
{
|
|
MD5Update(&ctx->ctx_inside, (unsigned char *)msg, msg_len);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
hmac_md5_final(hmac_md5_ctx *ctx, unsigned char *hmac)
|
|
{
|
|
unsigned char digest_inside[MD5_DIGEST_LEN];
|
|
|
|
MD5Final(digest_inside, &ctx->ctx_inside);
|
|
MD5Update(&ctx->ctx_outside, digest_inside, MD5_DIGEST_LEN);
|
|
MD5Final(hmac, &ctx->ctx_outside);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
hmac_md5(const char *msg, const unsigned int msg_len,
|
|
unsigned char *hmac, const char *hmac_key, const int hmac_key_len)
|
|
{
|
|
hmac_md5_ctx ctx;
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
|
|
hmac_md5_init(&ctx, hmac_key, hmac_key_len);
|
|
hmac_md5_update(&ctx, msg, msg_len);
|
|
hmac_md5_final(&ctx, hmac);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Begin SHA1 HMAC functions
|
|
*/
|
|
static void
|
|
hmac_sha1_init(hmac_sha1_ctx *ctx, const char *key, const int key_len)
|
|
{
|
|
unsigned char final_key[MAX_DIGEST_BLOCK_LEN] = {0};
|
|
unsigned char init_key[MAX_DIGEST_BLOCK_LEN] = {0};
|
|
int final_len = key_len;
|
|
|
|
memset(final_key, 0x00, MAX_DIGEST_BLOCK_LEN);
|
|
memset(init_key, 0x00, MAX_DIGEST_BLOCK_LEN);
|
|
|
|
if(key_len > MAX_DIGEST_BLOCK_LEN)
|
|
final_len = MAX_DIGEST_BLOCK_LEN;
|
|
|
|
memcpy(init_key, key, final_len);
|
|
|
|
if(SHA1_BLOCK_LEN < key_len)
|
|
{
|
|
/* Calculate the digest of the key
|
|
*/
|
|
sha1(final_key, init_key, final_len);
|
|
}
|
|
else
|
|
{
|
|
memcpy(final_key, init_key, key_len);
|
|
}
|
|
|
|
pad_init(ctx->block_inner_pad, ctx->block_outer_pad, final_key, final_len);
|
|
|
|
sha1_init(&ctx->ctx_inside);
|
|
sha1_update(&ctx->ctx_inside, ctx->block_inner_pad, SHA1_BLOCK_LEN);
|
|
|
|
sha1_init(&ctx->ctx_outside);
|
|
sha1_update(&ctx->ctx_outside, ctx->block_outer_pad, SHA1_BLOCK_LEN);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
hmac_sha1_update(hmac_sha1_ctx *ctx, const char *msg,
|
|
unsigned int msg_len)
|
|
{
|
|
sha1_update(&ctx->ctx_inside, (unsigned char *)msg, msg_len);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
hmac_sha1_final(hmac_sha1_ctx *ctx, unsigned char *hmac)
|
|
{
|
|
unsigned char digest_inside[SHA1_DIGEST_LEN];
|
|
|
|
sha1_final(digest_inside, &ctx->ctx_inside);
|
|
sha1_update(&ctx->ctx_outside, digest_inside, SHA1_DIGEST_LEN);
|
|
sha1_final(hmac, &ctx->ctx_outside);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
hmac_sha1(const char *msg, const unsigned int msg_len,
|
|
unsigned char *hmac, const char *hmac_key, const int hmac_key_len)
|
|
{
|
|
hmac_sha1_ctx ctx;
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
|
|
hmac_sha1_init(&ctx, hmac_key, hmac_key_len);
|
|
hmac_sha1_update(&ctx, msg, msg_len);
|
|
hmac_sha1_final(&ctx, hmac);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Begin SHA256 HMAC functions
|
|
*/
|
|
static void
|
|
hmac_sha256_init(hmac_sha256_ctx *ctx, const char *key, const int key_len)
|
|
{
|
|
unsigned char final_key[MAX_DIGEST_BLOCK_LEN] = {0};
|
|
unsigned char init_key[MAX_DIGEST_BLOCK_LEN] = {0};
|
|
int final_len = key_len;
|
|
|
|
memset(final_key, 0x00, MAX_DIGEST_BLOCK_LEN);
|
|
memset(init_key, 0x00, MAX_DIGEST_BLOCK_LEN);
|
|
|
|
if(key_len > MAX_DIGEST_BLOCK_LEN)
|
|
final_len = MAX_DIGEST_BLOCK_LEN;
|
|
|
|
memcpy(init_key, key, final_len);
|
|
|
|
if(SHA256_BLOCK_LEN < key_len)
|
|
{
|
|
/* Calculate the digest of the key
|
|
*/
|
|
sha256(final_key, init_key, final_len);
|
|
}
|
|
else
|
|
{
|
|
memcpy(final_key, init_key, key_len);
|
|
}
|
|
|
|
pad_init(ctx->block_inner_pad, ctx->block_outer_pad, final_key, final_len);
|
|
|
|
SHA256_Init(&ctx->ctx_inside);
|
|
SHA256_Update(&ctx->ctx_inside, ctx->block_inner_pad, SHA256_BLOCK_LEN);
|
|
|
|
SHA256_Init(&ctx->ctx_outside);
|
|
SHA256_Update(&ctx->ctx_outside, ctx->block_outer_pad, SHA256_BLOCK_LEN);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
hmac_sha256_update(hmac_sha256_ctx *ctx, const char *msg,
|
|
unsigned int msg_len)
|
|
{
|
|
SHA256_Update(&ctx->ctx_inside, (unsigned char *)msg, msg_len);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
hmac_sha256_final(hmac_sha256_ctx *ctx, unsigned char *hmac)
|
|
{
|
|
unsigned char digest_inside[SHA256_DIGEST_LEN];
|
|
|
|
SHA256_Final(digest_inside, &ctx->ctx_inside);
|
|
SHA256_Update(&ctx->ctx_outside, digest_inside, SHA256_DIGEST_LEN);
|
|
SHA256_Final(hmac, &ctx->ctx_outside);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
hmac_sha256(const char *msg, const unsigned int msg_len,
|
|
unsigned char *hmac, const char *hmac_key, const int hmac_key_len)
|
|
{
|
|
hmac_sha256_ctx ctx;
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
|
|
hmac_sha256_init(&ctx, hmac_key, hmac_key_len);
|
|
hmac_sha256_update(&ctx, msg, msg_len);
|
|
hmac_sha256_final(&ctx, hmac);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Begin SHA384 HMAC functions
|
|
*/
|
|
static void
|
|
hmac_sha384_init(hmac_sha384_ctx *ctx, const char *key, const int key_len)
|
|
{
|
|
unsigned char final_key[MAX_DIGEST_BLOCK_LEN] = {0};
|
|
int final_len = key_len;
|
|
|
|
memset(final_key, 0x00, MAX_DIGEST_BLOCK_LEN);
|
|
|
|
if(key_len > MAX_DIGEST_BLOCK_LEN)
|
|
final_len = MAX_DIGEST_BLOCK_LEN;
|
|
|
|
/* When we eventually support arbitrary key sizes, take the digest
|
|
* of the key with: sha384(final_key, init_key, final_len);
|
|
*/
|
|
memcpy(final_key, key, final_len);
|
|
|
|
pad_init(ctx->block_inner_pad, ctx->block_outer_pad, final_key, final_len);
|
|
|
|
SHA384_Init(&ctx->ctx_inside);
|
|
SHA384_Update(&ctx->ctx_inside, ctx->block_inner_pad, SHA384_BLOCK_LEN);
|
|
|
|
SHA384_Init(&ctx->ctx_outside);
|
|
SHA384_Update(&ctx->ctx_outside, ctx->block_outer_pad, SHA384_BLOCK_LEN);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
hmac_sha384_update(hmac_sha384_ctx *ctx, const char *msg,
|
|
unsigned int msg_len)
|
|
{
|
|
SHA384_Update(&ctx->ctx_inside, (unsigned char *)msg, msg_len);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
hmac_sha384_final(hmac_sha384_ctx *ctx, unsigned char *hmac)
|
|
{
|
|
unsigned char digest_inside[SHA384_DIGEST_LEN];
|
|
|
|
SHA384_Final(digest_inside, &ctx->ctx_inside);
|
|
SHA384_Update(&ctx->ctx_outside, digest_inside, SHA384_DIGEST_LEN);
|
|
SHA384_Final(hmac, &ctx->ctx_outside);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
hmac_sha384(const char *msg, const unsigned int msg_len,
|
|
unsigned char *hmac, const char *hmac_key, const int hmac_key_len)
|
|
{
|
|
hmac_sha384_ctx ctx;
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
|
|
hmac_sha384_init(&ctx, hmac_key, hmac_key_len);
|
|
hmac_sha384_update(&ctx, msg, msg_len);
|
|
hmac_sha384_final(&ctx, hmac);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Begin SHA512 HMAC functions
|
|
*/
|
|
static void
|
|
hmac_sha512_init(hmac_sha512_ctx *ctx, const char *key, const int key_len)
|
|
{
|
|
unsigned char final_key[MAX_DIGEST_BLOCK_LEN] = {0};
|
|
int final_len = key_len;
|
|
|
|
memset(final_key, 0x00, MAX_DIGEST_BLOCK_LEN);
|
|
|
|
if(key_len > MAX_DIGEST_BLOCK_LEN)
|
|
final_len = MAX_DIGEST_BLOCK_LEN;
|
|
|
|
/* When we eventually support arbitrary key sizes, take the digest
|
|
* of the key with: sha512(final_key, init_key, final_len);
|
|
*/
|
|
memcpy(final_key, key, final_len);
|
|
|
|
pad_init(ctx->block_inner_pad, ctx->block_outer_pad, final_key, final_len);
|
|
|
|
SHA512_Init(&ctx->ctx_inside);
|
|
SHA512_Update(&ctx->ctx_inside, ctx->block_inner_pad, SHA512_BLOCK_LEN);
|
|
|
|
SHA512_Init(&ctx->ctx_outside);
|
|
SHA512_Update(&ctx->ctx_outside, ctx->block_outer_pad, SHA512_BLOCK_LEN);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
hmac_sha512_update(hmac_sha512_ctx *ctx, const char *msg,
|
|
unsigned int msg_len)
|
|
{
|
|
SHA512_Update(&ctx->ctx_inside, (unsigned char *)msg, msg_len);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
hmac_sha512_final(hmac_sha512_ctx *ctx, unsigned char *hmac)
|
|
{
|
|
unsigned char digest_inside[SHA512_DIGEST_LEN];
|
|
|
|
SHA512_Final(digest_inside, &ctx->ctx_inside);
|
|
SHA512_Update(&ctx->ctx_outside, digest_inside, SHA512_DIGEST_LEN);
|
|
SHA512_Final(hmac, &ctx->ctx_outside);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
hmac_sha512(const char *msg, const unsigned int msg_len,
|
|
unsigned char *hmac, const char *hmac_key, const int hmac_key_len)
|
|
{
|
|
hmac_sha512_ctx ctx;
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
|
|
hmac_sha512_init(&ctx, hmac_key, hmac_key_len);
|
|
hmac_sha512_update(&ctx, msg, msg_len);
|
|
hmac_sha512_final(&ctx, hmac);
|
|
|
|
return;
|
|
}
|