Search code examples
hashstm32sha256hal

SHA256 digest disagrees in Python vs. STM32's built-in HASH device


Problem:

I am trying to compute the SHA256 digests of single blocks (512 bit) on an STM32L552ZE-Q using mbedTLS the HASH device built into the STM32 I am using. Unfortunately, the digest doesn't agree with that of Python's SHA256 implementation, even for a single block of all-zeros.

Sample output:

  1. Python: f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b

  2. STM32, data-type= 1 bit: B20941D6177356919BCDF1F716029D5F53C81932439D59B98F04A5EE0E192A25

  3. STM32, data-type != 1 bit 037D6DFB3A369A41E01100FDD53C35EE3FB69DDEC5830D61E1138D066A4C2285

Above results are very confusing. The STM32 reference manual for my MCU specifies that the HASH device operates on 32 bit words in big-endian and that messages need to account for that by specifying the data type to be 1, 8, 16 or 32 bit. Setting these values causes the bits to be reordered, though in my mind the ordering should NOT have an impact for an all-0 block because any permuation of a sequence of 64 0's is again just 64 0's. I clearly misunderstand something, as changing the data type (i.e. the reordering) causes the hash to change.

Python code:

from cryptography.hazmat.primitives import hashes
digest = hashes.Hash(hashes.SHA256())
by = b"\0" * 64
digest.update(by)
print(digest.finalize().hex())

STM32 code:

#define SHA256_INPUT_SIZE       64                      // 512 bit
#define SHA256_OUTPUT_SIZE      32                      // 256 bit

// (0) Initialize the HASH peripheral
MX_HASH_Init();

// (1) Create input buffer of size one block (512 bit, 64 byte) and initialize it with all 0-bytes
byte_t ibuf[SHA256_INPUT_SIZE];
memset(ibuf, 0, sizeof(ibuf));

// (2) Create output buffer of size 256 bit, 32 byte (setting it to all 1-bytes shouldn't be relevant) 
byte_t obuf[SHA256_OUTPUT_SIZE];
memset(obuf, 1, sizeof(ibuf));

// (3) Compute the digest based on ibuf and store it into obuf
HAL_StatusTypeDef rv = HAL_HASHEx_SHA256_Start(&hhash, ibuf, SHA256_INPUT_SIZE, obuf, 10);
char debug_msg[MAX_PRINT_LEN];

// (4) Print an error message or the hash digest.
if(rv != HAL_OK){
    sprintf(debug_msg, "ERROR: HAL_HASHEx_SHA256_Start failed on line %i\r\n", __LINE__);
}
else{
    char hash_buf[SHA256_OUTPUT_SIZE * 3];                  // For one byte of the output digest, e.g. value 24, we write THREE characters (except for last byte of output, we only write TWO characters).
                                                            // Here, for value 24 we would write "18:" for 24 not the last byte and "18" if it is the last byte.
    memset(hash_buf, 1, sizeof(hash_buf));
    bytes_to_hex_string(obuf, SHA256_OUTPUT_SIZE, hash_buf);
    sprintf(debug_msg, "DEBUG: Hash output: %s\r\n", hash_buf);
}
print_debug(debug_msg);

Questions:

  1. What am I doing wrong, i.e. why is my STM32's hash incorrect (I also computed the digest in golang and it matches that of python)?
  2. How is it possible that changing the data-type affects the hash despite the block being all 0 (and therefore the ordering has no impact)

Visual representation of the reordering according to the "data-type": Reordering as a function of the data-type


Solution

  • Turns out that the problem was caused by me being sloppy and writing memset(obuf, 1, sizeof(ibuf)); instead of memset(obuf, 1, sizeof(obuf));. This caused the memset to overwrite part of the input buffer to contain 256 1-bits. This also explains why the reordering feature of the STM32 caused the output to change...