// Copyright (c) 2021 Christoffer Lerno. All rights reserved. // Use of this source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. // // Implementation was off Steve Reid's SHA-1 C implementation module std::hash::sha1; import std::hash::hmac; import std::bits; const BLOCK_BYTES = 64; const HASH_BYTES = 20; struct Sha1 { uint[5] state; uint[2] count; char[BLOCK_BYTES] buffer; } alias HmacSha1 = Hmac{Sha1, HASH_BYTES, BLOCK_BYTES}; alias hmac = hmac::hash{Sha1, HASH_BYTES, BLOCK_BYTES}; alias pbkdf2 = hmac::pbkdf2{Sha1, HASH_BYTES, BLOCK_BYTES}; fn char[HASH_BYTES] hash(char[] data) { Sha1 sha1 @noinit; sha1.init(); sha1.update(data); return sha1.final(); } fn void Sha1.init(&self) { // SHA1 initialization constants *self = { .state = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 } }; } <* @param [in] data @require data.len <= uint.max *> fn void Sha1.update(&self, char[] data) { uint j = self.count[0]; uint len = data.len; if ((self.count[0] += len << 3) < j) self.count[1]++; self.count[1] += len >> 29; j = (j >> 3) & 63; uint i; if (j + len > 63) { i = 64 - j; self.buffer[j..] = data[:i]; sha1_transform(&self.state, &self.buffer); for (; i + 63 < len; i += 64) { sha1_transform(&self.state, &data[i]); } j = 0; } self.buffer[j:len - i] = data[i..]; } fn char[HASH_BYTES] Sha1.final(&self) { char[8] finalcount; for (uint i = 0; i < 8; i++) { finalcount[i] = (char)((self.count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 0xFF); } self.update((char[]){ 0o200 }); while ((self.count[0] & 504) != 448) { self.update((char[]){ 0 }); } self.update(&finalcount); char[HASH_BYTES] digest; for (uint i = 0; i < HASH_BYTES; i++) { digest[i] = (char)((self.state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF); } // Clear mem *self = {}; finalcount = {}; return digest; } union Long16 @local { char[BLOCK_BYTES] c; uint[16] l; } macro blk(Long16* block, i) @local { return (block.l[i & 15] = (block.l[(i + 13) & 15] ^ block.l[(i + 8) & 15] ^ block.l[(i + 2) & 15] ^ block.l[i & 15]).rotl(1)); } macro blk0(Long16* block, i) @local { $if env::BIG_ENDIAN: return block.l[i]; $else return block.l[i] = (block.l[i].rotl(24) & 0xFF00FF00) | (block.l[i].rotl(8) & 0x00FF00FF); $endif } macro r0(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local { var w = *wref; *z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + v.rotl(5); *wref = w.rotl(30); } macro r1(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local { var w = *wref; *z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + v.rotl(5); *wref = w.rotl(30); } macro r2(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local { var w = *wref; *z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + v.rotl(5); *wref = w.rotl(30); } macro r3(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local { var w = *wref; *z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + v.rotl(5); *wref = w.rotl(30); } macro r4(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local { var w = *wref; *z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + v.rotl(5); *wref = w.rotl(30); } <* @param [&inout] state @param [&in] buffer *> fn void sha1_transform(uint[5]* state, char* buffer) @local { Long16 block; block.c[..] = buffer[:64]; uint a = (*state)[0]; uint b = (*state)[1]; uint c = (*state)[2]; uint d = (*state)[3]; uint e = (*state)[4]; r0(&block, a, &b, c, d, &e, 0); r0(&block, e, &a, b, c, &d, 1); r0(&block, d, &e, a, b, &c, 2); r0(&block, c, &d, e, a, &b, 3); r0(&block, b, &c, d, e, &a, 4); r0(&block, a, &b, c, d, &e, 5); r0(&block, e, &a, b, c, &d, 6); r0(&block, d, &e, a, b, &c, 7); r0(&block, c, &d, e, a, &b, 8); r0(&block, b, &c, d, e, &a, 9); r0(&block, a, &b, c, d, &e, 10); r0(&block, e, &a, b, c, &d, 11); r0(&block, d, &e, a, b, &c, 12); r0(&block, c, &d, e, a, &b, 13); r0(&block, b, &c, d, e, &a, 14); r0(&block, a, &b, c, d, &e, 15); r1(&block, e, &a, b, c, &d, 16); r1(&block, d, &e, a, b, &c, 17); r1(&block, c, &d, e, a, &b, 18); r1(&block, b, &c, d, e, &a, 19); r2(&block, a, &b, c, d, &e, 20); r2(&block, e, &a, b, c, &d, 21); r2(&block, d, &e, a, b, &c, 22); r2(&block, c, &d, e, a, &b, 23); r2(&block, b, &c, d, e, &a, 24); r2(&block, a, &b, c, d, &e, 25); r2(&block, e, &a, b, c, &d, 26); r2(&block, d, &e, a, b, &c, 27); r2(&block, c, &d, e, a, &b, 28); r2(&block, b, &c, d, e, &a, 29); r2(&block, a, &b, c, d, &e, 30); r2(&block, e, &a, b, c, &d, 31); r2(&block, d, &e, a, b, &c, 32); r2(&block, c, &d, e, a, &b, 33); r2(&block, b, &c, d, e, &a, 34); r2(&block, a, &b, c, d, &e, 35); r2(&block, e, &a, b, c, &d, 36); r2(&block, d, &e, a, b, &c, 37); r2(&block, c, &d, e, a, &b, 38); r2(&block, b, &c, d, e, &a, 39); r3(&block, a, &b, c, d, &e, 40); r3(&block, e, &a, b, c, &d, 41); r3(&block, d, &e, a, b, &c, 42); r3(&block, c, &d, e, a, &b, 43); r3(&block, b, &c, d, e, &a, 44); r3(&block, a, &b, c, d, &e, 45); r3(&block, e, &a, b, c, &d, 46); r3(&block, d, &e, a, b, &c, 47); r3(&block, c, &d, e, a, &b, 48); r3(&block, b, &c, d, e, &a, 49); r3(&block, a, &b, c, d, &e, 50); r3(&block, e, &a, b, c, &d, 51); r3(&block, d, &e, a, b, &c, 52); r3(&block, c, &d, e, a, &b, 53); r3(&block, b, &c, d, e, &a, 54); r3(&block, a, &b, c, d, &e, 55); r3(&block, e, &a, b, c, &d, 56); r3(&block, d, &e, a, b, &c, 57); r3(&block, c, &d, e, a, &b, 58); r3(&block, b, &c, d, e, &a, 59); r4(&block, a, &b, c, d, &e, 60); r4(&block, e, &a, b, c, &d, 61); r4(&block, d, &e, a, b, &c, 62); r4(&block, c, &d, e, a, &b, 63); r4(&block, b, &c, d, e, &a, 64); r4(&block, a, &b, c, d, &e, 65); r4(&block, e, &a, b, c, &d, 66); r4(&block, d, &e, a, b, &c, 67); r4(&block, c, &d, e, a, &b, 68); r4(&block, b, &c, d, e, &a, 69); r4(&block, a, &b, c, d, &e, 70); r4(&block, e, &a, b, c, &d, 71); r4(&block, d, &e, a, b, &c, 72); r4(&block, c, &d, e, a, &b, 73); r4(&block, b, &c, d, e, &a, 74); r4(&block, a, &b, c, d, &e, 75); r4(&block, e, &a, b, c, &d, 76); r4(&block, d, &e, a, b, &c, 77); r4(&block, c, &d, e, a, &b, 78); r4(&block, b, &c, d, e, &a, 79); (*state)[0] += a; (*state)[1] += b; (*state)[2] += c; (*state)[3] += d; (*state)[4] += e; a = b = c = d = e = 0; block = {}; }