///|
/// Fixed User privilege (02h), name-and-privilege lookup (bit 4 clear).
/// Username is an opaque sequence of 1..16 bytes, not a locale-dependent string.
pub fn rakp1_request(
  tag : Int32,
  bmc_id : UInt32,
  console_random : Bytes,
  username : Bytes,
) -> Bytes raise ProtocolError {
  guard tag >= 0 &&
    tag <= 255 &&
    bmc_id != 0U &&
    console_random.length() == 16 &&
    username.length() >= 1 &&
    username.length() <= 16 else {
    raise invalid("invalid RAKP1 parameters")
  }
  let out : Array[Byte] = [byte(tag), 0, 0, 0]
  put32(out, bmc_id)
  append(out, console_random)
  out.append([2, 0, 0, byte(username.length())])
  append(out, username)
  Bytes::from_array(out)
}

///|
/// Parsed, NOT authenticated RAKP2 challenge. Callers must authenticate it
/// before sending RAKP3. Parsing alone does not establish a trusted session.
pub struct RakpChallenge {
  bmc_random : Bytes
  bmc_guid : Bytes
  authentication_code : Bytes
}

///|
pub fn decode_rakp2(
  data : Bytes,
  tag : Int32,
  console_id : UInt32,
) -> RakpChallenge raise ProtocolError {
  guard tag >= 0 && tag <= 255 && console_id != 0U else {
    raise invalid("invalid expected session identity")
  }
  guard data.length() >= 8 else { raise invalid("truncated RAKP2") }
  guard data[0].to_int() == tag &&
    u32(data, 4) == console_id &&
    data[2] == 0 &&
    data[3] == 0 else {
    raise invalid("RAKP2 response identity mismatch")
  }
  guard data[1] == 0 else { raise invalid("BMC rejected RAKP1") }
  guard data.length() == 72 else {
    raise invalid("suite 17 RAKP2 must contain 72 bytes")
  }
  {
    bmc_random: slice(data, 8, 24),
    bmc_guid: slice(data, 24, 40),
    authentication_code: slice(data, 40, 72),
  }
}

///|
/// Encode a suite 17 RAKP3 packet. Authentication code must already have been
/// calculated after successful RAKP2 verification. This function does not verify it.
pub fn rakp3_request(
  tag : Int32,
  bmc_id : UInt32,
  authentication_code : Bytes,
) -> Bytes raise ProtocolError {
  guard tag >= 0 &&
    tag <= 255 &&
    bmc_id != 0U &&
    authentication_code.length() == 32 else {
    raise invalid("invalid suite 17 RAKP3 parameters")
  }
  let out : Array[Byte] = [byte(tag), 0, 0, 0]
  put32(out, bmc_id)
  append(out, authentication_code)
  Bytes::from_array(out)
}

///|
/// Return the unverified suite 17 RAKP4 integrity value. A session may only
/// become active after a constant-time comparison to the calculated value.
pub fn decode_rakp4(
  data : Bytes,
  tag : Int32,
  console_id : UInt32,
) -> Bytes raise ProtocolError {
  guard tag >= 0 && tag <= 255 && console_id != 0U else {
    raise invalid("invalid expected session identity")
  }
  guard data.length() >= 8 else { raise invalid("truncated RAKP4") }
  guard data[0].to_int() == tag &&
    u32(data, 4) == console_id &&
    data[2] == 0 &&
    data[3] == 0 else {
    raise invalid("RAKP4 response identity mismatch")
  }
  guard data[1] == 0 else { raise invalid("BMC rejected RAKP3") }
  guard data.length() == 24 else {
    raise invalid("suite 17 RAKP4 must contain 24 bytes")
  }
  slice(data, 8, 24)
}