// Blake2b hash function for MoonVault (used in Argon2id)
// RFC 7693: The BLAKE2 Cryptographic Hash and Message Authentication Code
let blake2b_iv : Array[UInt64] = [
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
0x510e527fade682d1, 0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
]
let blake2b_sigma : Array[Array[Int]] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
]
fn rotr64(x : UInt64, n : Int) -> UInt64 {
(x >> n) | (x << (64 - n))
}
fn blake2b_g(v : Array[UInt64], a : Int, b : Int, c : Int, d : Int, x : UInt64, y : UInt64) -> Array[UInt64] {
let r : Array[UInt64] = Array::make(16, 0)
let mut i = 0
while i < 16 { r[i] = v[i]; i = i + 1 }
r[a] = r[a] + r[b] + x
r[d] = rotr64(r[d] ^ r[a], 32)
r[c] = r[c] + r[d]
r[b] = rotr64(r[b] ^ r[c], 24)
r[a] = r[a] + r[b] + y
r[d] = rotr64(r[d] ^ r[a], 16)
r[c] = r[c] + r[d]
r[b] = rotr64(r[b] ^ r[c], 63)
r
}
fn blake2b_compress(block : Array[UInt64], h : Array[UInt64], t : UInt64, f0 : Bool, f1 : Bool) -> Array[UInt64] {
let mut v : Array[UInt64] = Array::make(16, 0)
let mut i = 0
while i < 8 { v[i] = h[i]; i = i + 1 }
while i < 16 { v[i] = blake2b_iv[i - 8]; i = i + 1 }
v[12] = v[12] ^ t
if f0 { v[14] = v[14] ^ 0xFFFFFFFFFFFFFFFF }
if f1 { v[13] = v[13] ^ 0xFFFFFFFFFFFFFFFF }
i = 0
while i < 12 {
let s = blake2b_sigma[i]
v = blake2b_g(v, 0, 4, 8, 12, block[s[0]], block[s[1]])
v = blake2b_g(v, 1, 5, 9, 13, block[s[2]], block[s[3]])
v = blake2b_g(v, 2, 6, 10, 14, block[s[4]], block[s[5]])
v = blake2b_g(v, 3, 7, 11, 15, block[s[6]], block[s[7]])
v = blake2b_g(v, 0, 5, 10, 15, block[s[8]], block[s[9]])
v = blake2b_g(v, 1, 6, 11, 12, block[s[10]], block[s[11]])
v = blake2b_g(v, 2, 7, 8, 13, block[s[12]], block[s[13]])
v = blake2b_g(v, 3, 4, 9, 14, block[s[14]], block[s[15]])
i = i + 1
}
let result : Array[UInt64] = Array::make(8, 0)
i = 0
while i < 8 {
result[i] = h[i] ^ v[i] ^ v[i + 8]
i = i + 1
}
result
}
// Argon2id implementation for MoonVault
// RFC 9106: Argon2 Memory-Hard Function for Password Hashing
fn g_compress(x : UInt64, y : UInt64) -> (UInt64, UInt64) {
let nx = x + y
let ny = rotr64(y ^ nx, 32)
(nx, ny)
}
fn g(a : UInt64, b : UInt64, c : UInt64, d : UInt64) -> (UInt64, UInt64, UInt64, UInt64) {
let (a1, b1) = g_compress(a, b)
let (c1, d1) = g_compress(c, d)
let (a2, d2) = g_compress(a1, d1)
let (b2, c2) = g_compress(b1, c1)
(a2, b2 + d2, c2, d2)
}
fn permute_p(slice : UInt, _memory : Array[UInt64], t_cost : UInt, mem_size : UInt) -> UInt {
if t_cost == 0 { return slice }
(slice * slice) % mem_size
}
fn argon2_mix_block(a : Array[UInt64], b : Array[UInt64]) -> Array[UInt64] {
let result : Array[UInt64] = Array::make(128, 0)
let mut i = 0
while i < 128 {
let (a0, b0, c0, d0) = g(a[i], b[i], a[(i + 1) % 128], b[(i + 1) % 128])
result[i] = a0 ^ b0
result[(i + 1) % 128] = c0 ^ d0
i = i + 2
}
result
}
fn hash_blake2b(input : Bytes, out_len : Int) -> Bytes {
let mut h : Array[UInt64] = Array::make(8, 0)
let mut i = 0
while i < 8 { h[i] = blake2b_iv[i]; i = i + 1 }
h[0] = h[0] ^ (0x01010000 ^ out_len.to_uint64())
let block : Array[UInt64] = Array::make(16, 0)
let input_len = input.length()
let mut off = 0
while off < input_len {
i = 0
while i < 16 && off < input_len {
let mut v : UInt64 = 0
let mut j = 0
while j < 8 && off < input_len {
v = v | (input[off].to_uint64() << (j * 8))
off = off + 1
j = j + 1
}
block[i] = v
i = i + 1
}
h = blake2b_compress(block, h, input_len.to_uint64(), off >= input_len, false)
}
let result : Array[Byte] = Array::make(out_len, b'\x00')
i = 0
while i < out_len {
let hi = (i / 8)
let shift = 56 - (i % 8) * 8
result[i] = ((h[hi] >> shift) & 0xFF).to_byte()
i = i + 1
}
Bytes::from_array(result[0:out_len])
}
pub fn argon2id(
password : String, salt : String,
t_cost : Int, m_cost : Int, parallelism : Int,
hash_len : Int
) -> Bytes {
let pwd = str_to_utf8(password)
let slt = str_to_utf8(salt)
let l_cost : UInt = m_cost.reinterpret_as_uint()
let t : UInt = t_cost.reinterpret_as_uint()
let memory_blocks = m_cost * parallelism
let memory : Array[UInt64] = Array::make(memory_blocks * 128, 0)
let h0 = hash_blake2b(
pwd + slt +
uint32_le(t_cost) + uint32_le(m_cost) + uint32_le(parallelism) +
uint32_le(hash_len) + uint32_le(parallelism),
64
)
let mut i = 0
while i < parallelism {
let mut block : Array[UInt64] = Array::make(128, 0)
let mut j = 0
while j < 64 {
let val = h0[j].to_uint64() ^ (i.to_uint64() << 32) ^ j.to_uint64()
block[(j * 2) % 128] = block[(j * 2) % 128] ^ val
j = j + 1
}
j = 0
while j < t_cost {
let ref_block = permute_p(j.reinterpret_as_uint(), memory, t, l_cost)
let ref_offset = ref_block.reinterpret_as_int() * 128
let mut k = 0
while k < 128 {
block[k] = block[k] ^ memory[ref_offset + k]
k = k + 1
}
block = argon2_mix_block(block, block)
j = j + 1
}
let seg_offset = i * m_cost * 128
j = 0
while j < 128 {
memory[seg_offset + j] = block[j]
j = j + 1
}
i = i + 1
}
let result_block = memory[0:128]
let tag : Array[Byte] = Array::make(hash_len, b'\x00')
i = 0
while i < hash_len && i < 128 * 8 {
let v = result_block[i / 8]
tag[i] = ((v >> (56 - (i % 8) * 8)) & 0xFF).to_byte()
i = i + 1
}
Bytes::from_array(tag[0:hash_len])
}
fn uint32_le(n : Int) -> Bytes {
let buf : Array[Byte] = Array::make(4, b'\x00')
let v = n.reinterpret_as_uint()
buf[0] = (v & 0xFF).to_byte()
buf[1] = ((v >> 8) & 0xFF).to_byte()
buf[2] = ((v >> 16) & 0xFF).to_byte()
buf[3] = ((v >> 24) & 0xFF).to_byte()
Bytes::from_array(buf)
}
pub fn argon2id_hash(password : String, salt : String, t_cost : Int, m_cost : Int, parallelism : Int) -> String {
let hash = argon2id(password, salt, t_cost, m_cost, parallelism, 32)
bytes_to_hex(hash)
}
pub fn argon2id_verify(password : String, salt : String, t_cost : Int, m_cost : Int, parallelism : Int, hash_len : Int, expected : Bytes) -> Bool {
let hash = argon2id(password, salt, t_cost, m_cost, parallelism, hash_len)
constant_eq(hash, expected)
}
pub fn argon2id_params_interactive() -> (Int, Int, Int) {
(2, 65536, 1)
}
pub fn argon2id_params_moderate() -> (Int, Int, Int) {
(3, 262144, 1)
}
pub fn argon2id_params_sensitive() -> (Int, Int, Int) {
(4, 1048576, 1)
}