AnsweredAssumed Answered

sha256 wrong with 56+ byte blocks using MMCAU in K64

Question asked by E Engineer on May 4, 2017

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:

  • single 1 bit after the last data byte (0x80 byte)
  • zero bytes (0x00) from there until the last 4 bytes (I don't use the upper 4 bytes of the length, not needed)
  • last 4 bytes (of 128) hold big endian count of total data BITS for ALL bytes hashed (not including the added 1 bit).

 

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]);
    }
}

//-----------------------------------------------------------------------

Outcomes