Skip to content

Instantly share code, notes, and snippets.

@ddjerqq
Created June 30, 2024 12:53
Show Gist options
  • Save ddjerqq/c292a6d45b4e803d4b49aa0aaf458c72 to your computer and use it in GitHub Desktop.
Save ddjerqq/c292a6d45b4e803d4b49aa0aaf458c72 to your computer and use it in GitHub Desktop.
Implementation of sha256 in C#, with unsafe code blocks
using System.Diagnostics;
using System.Text;
unsafe
{
Sha256 sha256 = new Sha256();
var payload = "aaa";
byte[] data = Encoding.UTF8.GetBytes(payload);
fixed (byte* p = data)
sha256.Update(p, payload.Length);
var output = sha256.Digest();
// convert to array with 64 len
byte[] hash = new byte[32];
for (int i = 0; i < 32; i++)
{
hash[i] = output[i];
}
var hexDigest = BitConverter.ToString(hash).Replace("-", "");
Console.WriteLine(hexDigest);
Debug.Assert(hexDigest == "9834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0");
}
public unsafe struct Sha256
{
private static readonly uint[] 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,
];
private fixed byte _data[64];
private fixed uint _state[8];
private uint _blocklen = 0;
private ulong _bitlen = 0;
public Sha256()
{
_state[0] = 0x6a09e667;
_state[1] = 0xbb67ae85;
_state[2] = 0x3c6ef372;
_state[3] = 0xa54ff53a;
_state[4] = 0x510e527f;
_state[5] = 0x9b05688c;
_state[6] = 0x1f83d9ab;
_state[7] = 0x5be0cd19;
}
public void Update(byte* data, long size)
{
for (long i = 0; i < size; i++)
{
_data[_blocklen++] = data[i];
if (_blocklen == 64)
{
Transform();
// end of the block
_bitlen += 512;
_blocklen = 0;
}
}
}
public byte* Digest()
{
byte* hash = stackalloc byte[32];
Pad();
Revert(hash);
return hash;
}
private void Pad()
{
ulong i = _blocklen;
byte end = _blocklen < 56 ? (byte)56 : (byte)64;
_data[i++] = 0x80; // Append a bit 1
while (i < end)
{
_data[i++] = 0x00; // Pad with zeros
}
if (_blocklen >= 56)
{
Transform();
for (var j = 0; j < 56; j++)
_data[j] = 0;
}
// Append to the padding the total message's length in bits and transform.
_bitlen += _blocklen * 8;
_data[63] = (byte)(_bitlen >> 0);
_data[62] = (byte)(_bitlen >> 8);
_data[61] = (byte)(_bitlen >> 16);
_data[60] = (byte)(_bitlen >> 24);
_data[59] = (byte)(_bitlen >> 32);
_data[58] = (byte)(_bitlen >> 40);
_data[57] = (byte)(_bitlen >> 48);
_data[56] = (byte)(_bitlen >> 56);
Transform();
}
private void Transform()
{
uint maj, xorA, ch, xorE, sum, newA, newE;
uint[] m = new uint[64];
uint* state = stackalloc uint[8];
// Split data in 32 bit blocks for the 16 first words
for (byte i = 0, j = 0; i < 16; i++, j += 4)
m[i] = (uint)((_data[j] << 24) | (_data[j + 1] << 16) | (_data[j + 2] << 8) | (_data[j + 3]));
// Remaining 48 blocks
for (byte k = 16; k < 64; k++)
m[k] = Sig1(m[k - 2]) + m[k - 7] + Sig0(m[k - 15]) + m[k - 16];
for (byte i = 0; i < 8; i++)
state[i] = _state[i];
for (byte i = 0; i < 64; i++)
{
maj = Majority(state[0], state[1], state[2]);
xorA = Rotr(state[0], 2) ^ Rotr(state[0], 13) ^ Rotr(state[0], 22);
ch = Choose(state[4], state[5], state[6]);
xorE = Rotr(state[4], 6) ^ Rotr(state[4], 11) ^ Rotr(state[4], 25);
sum = m[i] + K[i] + state[7] + ch + xorE;
newA = xorA + maj + sum;
newE = state[3] + sum;
state[7] = state[6];
state[6] = state[5];
state[5] = state[4];
state[4] = newE;
state[3] = state[2];
state[2] = state[1];
state[1] = state[0];
state[0] = newA;
}
for (byte i = 0; i < 8; i++)
{
_state[i] += state[i];
}
}
private void Revert(byte* hash)
{
// SHA uses big endian byte ordering
// Revert all bytes
for (byte i = 0; i < 4; i++)
{
for (byte j = 0; j < 8; j++)
{
hash[i + j * 4] = (byte)((_state[j] >> (24 - i * 8)) & 0x000000ff);
}
}
}
private static uint Rotr(uint x, int n) => (x >> n) | (x << (32 - n));
private static uint Choose(uint e, uint f, uint g) => (e & f) ^ (~e & g);
private static uint Majority(uint a, uint b, uint c) => (a & (b | c)) | (b & c);
private static uint Sig0(uint x) => Rotr(x, 7) ^ Rotr(x, 18) ^ (x >> 3);
private static uint Sig1(uint x) => Rotr(x, 17) ^ Rotr(x, 19) ^ (x >> 10);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment