0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-01-23 09:21:53 +00:00

Add HMAC-SHA256 hash library and unit tests

This commit is contained in:
Pavel Tvrdík 2015-04-25 10:49:43 +02:00
parent 26b1fd1dc4
commit c23cd84ed5
4 changed files with 530 additions and 1 deletions

313
lib/sha256_hmac.c Normal file
View File

@ -0,0 +1,313 @@
/*
* BIRD -- HMAC-SHA256 Message Authentication
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Based on the code from libgcrypt-1.6.0, which is
* (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "sysdep/config.h"
#include "lib/null.h"
#include "lib/unaligned.h"
#include "lib/sha256_hmac.h"
/* Bitwise rotation of an unsigned int to the right */
static inline u32 ror(u32 x, int n)
{
return ( (x >> (n&(32-1))) | (x << ((32-n)&(32-1))) );
}
#define my_wipememory2(_ptr,_set,_len) do { \
volatile char *_vptr=(volatile char *)(_ptr); \
size_t _vlen=(_len); \
while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \
} while(0)
#define my_wipememory(_ptr,_len) my_wipememory2(_ptr,0,_len)
/*
The SHA-256 core: Transform the message X which consists of 16
32-bit-words. See FIPS 180-2 for details.
*/
static void
transform(sha256_hmac_context *hd, const void *data_arg)
{
const unsigned char *data = data_arg;
#define Cho(x,y,z) (z ^ (x & (y ^ z))) /* (4.2) same as SHA-1's F1 */
#define Maj(x,y,z) ((x & y) | (z & (x|y))) /* (4.3) same as SHA-1's F3 */
#define Sum0(x) (ror((x), 2) ^ ror((x), 13) ^ ror((x), 22)) /* (4.4) */
#define Sum1(x) (ror((x), 6) ^ ror((x), 11) ^ ror((x), 25)) /* (4.5) */
#define S0(x) (ror((x), 7) ^ ror((x), 18) ^ ((x) >> 3)) /* (4.6) */
#define S1(x) (ror((x), 17) ^ ror((x), 19) ^ ((x) >> 10)) /* (4.7) */
#define R(a,b,c,d,e,f,g,h,k,w) do \
{ \
t1 = (h) + Sum1((e)) + Cho((e),(f),(g)) + (k) + (w); \
t2 = Sum0((a)) + Maj((a),(b),(c)); \
h = g; \
g = f; \
f = e; \
e = d + t1; \
d = c; \
c = b; \
b = a; \
a = t1 + t2; \
} while(0)
static const u32 K[64] =
{
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
u32 a, b, c, d, e, f, g, h, t1, t2;
u32 x[16];
u32 w[64];
int i;
a = hd->h0;
b = hd->h1;
c = hd->h2;
d = hd->h3;
e = hd->h4;
f = hd->h5;
g = hd->h6;
h = hd->h7;
#ifdef WORDS_BIGENDIAN
memcpy(x, data, 64);
#else /*!WORDS_BIGENDIAN*/
{
unsigned char *p2;
for(i=0, p2=(unsigned char*)x; i < 16; i++, p2 += 4 )
{
p2[3] = *data++;
p2[2] = *data++;
p2[1] = *data++;
p2[0] = *data++;
}
}
#endif /*!WORDS_BIGENDIAN*/
for(i=0; i < 16; i++)
w[i] = x[i];
for(; i < 64; i++)
w[i] = S1(w[i-2]) + w[i-7] + S0(w[i-15]) + w[i-16];
for(i=0; i < 64; i++)
R(a,b,c,d,e,f,g,h,K[i],w[i]);
hd->h0 += a;
hd->h1 += b;
hd->h2 += c;
hd->h3 += d;
hd->h4 += e;
hd->h5 += f;
hd->h6 += g;
hd->h7 += h;
}
#undef Cho
#undef Maj
#undef Sum0
#undef Sum1
#undef S0
#undef S1
#undef R
/* Finalize the current SHA256 calculation. */
static void
finalize(sha256_hmac_context *hd)
{
u32 t, msb, lsb;
unsigned char *p;
if (hd->finalized)
return; /* Silently ignore a finalized context. */
sha256_hmac_update(hd, NULL, 0); /* Flush. */
t = hd->nblocks;
/* Multiply by 64 to make a byte count. */
lsb = t << 6;
msb = t >> 26;
/* Add the count. */
t = lsb;
if ((lsb += hd->count) < t)
msb++;
/* Multiply by 8 to make a bit count. */
t = lsb;
lsb <<= 3;
msb <<= 3;
msb |= t >> 29;
if (hd->count < 56)
{ /* Enough room. */
hd->buf[hd->count++] = 0x80; /* pad */
while(hd->count < 56)
hd->buf[hd->count++] = 0; /* pad */
}
else
{ /* Need one extra block. */
hd->buf[hd->count++] = 0x80; /* pad character */
while(hd->count < 64)
hd->buf[hd->count++] = 0;
sha256_hmac_update(hd, NULL, 0); /* Flush. */;
memset(hd->buf, 0, 56 ); /* Zero out next next block. */
}
/* Append the 64 bit count. */
hd->buf[56] = msb >> 24;
hd->buf[57] = msb >> 16;
hd->buf[58] = msb >> 8;
hd->buf[59] = msb;
hd->buf[60] = lsb >> 24;
hd->buf[61] = lsb >> 16;
hd->buf[62] = lsb >> 8;
hd->buf[63] = lsb;
transform(hd, hd->buf);
/* Store the digest into hd->buf. */
p = hd->buf;
#define X(a) do { *p++ = hd->h##a >> 24; *p++ = hd->h##a >> 16; \
*p++ = hd->h##a >> 8; *p++ = hd->h##a; } while(0)
X(0);
X(1);
X(2);
X(3);
X(4);
X(5);
X(6);
X(7);
#undef X
hd->finalized = 1;
}
/* Create a new context. On error NULL is returned and errno is set
appropriately. If KEY is given the function computes HMAC using
this key; with KEY given as NULL, a plain SHA-256 digest is
computed. */
void
sha256_hmac_init(sha256_hmac_context *ctx, const void *key, size_t keylen)
{
ctx->h0 = 0x6a09e667;
ctx->h1 = 0xbb67ae85;
ctx->h2 = 0x3c6ef372;
ctx->h3 = 0xa54ff53a;
ctx->h4 = 0x510e527f;
ctx->h5 = 0x9b05688c;
ctx->h6 = 0x1f83d9ab;
ctx->h7 = 0x5be0cd19;
ctx->nblocks = 0;
ctx->count = 0;
ctx->finalized = 0;
ctx->use_hmac = 0;
if (key)
{
int i;
unsigned char ipad[64];
memset(ipad, 0, 64);
memset(ctx->opad, 0, 64);
if (keylen <= 64)
{
memcpy(ipad, key, keylen);
memcpy(ctx->opad, key, keylen);
}
else
{
sha256_hmac_context tmp_ctx;
sha256_hmac_init(&tmp_ctx, NULL, 0);
sha256_hmac_update(&tmp_ctx, key, keylen);
finalize(&tmp_ctx);
memcpy(ipad, tmp_ctx.buf, 32);
memcpy(ctx->opad, tmp_ctx.buf, 32);
}
for(i=0; i < 64; i++)
{
ipad[i] ^= 0x36;
ctx->opad[i] ^= 0x5c;
}
ctx->use_hmac = 1;
sha256_hmac_update(ctx, ipad, 64);
my_wipememory(ipad, 64);
}
}
/* Update the message digest with the contents of BUFFER containing
LENGTH bytes. */
void
sha256_hmac_update(sha256_hmac_context *ctx, const void *buffer, size_t length)
{
const unsigned char *inbuf = buffer;
if (ctx->finalized)
return; /* Silently ignore a finalized context. */
if (ctx->count == 64)
{
/* Flush the buffer. */
transform(ctx, ctx->buf);
ctx->count = 0;
ctx->nblocks++;
}
if (!inbuf)
return; /* Only flushing was requested. */
if (ctx->count)
{
for(; length && ctx->count < 64; length--)
ctx->buf[ctx->count++] = *inbuf++;
sha256_hmac_update(ctx, NULL, 0); /* Flush. */
if (!length)
return;
}
while(length >= 64)
{
transform(ctx, inbuf);
ctx->count = 0;
ctx->nblocks++;
length -= 64;
inbuf += 64;
}
for(; length && ctx->count < 64; length--)
ctx->buf[ctx->count++] = *inbuf++;
}
/* Finalize an operation and return the digest. If R_DLEN is not NULL
the length of the digest will be stored at that address. The
returned value is valid as long as the context exists. On error
NULL is returned. */
const byte *
sha256_hmac_final(sha256_hmac_context *ctx)
{
finalize(ctx);
if (ctx->use_hmac)
{
sha256_hmac_context tmp_ctx;
sha256_hmac_init(&tmp_ctx, NULL, 0);
sha256_hmac_update(&tmp_ctx, ctx->opad, 64);
sha256_hmac_update(&tmp_ctx, ctx->buf, 32);
finalize(&tmp_ctx);
memcpy(ctx->buf, tmp_ctx.buf, 32);
}
return ctx->buf;
}

37
lib/sha256_hmac.h Normal file
View File

@ -0,0 +1,37 @@
/*
* BIRD -- HMAC-SHA256 Message Authentication
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Based on the code from libgcrypt-1.6.0, which is
* (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef SHA256_HMAC_H
#define SHA256_HMAC_H
#define SHA256_SIZE 32
#define SHA256_HEX_SIZE 65
#define SHA224_SIZE 28
#define SHA224_HEX_SIZE 57
/* The context used by this module. */
typedef struct
{
u32 h0, h1, h2, h3, h4, h5, h6, h7;
u32 nblocks;
int count;
int finalized:1;
int use_hmac:1;
byte buf[64];
byte opad[64];
} sha256_hmac_context;
void sha256_hmac_init(sha256_hmac_context *ctx, const void *key, size_t keylen);
void sha256_hmac_update(sha256_hmac_context *ctx, const void *buf, size_t buflen);
const byte *sha256_hmac_final(sha256_hmac_context *ctx);
#endif /* SHA256_HMAC_H */

179
lib/sha256_hmac_test.c Normal file
View File

@ -0,0 +1,179 @@
/*
* BIRD Library -- HMAC-SHA256 Message Authentication Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdlib.h>
#include "test/birdtest.h"
#include "sysdep/config.h"
#include "lib/sha256_hmac.h"
#include "lib/sha256_hmac.c" /* REMOVE ME */
#define HMAC_BUFFER_SIZE 160
struct hmac_data_in {
byte key[HMAC_BUFFER_SIZE];
uint key_len;
byte data[HMAC_BUFFER_SIZE];
uint data_len;
};
static void
get_sha256_hmac(const struct hmac_data_in in, char (*out_hash)[SHA256_HEX_SIZE])
{
sha256_hmac_context ctx;
sha256_hmac_init(&ctx, in.key, in.key_len);
sha256_hmac_update(&ctx, in.data, in.data_len);
byte *hash_byte = sha256_hmac_final(&ctx);
int i;
for (i = 0; i < SHA256_SIZE; i++)
sprintf(*out_hash + i*2, "%02x", hash_byte[i]);
}
static int
t_sha256_hmac(void)
{
struct in_out {
struct hmac_data_in in;
char out[SHA256_HEX_SIZE];
} in_out[] = {
{
.in = {
.key = {
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
},
.key_len = 20,
.data = "Hi There",
.data_len = 8,
},
.out = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
},
{
.in = {
.key = "Jefe",
.key_len = 4,
.data = "what do ya want for nothing?",
.data_len = 28,
},
.out = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
},
{
.in = {
.key = {
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
},
.key_len = 20,
.data = {
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
},
.data_len = 50,
},
.out = "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
},
{
.in = {
.key = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19,
},
.key_len = 25,
.data = {
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
},
.data_len = 50,
},
.out = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
},
{
.in = {
.key = {
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
},
.key_len = 20,
.data = "Test With Truncation",
.data_len = 20,
},
.out = "a3b6167473100ee06e0c796c2955552bfa6f7c0a6a8aef8b93f860aab0cd20c5",
},
{
.in = {
.key = {
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa,
},
.key_len = 131,
.data = "Test Using Larger Than Block-Size Key - Hash Key First",
.data_len = 54,
},
.out = "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
},
{
.in = {
.key = {
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa,
},
.key_len = 131,
.data = "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
.data_len = 152,
},
.out = "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2",
},
};
bt_assert_fn_in_out(get_sha256_hmac, in_out, NULL, "'%s'");
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_sha256_hmac, "Test Suite by RFC 4231");
// bt_test_suite(t_sha224_hmac, "Test Suite by RFC 4231");
return bt_end();
}

View File

@ -38,7 +38,7 @@ void bt_result(const char *result, const char *msg, ...);
#define BT_RANDOM_SEED 982451653
#define BT_BUFFER_SIZE 1000
#define BT_BUFFER_SIZE 10000
#define BT_PROMPT_GREEN "\e[1;32m"
#define BT_PROMPT_RED "\e[1;31m"