Skip to content

Instantly share code, notes, and snippets.

@baggers-br
Created November 13, 2023 22:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baggers-br/48bba51a9e0b1c5a0632c57537f84534 to your computer and use it in GitHub Desktop.
Save baggers-br/48bba51a9e0b1c5a0632c57537f84534 to your computer and use it in GitHub Desktop.
md4.cs
using System;
using Unity.Mathematics;
// I based this on the code from https://www.robinryf.com/blog/2017/10/30/unity-behaviour-in-dlls.html but
// with a rewrite to avoid heap allocations
public static unsafe class MD4
{
ref struct Context
{
public uint4 Wip;
public Span<uint> Block;
public int BytesProcessed;
}
public static uint4 Hash(ReadOnlySpan<byte> data)
{
var context = new Context()
{
Wip = new uint4()
{
x = 0x67452301,
y = 0xefcdab89,
z = 0x98badcfe,
w = 0x10325476,
},
Block = stackalloc uint[16],
BytesProcessed = 0,
};
var len = data.Length;
for (var i = 0; i < len; i++)
{
HashByte(ref context, data[i]);
}
Pad(ref context);
return context.Wip;
}
static void Pad(ref Context context)
{
var _bytesProcessed = context.BytesProcessed;
var padLen = ((_bytesProcessed + 8) & 0x7fffffc0) + 55 - _bytesProcessed;
HashByte(ref context, 128);
for (var i = 0; i < padLen; i++)
{
HashByte(ref context, 0);
}
HashUInt(ref context, ((uint) _bytesProcessed << 3));
HashUInt(ref context, 0);
}
static void HashUInt(ref Context context, uint val)
{
HashByte(ref context, (byte) (val & 255));
HashByte(ref context, (byte) ((val >> 8) & 255));
HashByte(ref context, (byte) ((val >> 16) & 255));
HashByte(ref context, (byte) ((val >> 24) & 255));
}
static void HashByte(ref Context context, byte val)
{
var c = context.BytesProcessed & 63;
var i = c >> 2;
var s = (c & 3) << 3;
context.Block[i] = (context.Block[i] & ~((uint) 255 << s)) | ((uint) val << s);
if (c == 63)
{
Process16WordBlock(ref context);
}
context.BytesProcessed++;
}
static void Process16WordBlock(ref Context context)
{
var wip = context.Wip;
var x = context.Block;
{
wip.x = Round1Operation(wip, x[0], 3);
wip.w = Round1Operation(wip.wxyz, x[0 + 1], 7);
wip.z = Round1Operation(wip.zwxy, x[0 + 2], 11);
wip.y = Round1Operation(wip.yzwx, x[0 + 3], 19);
wip.x = Round1Operation(wip, x[4], 3);
wip.w = Round1Operation(wip.wxyz, x[4 + 1], 7);
wip.z = Round1Operation(wip.zwxy, x[4 + 2], 11);
wip.y = Round1Operation(wip.yzwx, x[4 + 3], 19);
wip.x = Round1Operation(wip, x[8], 3);
wip.w = Round1Operation(wip.wxyz, x[8 + 1], 7);
wip.z = Round1Operation(wip.zwxy, x[8 + 2], 11);
wip.y = Round1Operation(wip.yzwx, x[8 + 3], 19);
wip.x = Round1Operation(wip, x[12], 3);
wip.w = Round1Operation(wip.wxyz, x[12 + 1], 7);
wip.z = Round1Operation(wip.zwxy, x[12 + 2], 11);
wip.y = Round1Operation(wip.yzwx, x[12 + 3], 19);
}
{
wip.x = Round2Operation(wip, x[0], 3);
wip.w = Round2Operation(wip.wxyz, x[0 + 4], 5);
wip.z = Round2Operation(wip.zwxy, x[0 + 8], 9);
wip.y = Round2Operation(wip.yzwx, x[0 + 12], 13);
wip.x = Round2Operation(wip, x[1], 3);
wip.w = Round2Operation(wip.wxyz, x[1 + 4], 5);
wip.z = Round2Operation(wip.zwxy, x[1 + 8], 9);
wip.y = Round2Operation(wip.yzwx, x[1 + 12], 13);
wip.x = Round2Operation(wip, x[2], 3);
wip.w = Round2Operation(wip.wxyz, x[2 + 4], 5);
wip.z = Round2Operation(wip.zwxy, x[2 + 8], 9);
wip.y = Round2Operation(wip.yzwx, x[2 + 12], 13);
wip.x = Round2Operation(wip, x[3], 3);
wip.w = Round2Operation(wip.wxyz, x[3 + 4], 5);
wip.z = Round2Operation(wip.zwxy, x[3 + 8], 9);
wip.y = Round2Operation(wip.yzwx, x[3 + 12], 13);
}
{
wip.x = Round3Operation(wip, x[0], 3);
wip.w = Round3Operation(wip.wxyz, x[0 + 8], 9);
wip.z = Round3Operation(wip.zwxy, x[0 + 4], 11);
wip.y = Round3Operation(wip.yzwx, x[0 + 12], 15);
wip.x = Round3Operation(wip, x[2], 3);
wip.w = Round3Operation(wip.wxyz, x[2 + 8], 9);
wip.z = Round3Operation(wip.zwxy, x[2 + 4], 11);
wip.y = Round3Operation(wip.yzwx, x[2 + 12], 15);
wip.x = Round3Operation(wip, x[1], 3);
wip.w = Round3Operation(wip.wxyz, x[1 + 8], 9);
wip.z = Round3Operation(wip.zwxy, x[1 + 4], 11);
wip.y = Round3Operation(wip.yzwx, x[1 + 12], 15);
wip.x = Round3Operation(wip, x[3], 3);
wip.w = Round3Operation(wip.wxyz, x[3 + 8], 9);
wip.z = Round3Operation(wip.zwxy, x[3 + 4], 11);
wip.y = Round3Operation(wip.yzwx, x[3 + 12], 15);
}
context.Wip += wip;
}
static uint ROL(uint value, int numberOfBits)
{
return (value << numberOfBits) | (value >> (32 - numberOfBits));
}
static uint Round1Operation(uint4 v, uint xk, int s)
{
unchecked
{
return ROL(v.x + ((v.y & v.z) | (~v.y & v.w)) + xk, s);
}
}
static uint Round2Operation(uint4 v, uint xk, int s)
{
unchecked
{
return ROL(v.x + ((v.y & v.z) | (v.y & v.w) | (v.z & v.w)) + xk + 0x5a827999, s);
}
}
static uint Round3Operation(uint4 v, uint xk, int s)
{
unchecked
{
return ROL(v.x + (v.y ^ v.z ^ v.w) + xk + 0x6ed9eba1, s);
}
}
}
@baggers-br
Copy link
Author

uint4 is a unity.mathematics type, but you could make your own fairly easily.

@HolloFox
Copy link

NICE 🤤

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment