// Initialize the hashing context "ctx" with optional key "key". // 1 <= outlen <= 32 gives the digest size in bytes. // Secret key (also <= 32 bytes) is optional (keylen = 0). unsafe public static void init(ref Blake2Context ctx, uint outlen, void *key, uint keylen) // (keylen=0: no key) { if (outlen == 0 || outlen > 32 || keylen > 32) { throw new ArgumentException(); } for (uint i = 0; i < 8; i++) // state, "param block" { ctx.h[i] = iv[i]; } ctx.h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen; ctx.t[0] = 0; // input count low word ctx.t[1] = 0; // input count high word ctx.c = 0; // pointer within buffer ctx.outlen = outlen; for (uint i = keylen; i < 64; i++) // zero input block { ctx.b[i] = 0; } if (keylen > 0) { update(ref ctx, key, keylen); ctx.c = 64; // at the end } }
// Add "inlen" bytes from "in" into the hash. unsafe public static void update(ref Blake2Context ctx, void *data, uint inlen) // data bytes { for (uint i = 0; i < inlen; i++) { if (ctx.c == 64) // buffer full ? { ctx.t[0] += ctx.c; // add counters if (ctx.t[0] < ctx.c) // carry overflow ? { ctx.t[1]++; // high word } compress(ref ctx, false); // compress (not last) ctx.c = 0; // counter to zero } ctx.b[ctx.c++] = ((byte *)data)[i]; } }
// Compression function. "last" flag indicates last block. unsafe static void compress(ref Blake2Context ctx, bool last) { uint *v = stackalloc uint[16]; uint *m = stackalloc uint[16]; for (uint i = 0; i < 8; i++) // init work variables { v[i] = ctx.h[i]; v[i + 8] = iv[i]; } v[12] ^= ctx.t[0]; // low 32 bits of offset v[13] ^= ctx.t[1]; // high 32 bits if (last) // last block flag set ? { v[14] = ~v[14]; fixed(byte *pb = &ctx.b[0]) for (uint i = 0; i < 16; i++) // get little-endian words m[i] = getUInt32(&pb[sizeof(uint) * i]); for (uint i = 0; i < 10; i++) // ten rounds { mix(ref v[0], ref v[4], ref v[8], ref v[12], m[sigma[i][0]], m[sigma[i][1]]); mix(ref v[1], ref v[5], ref v[9], ref v[13], m[sigma[i][2]], m[sigma[i][3]]); mix(ref v[2], ref v[6], ref v[10], ref v[14], m[sigma[i][4]], m[sigma[i][5]]); mix(ref v[3], ref v[7], ref v[11], ref v[15], m[sigma[i][6]], m[sigma[i][7]]); mix(ref v[0], ref v[5], ref v[10], ref v[15], m[sigma[i][8]], m[sigma[i][9]]); mix(ref v[1], ref v[6], ref v[11], ref v[12], m[sigma[i][10]], m[sigma[i][11]]); mix(ref v[2], ref v[7], ref v[8], ref v[13], m[sigma[i][12]], m[sigma[i][13]]); mix(ref v[3], ref v[4], ref v[9], ref v[14], m[sigma[i][14]], m[sigma[i][15]]); } for (uint i = 0; i < 8; i++) { ctx.h[i] ^= v[i] ^ v[i + 8]; } } // Initialize the hashing context "ctx" with optional key "key". // 1 <= outlen <= 32 gives the digest size in bytes. // Secret key (also <= 32 bytes) is optional (keylen = 0). unsafe public static void init(ref Blake2Context ctx, uint outlen,
// Generate the message digest (size given in init). // Result placed in "out". unsafe public static void final(ref Blake2Context ctx, void *hash) { ctx.t[0] += ctx.c; // mark last block offset if (ctx.t[0] < ctx.c) // carry overflow ctx.t[1]++; // high word }