-
-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2233f24
commit 54da4fa
Showing
3 changed files
with
251 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
module std::hash::sha256; | ||
|
||
import std::hash::hmac; | ||
|
||
const BLOCK_SIZE = 64; | ||
const HASH_SIZE = 32; | ||
|
||
const uint[64] K @local = { | ||
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 | ||
}; | ||
|
||
// Right rotate function | ||
macro uint @rotr(uint x, uint n) @local => (((x) >> (n)) | ((x) << (32 - (n)))); | ||
|
||
// SHA-256 functions | ||
macro uint @ch(uint x, uint y, uint z) @local => (x & y) ^ (~x & z); | ||
macro uint @maj(uint x, uint y, uint z) @local => (x & y) ^ (x & z) ^ (y & z); | ||
macro uint @_sigma0(uint x) @local => @rotr(x, 2) ^ @rotr(x, 13) ^ @rotr(x, 22); | ||
macro uint @_sigma1(uint x) @local => @rotr(x, 6) ^ @rotr(x, 11) ^ @rotr(x, 25); | ||
macro uint @sigma0(uint x) @local => @rotr(x, 7) ^ @rotr(x, 18) ^ (x >> 3); | ||
macro uint @sigma1(uint x) @local => @rotr(x, 17) ^ @rotr(x, 19) ^ (x >> 10); | ||
|
||
struct Sha256 | ||
{ | ||
uint[8] state; | ||
ulong bitcount; | ||
char[BLOCK_SIZE] buffer; | ||
} | ||
|
||
def HmacSha256 = Hmac(<Sha256, HASH_SIZE, BLOCK_SIZE>); | ||
def hmac = hmac::hash(<Sha256, HASH_SIZE, BLOCK_SIZE>); | ||
def pbkdf2 = hmac::pbkdf2(<Sha256, HASH_SIZE, BLOCK_SIZE>); | ||
|
||
fn char[HASH_SIZE] hash(char[] data) | ||
{ | ||
Sha256 sha256 @noinit; | ||
sha256.init(); | ||
sha256.update(data); | ||
return sha256.final(); | ||
} | ||
|
||
fn void Sha256.init(&self) | ||
{ | ||
// Sha256 initialization constants | ||
*self = { | ||
.state = { | ||
0x6A09E667, | ||
0xBB67AE85, | ||
0x3C6EF372, | ||
0xA54FF53A, | ||
0x510E527F, | ||
0x9B05688C, | ||
0x1F83D9AB, | ||
0x5BE0CD19 | ||
} | ||
}; | ||
} | ||
|
||
/** | ||
* @param [in] data | ||
* @require data.len <= uint.max | ||
**/ | ||
fn void Sha256.update(&self, char[] data) { | ||
uint i = 0; | ||
uint len = data.len; | ||
uint buffer_pos = (uint)(self.bitcount / 8) % BLOCK_SIZE; | ||
self.bitcount += (ulong)(len * 8); | ||
|
||
while (len--) { | ||
self.buffer[buffer_pos++] = data[i++]; | ||
if (buffer_pos == BLOCK_SIZE) { | ||
sha256_transform(&self.state, &self.buffer); | ||
buffer_pos = 0; // Reset buffer position | ||
} | ||
} | ||
} | ||
|
||
fn char[HASH_SIZE] Sha256.final(&self) { | ||
char[HASH_SIZE] hash; | ||
ulong i = (self.bitcount / 8) % BLOCK_SIZE; | ||
|
||
// Append 0x80 to the buffer | ||
self.buffer[i++] = 0x80; | ||
|
||
// Pad the buffer with zeros | ||
if (i > BLOCK_SIZE - 8) { | ||
while (i < BLOCK_SIZE) { | ||
self.buffer[i++] = 0x00; | ||
} | ||
sha256_transform(&self.state, &self.buffer); | ||
i = 0; // Reset buffer index after transformation | ||
} | ||
|
||
while (i < BLOCK_SIZE - 8) { | ||
self.buffer[i++] = 0x00; | ||
} | ||
|
||
// Append the bitcount in big-endian format | ||
for (int j = 0; j < 8; ++j) { | ||
self.buffer[BLOCK_SIZE - 8 + j] = (char)((self.bitcount >> (56 - j * 8)) & 0xFF); | ||
} | ||
|
||
sha256_transform(&self.state, &self.buffer); | ||
|
||
// Convert state to the final hash | ||
for (i = 0; i < 8; ++i) { | ||
hash[i * 4] = (char)((self.state[i] >> 24) & 0xFF); | ||
hash[i * 4 + 1] = (char)((self.state[i] >> 16) & 0xFF); | ||
hash[i * 4 + 2] = (char)((self.state[i] >> 8) & 0xFF); | ||
hash[i * 4 + 3] = (char)(self.state[i] & 0xFF); | ||
} | ||
|
||
return hash; | ||
} | ||
|
||
/** | ||
* @param [&inout] state | ||
* @param [&in] buffer | ||
**/ | ||
fn void sha256_transform(uint* state, char* buffer) @local { | ||
uint a, b, c, d, e, f, g, h, t1, t2; | ||
uint[64] m; | ||
int i; | ||
|
||
// Prepare the message schedule | ||
for (i = 0; i < 16; ++i) { | ||
m[i] = ((uint)buffer[i * 4] << 24) | ((uint)buffer[i * 4 + 1] << 16) | | ||
((uint)buffer[i * 4 + 2] << 8) | ((uint)buffer[i * 4 + 3]); // Ensure values are cast to uint for correct shifts | ||
} | ||
for (i = 16; i < 64; ++i) { | ||
m[i] = @sigma1(m[i - 2]) + m[i - 7] + @sigma0(m[i - 15]) + m[i - 16]; | ||
} | ||
|
||
// Initialize working variables | ||
a = state[0]; | ||
b = state[1]; | ||
c = state[2]; | ||
d = state[3]; | ||
e = state[4]; | ||
f = state[5]; | ||
g = state[6]; | ||
h = state[7]; | ||
|
||
// Perform the main SHA-256 compression function | ||
for (i = 0; i < 64; ++i) { | ||
t1 = h + @_sigma1(e) + @ch(e, f, g) + K[i] + m[i]; | ||
t2 = @_sigma0(a) + @maj(a, b, c); | ||
h = g; | ||
g = f; | ||
f = e; | ||
e = d + t1; | ||
d = c; | ||
c = b; | ||
b = a; | ||
a = t1 + t2; | ||
} | ||
|
||
// Update the state | ||
state[0] += a; | ||
state[1] += b; | ||
state[2] += c; | ||
state[3] += d; | ||
state[4] += e; | ||
state[5] += f; | ||
state[6] += g; | ||
state[7] += h; | ||
a = b = c = d = e = f = g = h = t1 = t2 = i = 0; | ||
m[:64] = buffer[:64] = 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
module std::hash::sha256_test @test; | ||
import std::hash::sha256; | ||
|
||
fn void test_sha256_empty() | ||
{ | ||
Sha256 sha; | ||
sha.init(); | ||
sha.update(""); | ||
|
||
assert(sha.final() == x"E3B0C442 98FC1C14 9AFBF4C8 996FB924 27AE41E4 649B934C A495991B 7852B855"); | ||
} | ||
|
||
fn void test_sha256_abc() | ||
{ | ||
Sha256 sha; | ||
sha.init(); | ||
sha.update("abc"); | ||
|
||
assert(sha.final() == x"BA7816BF 8F01CFEA 414140DE 5DAE2223 B00361A3 96177A9C B410FF61 F20015AD"); | ||
} | ||
|
||
fn void test_sha256_longer() | ||
{ | ||
Sha256 sha; | ||
sha.init(); | ||
sha.update("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopqabcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); | ||
assert(sha.final() == x"59F109D9 533B2B70 E7C3B814 A2BD218F 78EA5D37 14455BC6 7987CF0D 664399CF"); | ||
} | ||
|
||
fn void test_pbkdf2() | ||
{ | ||
char[] pw = "password"; | ||
char[] s = "salt"; | ||
char[32] out; | ||
sha256::pbkdf2(pw, s, 1, &out); | ||
assert(out == x'120FB6CF FCF8B32C 43E72252 56C4F837 A86548C9 2CCC3548 0805987C B70BE17B'); | ||
sha256::pbkdf2(pw, s, 2, &out); | ||
assert(out == x'AE4D0C95 AF6B46D3 2D0ADFF9 28F06DD0 2A303F8E F3C251DF D6E2D85A 95474C43'); | ||
sha256::pbkdf2(pw, s, 4096, &out); | ||
assert(out == x'C5E478D5 9288C841 AA530DB6 845C4C8D 962893A0 01CE4E11 A4963873 AA98134A'); | ||
} | ||
|
||
fn void test_pbkdf2_2() | ||
{ | ||
char[] pw = "passwordPASSWORDpassword"; | ||
char[] s = "saltSALTsaltSALTsaltSALTsaltSALTsalt"; | ||
char[32] out; | ||
sha256::pbkdf2(pw, s, 4096, &out); | ||
assert(out == x'348C89DB CBD32B2F 32D814B8 116E84CF 2B17347E BC180018 1C4E2A1F B8DD53E1'); | ||
} | ||
|
||
import std::io; | ||
fn void test_pbkdf2_3() | ||
{ | ||
char[] pw = "pass\0word"; | ||
char[] salt = "sa\0lt"; | ||
char[32] out; | ||
sha256::pbkdf2(pw, salt, 4096, &out); | ||
|
||
assert(out == x'89B69D05 16F82989 3C696226 650A8687 8C029AC1 3EE27650 9D5AE58B 6466A724'); | ||
} | ||
|
||
fn void test_sha256_million_a() | ||
{ | ||
Sha256 sha; | ||
sha.init(); | ||
const int COUNT = 1_000_000; | ||
for (int i = 0; i < COUNT / 10; i++) | ||
{ | ||
sha.update("aaaaaaaaaa"); | ||
} | ||
|
||
assert(sha.final() == x"CDC76E5C 9914FB92 81A1C7E2 84D73E67 F1809A48 A497200E 046D39CC C7112CD0"); | ||
} |