SHA 256


Reading time: about 6 minutes

SHA-256 is one of the most commonly used cryptographic hash functions.

Useful links

  • https://www.youtube.com/watch?v=f9EbD6iY9zI
  • https://sha256algorithm.com/

Typescript implementation

I wrote this for a Nostr client back in December 2022.

// This file implements the SHA-256 hash function.

// See https://en.wikipedia.org/wiki/SHA-2#Pseudocode

// Everything is done in big-endian.

const K = [
  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,
];

function u32(x: number): number {
  return (x & 0xffffffff) >>> 0;
}

function rotate_right(x: number, n: number): number {
  return u32((x >>> n) | (x << (32 - n)));
}

function pad_to_512_bits(message: Uint8Array): Uint8Array {
  const len = BigInt(message.length);
  const bits = BigInt(len * 8n);
  const k = 512n - ((bits + 1n + 64n) % 512n);
  const padded = new Uint8Array(Number(len + 1n + k / 8n + 8n));
  padded.set(message);
  padded[Number(len)] = 0x80;
  padded[padded.length - 8] = Number(bits >> 56n);
  padded[padded.length - 7] = Number(bits >> 48n);
  padded[padded.length - 6] = Number(bits >> 40n);
  padded[padded.length - 5] = Number(bits >> 32n);
  padded[padded.length - 4] = Number(bits >> 24n);
  padded[padded.length - 3] = Number(bits >> 16n);
  padded[padded.length - 2] = Number(bits >> 8n);
  padded[padded.length - 1] = Number(bits);
  assert(padded.length % 64 === 0);
  return padded;
}

function sha256(message: Uint8Array): Uint8Array {
  const padded = pad_to_512_bits(message);

  let h0 = 0x6a09e667;
  let h1 = 0xbb67ae85;
  let h2 = 0x3c6ef372;
  let h3 = 0xa54ff53a;
  let h4 = 0x510e527f;
  let h5 = 0x9b05688c;
  let h6 = 0x1f83d9ab;
  let h7 = 0x5be0cd19;

  for (let chunk_start = 0; chunk_start < padded.length; chunk_start += 64) {
    const chunk = padded.slice(chunk_start, chunk_start + 64);
    const data = new DataView(chunk.buffer);

    const w = new Array<number>(64);
    for (let i = 0; i < 16; i++) {
      w[i] = data.getUint32(i * 4, false);
    }

    for (let i = 16; i < 64; i++) {
      const s0 =
        rotate_right(w[i - 15], 7) ^
        rotate_right(w[i - 15], 18) ^
        (w[i - 15] >>> 3);
      const s1 =
        rotate_right(w[i - 2], 17) ^
        rotate_right(w[i - 2], 19) ^
        (w[i - 2] >>> 10);
      w[i] = u32(w[i - 16] + s0 + w[i - 7] + s1);
    }

    // Working variables
    let a = h0;
    let b = h1;
    let c = h2;
    let d = h3;
    let e = h4;
    let f = h5;
    let g = h6;
    let h = h7;

    // Compression function main loop
    for (let i = 0; i < 64; i++) {
      const S1 = rotate_right(e, 6) ^ rotate_right(e, 11) ^ rotate_right(e, 25);
      const ch = (e & f) ^ (~e & g);
      const temp1 = u32(h + S1 + ch + K[i] + w[i]);
      const S0 = rotate_right(a, 2) ^ rotate_right(a, 13) ^ rotate_right(a, 22);
      const maj = (a & b) ^ (a & c) ^ (b & c);
      const temp2 = u32(S0 + maj);

      h = g;
      g = f;
      f = e;
      e = u32(d + temp1);
      d = c;
      c = b;
      b = a;
      a = u32(temp1 + temp2);
    }

    // Add the compressed chunk to the current hash value
    h0 = u32(h0 + a);
    h1 = u32(h1 + b);
    h2 = u32(h2 + c);
    h3 = u32(h3 + d);
    h4 = u32(h4 + e);
    h5 = u32(h5 + f);
    h6 = u32(h6 + g);
    h7 = u32(h7 + h);
  }

  // Produce the final hash value (big-endian)
  const hash = new Uint8Array(32);
  const hash_data = new DataView(hash.buffer);
  hash_data.setUint32(0, h0, false);
  hash_data.setUint32(4, h1, false);
  hash_data.setUint32(8, h2, false);
  hash_data.setUint32(12, h3, false);
  hash_data.setUint32(16, h4, false);
  hash_data.setUint32(20, h5, false);
  hash_data.setUint32(24, h6, false);
  hash_data.setUint32(28, h7, false);
  return hash;
}

export default sha256;

Citation

If you find this work useful, please cite it as:
@article{yaltirakli,
  title   = "SHA 256",
  author  = "Yaltirakli, Gokberk",
  journal = "gkbrk.com",
  year    = "2024",
  url     = "https://www.gkbrk.com/sha-256"
}
Not using BibTeX? Click here for more citation styles.
IEEE Citation
Gokberk Yaltirakli, "SHA 256", November, 2024. [Online]. Available: https://www.gkbrk.com/sha-256. [Accessed Nov. 12, 2024].
APA Style
Yaltirakli, G. (2024, November 12). SHA 256. https://www.gkbrk.com/sha-256
Bluebook Style
Gokberk Yaltirakli, SHA 256, GKBRK.COM (Nov. 12, 2024), https://www.gkbrk.com/sha-256

Comments

© 2024 Gokberk Yaltirakli