I'm running code to do HMAC with sha256 using the MMCAU on a K64F board. My results agree with those of a PC program which communicates with it except when the length of bytes in a block hits 56.
At 56 bytes (448 bits) of data, I start doing the padding as required, and hash a second block (so 128 bytes total). When I inspect the two blocks in memory after padding and before hash, everything looks correct:
When I change the limit to 48 bytes, it starts doing the same thing there (i.e. ok with less than 48, breaks at 48). So it seems as soon as I start doing two blocks (and the related padding), things break.
Hopefully somebody can see what stupid mistake I have made. I try not to use code trickery, it all seems straightforward. And obviously, the HashN function works on multiple blocks in general, at least when called without padding, because I successfully use it on hundreds of kB of data, as long as I don't have 56 bytes left at the end.
Thank you.
// UNPACK LITTLE ENDIAN data to BIG ENDIAN string
// takes LITTLE ENDIAN 32-bit data value X, put in BIG ENDIAN string *str
#define UNPACK32(x, str) \
{ \
*((str) + 3) = (uint8_t) ((x) ); \
*((str) + 2) = (uint8_t) ((x) >> 8); \
*((str) + 1) = (uint8_t) ((x) >> 16); \
*((str) + 0) = (uint8_t) ((x) >> 24); \
}
//-----------------------------------------------------------------------
#define SHA256_DIGEST_SIZE ( 256 / 8)
#define SHA256_BLOCK_SIZE ( 512 / 8)
//-----------------------------------------------------------------------
typedef struct {
uint32_t tot_len; // total bytes actually hashed
uint16_t len; // new bytes stored but not yet hashed (block not full!)
uint8_t block[2 * SHA256_BLOCK_SIZE];
uint32_t h[8];
} sha256_ctx;
//-----------------------------------------------------------------------
// void sha256_final(sha256_ctx *ctx, uint8_t *digest)
// finalizes a hash in progress, and returns digest of bytes
void sha256_final(sha256_ctx *ctx, uint8_t *digest)
{
uint16_t block_nb;
uint16_t padded_len, remaining;
uint32_t bitlen;
int16_t i;
// start with one block
block_nb = 1;
// any pending partial-block bytes
remaining = (ctx->len) % SHA256_BLOCK_SIZE;
// if more than 55 bytes already taken from block, must do two blocks
// (or can't fit padding!)
if (remaining > (SHA256_BLOCK_SIZE - 9))
block_nb++; // two blocks
// total length in bits (bytes x 8) to put into padding
// note: this includes both already hashed and pending
bitlen = (ctx->tot_len + ctx->len) << 3;
// total padded length = blocks * 64
padded_len = block_nb << 6;
// pad with zeroes...
// starting after partial-block bytes
// (note length omits any partial-block bytes)
memset(ctx->block + ctx->len, 0, padded_len - ctx->len);
// set that one extra bit at start of padding
ctx->block[ctx->len] = 0x80;
// store TOTAL bit count at end
// (the -4 allows space for the 4-byte bit count to be within the padding)
UNPACK32(bitlen, (ctx->block + padded_len - 4));
// now do the hash of 1 or 2 blocks
MMCAU_SHA256_HashN(ctx->block, (uint32_t)block_nb, ctx->h);
// final result is in h[]
// msb of h[i] goes to first byte
// lsb goes to 4th byte
// i.e. output as big endian
for (i = 0 ; i < 8; i++) {
UNPACK32(ctx->h[i], &digest[i << 2]);
}
}
//-----------------------------------------------------------------------