Understanding the SHA256 Motoko Implementation

Posted Jan. 5 2022 by Tarek.

This is the source code being analyzed. The block size is 64 bytes and the number of rounds is 64. These are not explicitly defined but used inline.

1. Initialization

Line 19 defines the 64 round constants of size 32-bits each.

private let K : [Nat32] = [
  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
  0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
  ...
];

Line 38 initialized the 8 initial hash values of 32-bits each.

private let S : [Nat32] = [
  0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
  0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];

2. Processing

The processing phase is a collaboration between the functions write and block.

The block at Line 71 checks if there is already buffered data and appends incoming bytes to it at most until it fills BLOCK_SIZE.

let n = Nat.min(p.size(), 64 - nx);
for (i in Iter.range(0, n - 1)) {
  x[nx + i] := p[i];
};

If the buffer size reaches BLOCK_SIZE send to block where the hash values get updated:

if (nx == 64) {
  let buf = Array.freeze<Nat8>(x);
  block(buf);
  nx := 0;
};

If the remaining data has enough bytes to fill a BLOCK_SIZE, it will be processed by block up to its last chunk if it's not yet of size BLOCK_SIZE.

3. Sum

The function sum handles padding and outputting the final hash.