// SM4 GCM 认证加密模式(Galois/Counter Mode)
// 依据:GB/T 32907-2016 分组密码 + NIST SP 800-38D 定义的 GCM 工作方式,
// 并以 RFC 8998(SM4-GCM for TLS 1.3)附录 A 测试向量对拍。
// GHASH 使用 GF(2^128) 上的乘法,约减多项式 R = 0xe1 || 0^120。
// 当前实现固定输出 16 字节(128 位)认证标签,支持任意长度 IV。

///|
/// GF(2^128) 乘法(MSB 在前的位序约定,SP 800-38D)。
fn gcm_gf_mul(x : FixedArray[Byte], y : FixedArray[Byte]) -> FixedArray[Byte] {
  let z : FixedArray[Byte] = FixedArray::make(16, b'\x00')
  let v : FixedArray[Byte] = FixedArray::make(16, b'\x00')
  for i in 0..<16 {
    v[i] = y[i]
  }
  for i in 0..<128 {
    let bit = (x[i / 8].to_int() >> (7 - i % 8)) & 1
    if bit == 1 {
      for j in 0..<16 {
        z[j] = (z[j].to_int() ^ v[j].to_int()).to_byte()
      }
    }
    let lsb = (v[15].to_int() & 1) == 1
    // v 整体右移 1 位
    let mut j = 15
    while j >= 1 {
      v[j] = ((v[j].to_int() >> 1) | ((v[j - 1].to_int() & 1) << 7)).to_byte()
      j = j - 1
    }
    v[0] = (v[0].to_int() >> 1).to_byte()
    if lsb {
      v[0] = (v[0].to_int() ^ 0xe1).to_byte()
    }
  }
  z
}

///|
/// 流式 GHASH:按 16 字节块累积 `y = GHASH_H(data)`,支持任意长度分段输入。
priv struct GHash {
  h : FixedArray[Byte]
  mut y : FixedArray[Byte]
  buf : FixedArray[Byte]
  mut buf_len : Int
}

///|
fn GHash::new(h : FixedArray[Byte]) -> GHash {
  {
    h,
    y: FixedArray::make(16, b'\x00'),
    buf: FixedArray::make(16, b'\x00'),
    buf_len: 0,
  }
}

///|
/// 对一个 16 字节块做 y = (y ^ block) * H。
fn GHash::absorb_block(self : GHash, block : FixedArray[Byte]) -> Unit {
  for j in 0..<16 {
    self.y[j] = (self.y[j].to_int() ^ block[j].to_int()).to_byte()
  }
  self.y = gcm_gf_mul(self.y, self.h)
}

///|
/// 喂入任意长度的数据,内部缓存不足一块的部分。
fn GHash::update(self : GHash, data : Bytes) -> Unit {
  let mut off = 0
  if self.buf_len > 0 {
    let space = 16 - self.buf_len
    let to_copy = if data.length() < space { data.length() } else { space }
    for i in 0..= 16 {
    let block : FixedArray[Byte] = FixedArray::make(16, b'\x00')
    for i in 0..<16 {
      block[i] = data[off + i]
    }
    self.absorb_block(block)
    off = off + 16
  }
  if data.length() - off > 0 {
    let n = data.length() - off
    for i in 0.. Unit {
  if self.buf_len > 0 {
    for i in self.buf_len..<16 {
      self.buf[i] = b'\x00'
    }
    self.absorb_block(self.buf)
    self.buf_len = 0
  }
}

///|
/// 8 字节大端位长编码。注意 MoonBit 的 Int 移位量超过 31 会回绕,
/// 因此高 4 字节直接置零、只用 ≤24 的移位量写低 4 字节。
fn gcm_len64(nbits : Int, out : FixedArray[Byte], offset : Int) -> Unit {
  for i in 0..<4 {
    out[offset + i] = b'\x00'
  }
  for i in 0..<4 {
    out[offset + 4 + i] = ((nbits >> (24 - i * 8)) & 0xff).to_byte()
  }
}

///|
/// 由 IV 派生预计数器 J0(SP 800-38D 8.1.1):
/// 12 字节 IV 时 `J0 = IV || 0^31 || 1`;其他长度经 GHASH_H 计算。
fn gcm_j0(h : FixedArray[Byte], iv : Bytes) -> FixedArray[Byte] {
  if iv.length() == 12 {
    let j0 : FixedArray[Byte] = FixedArray::make(16, b'\x00')
    for i in 0..<12 {
      j0[i] = iv[i]
    }
    j0[15] = b'\x01'
    j0
  } else {
    let g = GHash::new(h)
    g.update(iv)
    g.flush()
    // 长度块固定 16 字节:8 字节零 + 64 位 IV 位长
    let tail : FixedArray[Byte] = FixedArray::make(16, b'\x00')
    gcm_len64(iv.length() * 8, tail, 8)
    g.absorb_block(tail)
    g.y
  }
}

///|
/// GCM 计数器递增:仅最后 4 字节按 32 位大端加一(mod 2^32)。
fn gcm_inc32(cbe : FixedArray[Byte]) -> Unit {
  let mut i = 15
  while i >= 12 {
    let v = cbe[i].to_int() + 1
    if v <= 0xff {
      cbe[i] = v.to_byte()
      return
    }
    cbe[i] = 0
    i = i - 1
  }
}

///|
/// GCM 核心的 CTR 输出:从 inc32(J0) 开始生成密钥流并异或。
fn gcm_ctr_xor(
  c : Sm4,
  j0 : FixedArray[Byte],
  data : Bytes,
) -> FixedArray[Byte] {
  let cb : FixedArray[Byte] = FixedArray::make(16, b'\x00')
  for i in 0..<16 {
    cb[i] = j0[i]
  }
  gcm_inc32(cb)
  let out : FixedArray[Byte] = FixedArray::make(data.length(), b'\x00')
  let mut off = 0
  while off < data.length() {
    let ks = c.encrypt_block(Bytes::from_array(cb))
    let n = if data.length() - off < 16 { data.length() - off } else { 16 }
    for i in 0.. FixedArray[Byte] {
  let g = GHash::new(h)
  g.update(aad)
  g.flush()
  g.update(ciphertext)
  g.flush()
  let tail : FixedArray[Byte] = FixedArray::make(16, b'\x00')
  gcm_len64(aad.length() * 8, tail, 0)
  gcm_len64(ciphertext.length() * 8, tail, 8)
  g.absorb_block(tail)
  let ekj0 = c.encrypt_block(Bytes::from_array(j0))
  let t : FixedArray[Byte] = FixedArray::make(16, b'\x00')
  for i in 0..<16 {
    t[i] = (g.y[i].to_int() ^ ekj0[i].to_int()).to_byte()
  }
  t
}

///|
/// 常数时间标签比较(长度不同直接失败)。
fn gcm_tag_eq(a : FixedArray[Byte], b : Bytes) -> Bool {
  if a.length() != b.length() {
    return false
  }
  let mut diff = 0
  for i in 0.. Result[(Bytes, Bytes), Sm4Error] {
  let c = match sm4_of(key) {
    Ok(c) => c
    Err(e) => return Err(e)
  }
  if iv.length() == 0 {
    return Err(InvalidIvLength(0))
  }
  let h = gcm_h(c)
  let j0 = gcm_j0(h, iv)
  let ct = gcm_ctr_xor(c, j0, plaintext)
  let tag = gcm_tag(c, h, j0, aad, Bytes::from_array(ct))
  Ok((Bytes::from_array(ct), Bytes::from_array(tag)))
}

///|
/// GCM 认证解密:先校验 16 字节标签,不匹配返回 [AuthenticationFailed],
/// 校验通过后输出与密文等长的明文。
pub fn decrypt_gcm(
  key : Bytes,
  iv : Bytes,
  ciphertext : Bytes,
  tag : Bytes,
  aad : Bytes,
) -> Result[Bytes, Sm4Error] {
  let c = match sm4_of(key) {
    Ok(c) => c
    Err(e) => return Err(e)
  }
  if iv.length() == 0 {
    return Err(InvalidIvLength(0))
  }
  if tag.length() != 16 {
    return Err(InvalidIvLength(tag.length()))
  }
  let h = gcm_h(c)
  let j0 = gcm_j0(h, iv)
  let expected = gcm_tag(c, h, j0, aad, ciphertext)
  if !gcm_tag_eq(expected, tag) {
    return Err(AuthenticationFailed)
  }
  Ok(Bytes::from_array(gcm_ctr_xor(c, j0, ciphertext)))
}

///|
/// 由加密轮密钥派生 GHASH 子密钥 H = E(K, 0^128)。
fn gcm_h(c : Sm4) -> FixedArray[Byte] {
  let zero : Bytes = Bytes::from_array(FixedArray::make(16, b'\x00'))
  let b = c.encrypt_block(zero)
  let out : FixedArray[Byte] = FixedArray::make(16, b'\x00')
  for i in 0..<16 {
    out[i] = b[i]
  }
  out
}