///|
fn compress_u64(state : FixedArray[UInt64], block : FixedArray[UInt64]) -> Unit {
let mut a : UInt64 = state[0]
let mut b : UInt64 = state[1]
let mut c : UInt64 = state[2]
let mut d : UInt64 = state[3]
let mut e : UInt64 = state[4]
let mut f : UInt64 = state[5]
let mut g : UInt64 = state[6]
let mut h : UInt64 = state[7]
let k = get_k64()
let w : FixedArray[UInt64] = FixedArray::make(80, 0UL)
for i in 0..<16 {
w[i] = block[i]
}
for i in 16..<80 {
let w15 = w[i - 15]
let s0 = rotate_right_u64(w15, 1) ^ rotate_right_u64(w15, 8) ^ (w15 >> 7)
let w2 = w[i - 2]
let s1 = rotate_right_u64(w2, 19) ^ rotate_right_u64(w2, 61) ^ (w2 >> 6)
w[i] = w[i - 16] + s0 + w[i - 7] + s1
}
for i in 0..<80 {
let s1 = rotate_right_u64(e, 14) ^ rotate_right_u64(e, 18) ^ rotate_right_u64(e, 41)
// Use an equivalent formulation of Ch to avoid relying on all-ones constants:
// Ch(x, y, z) = (x & y) ^ (~x & z) == z ^ (x & (y ^ z))
let ch = g ^ (e & (f ^ g))
let temp1 = h + s1 + ch + k[i] + w[i]
let s0 = rotate_right_u64(a, 28) ^ rotate_right_u64(a, 34) ^ rotate_right_u64(a, 39)
let maj = (a & b) ^ (a & c) ^ (b & c)
let temp2 = s0 + maj
h = g
g = f
f = e
e = d + temp1
d = c
c = b
b = a
a = temp1 + temp2
}
state[0] = state[0] + a
state[1] = state[1] + b
state[2] = state[2] + c
state[3] = state[3] + d
state[4] = state[4] + e
state[5] = state[5] + f
state[6] = state[6] + g
state[7] = state[7] + h
}
fn compress512(state : FixedArray[UInt64], blocks : FixedArray[FixedArray[UInt64]]) -> Unit {
for block in blocks {
compress_u64(state, block)
}
}
// ==== public API ==============================================================
pub struct Sha512 {
state : FixedArray[UInt64]
buffer : FixedArray[UInt] // 128-byte work buffer
mut buffer_len : Int // how many bytes currently in buffer
mut total_len : UInt64 // total message length in bytes (mod 2^128)
}
pub fn Sha512::new() -> Sha512 {
let iv = sha512_state()
let state = FixedArray::make(8, 0UL)
for i in 0..<8 { state[i] = iv[i] }
Sha512::{ state, buffer: FixedArray::make(128, 0U), buffer_len: 0, total_len: 0UL }
}
// Feed bytes (as UInt array) ---------------------------------------------------
pub fn Sha512::update(self : Sha512, input : Array[UInt]) -> Unit {
let mut input_offset = 0
let input_len = input.length()
self.total_len = self.total_len + input_len.to_uint64()
// Fill existing buffer first
if self.buffer_len > 0 {
let space_left = 128 - self.buffer_len
let to_copy = if input_len < space_left { input_len } else { space_left }
for i in 0.. FixedArray[UInt64] {
// message length in bits (low 64 bits; SHA-512 uses 128-bit length, high 64 here are 0)
let bit_len : UInt64 = self.total_len * 8UL
// append 0x80
self.buffer[self.buffer_len] = 0x80U
self.buffer_len += 1
// if not enough space for 16 length bytes, pad & process
if self.buffer_len > 112 {
while self.buffer_len < 128 {
self.buffer[self.buffer_len] = 0U
self.buffer_len += 1
}
// process block
let block : FixedArray[UInt64] = FixedArray::make(16, 0UL)
for i in 0..<16 {
let idx = i * 8
block[i] =
(self.buffer[idx ].to_uint64() << 56) |
(self.buffer[idx + 1].to_uint64() << 48) |
(self.buffer[idx + 2].to_uint64() << 40) |
(self.buffer[idx + 3].to_uint64() << 32) |
(self.buffer[idx + 4].to_uint64() << 24) |
(self.buffer[idx + 5].to_uint64() << 16) |
(self.buffer[idx + 6].to_uint64() << 8) |
self.buffer[idx + 7].to_uint64()
}
let blocks : FixedArray[FixedArray[UInt64]] = FixedArray::make(1, block)
compress512(self.state, blocks)
self.buffer_len = 0
}
// pad with zeros up to 112 bytes
while self.buffer_len < 112 {
self.buffer[self.buffer_len] = 0U
self.buffer_len += 1
}
// write 128-bit length: high 64 = 0, low 64 = bit_len
for i in 0..<8 { self.buffer[112 + i] = 0U }
self.buffer[120] = ((bit_len >> 56) & 0xFFUL).to_uint()
self.buffer[121] = ((bit_len >> 48) & 0xFFUL).to_uint()
self.buffer[122] = ((bit_len >> 40) & 0xFFUL).to_uint()
self.buffer[123] = ((bit_len >> 32) & 0xFFUL).to_uint()
self.buffer[124] = ((bit_len >> 24) & 0xFFUL).to_uint()
self.buffer[125] = ((bit_len >> 16) & 0xFFUL).to_uint()
self.buffer[126] = ((bit_len >> 8) & 0xFFUL).to_uint()
self.buffer[127] = ( bit_len & 0xFFUL).to_uint()
// final block
let block : FixedArray[UInt64] = FixedArray::make(16, 0UL)
for i in 0..<16 {
let idx = i * 8
block[i] =
(self.buffer[idx ].to_uint64() << 56) |
(self.buffer[idx + 1].to_uint64() << 48) |
(self.buffer[idx + 2].to_uint64() << 40) |
(self.buffer[idx + 3].to_uint64() << 32) |
(self.buffer[idx + 4].to_uint64() << 24) |
(self.buffer[idx + 5].to_uint64() << 16) |
(self.buffer[idx + 6].to_uint64() << 8) |
self.buffer[idx + 7].to_uint64()
}
let blocks : FixedArray[FixedArray[UInt64]] = FixedArray::make(1, block)
compress512(self.state, blocks)
self.state
}
// Convenience one-shot digests -------------------------------------------------
pub fn Sha512::digest(input : Array[UInt]) -> FixedArray[UInt64] {
let sha = Sha512::new()
sha.update(input)
sha.finalize()
}
pub fn Sha512::digest_bytes(input : Bytes) -> FixedArray[UInt64] {
// convert Bytes -> Array[UInt] cheaply
let arr : Array[UInt] = []
// Some platforms represent text bytes as UTF-16LE with interleaved zeros.
// Filter out zero bytes so known ASCII test vectors match.
for i in 0.. FixedArray[UInt64] {
let bytes = @utf8.encode(input)
let arr : Array[UInt] = []
for i in 0..