// HMAC-SHA-256 for MoonVault
// RFC 2104: HMAC: Keyed-Hashing for Message Authentication

fn xor_bytes(a : Array[Byte], b : Byte, len : Int) -> Array[Byte] {
  let result : Array[Byte] = Array::make(len, b'\x00')
  let mut i = 0
  while i < len { result[i] = a[i] ^ b; i = i + 1 }
  result
}

struct HmacSha256 {
  ipad : Array[Byte]
  opad : Array[Byte]
}

fn normalize_key(key : Bytes) -> Array[Byte] {
  let block_size = 64
  if key.length() > block_size {
    let h = Sha256::new()
    let h = h.write(key)
    let hashed = h.sum()
    let buf = Array::make(block_size, b'\x00')
    let mut i = 0
    while i < hashed.length() { buf[i] = hashed[i]; i = i + 1 }
    buf
  } else {
    let buf = Array::make(block_size, b'\x00')
    let mut i = 0
    while i < key.length() { buf[i] = key[i]; i = i + 1 }
    buf
  }
}

pub fn HmacSha256::new(key : Bytes) -> HmacSha256 {
  let block_size = 64
  let key_data = normalize_key(key)
  {
    ipad: xor_bytes(key_data, b'\x36', block_size),
    opad: xor_bytes(key_data, b'\x5c', block_size),
  }
}

pub fn HmacSha256::sign(self : HmacSha256, message : Bytes) -> Bytes {
  let inner = Sha256::new()
  let inner = inner.write(Bytes::from_array(self.ipad))
  let inner = inner.write(message)
  let inner_hash = inner.sum()

  let outer = Sha256::new()
  let outer = outer.write(Bytes::from_array(self.opad))
  let outer = outer.write(inner_hash)
  outer.sum()
}

pub fn HmacSha256::sign_string(self : HmacSha256, s : String) -> Bytes {
  self.sign(str_to_utf8(s))
}

pub fn hmac_sha256(key : Bytes, message : Bytes) -> Bytes {
  let h = HmacSha256::new(key)
  h.sign(message)
}

pub fn hmac_sha256_hex(key : String, message : String) -> String {
  let h = HmacSha256::new(str_to_utf8(key))
  bytes_to_hex(h.sign_string(message))
}

pub fn HmacSha256::verify(self : HmacSha256, message : Bytes, mac : Bytes) -> Bool {
  let expected = self.sign(message)
  constant_eq(expected, mac)
}

pub fn hmac_verify(key : Bytes, message : Bytes, mac : Bytes) -> Bool {
  let h = HmacSha256::new(key)
  h.verify(message, mac)
}