// The three extensions RFC 8446 §9.2 makes mandatory alongside key_share, and the negotiation
// they drive. supported_versions (§4.2.1) is what actually says "TLS 1.3": the ServerHello's
// legacy_version field is frozen at 0x0303 for middleboxes, and a client that does not find
// 0x0304 in this extension reads the message as TLS 1.2 and gives up. supported_groups (§4.2.7)
// and signature_algorithms (§4.2.3) say which key exchange and which certificate signature the
// client will accept, so a server that assumes x25519 and ES256 rather than reading them will
// sooner or later pick something the client cannot use. `tls13_negotiate` reads all four
// extensions off a ClientHello and either settles the handshake's parameters, asks for a
// HelloRetryRequest, or raises the alert §6 names for that failure.

///|
/// The supported_groups extension type (RFC 8446 §4.2.7).
pub let tls_ext_supported_groups : Int = 0x000a

///|
/// The signature_algorithms extension type (RFC 8446 §4.2.3).
pub let tls_ext_signature_algorithms : Int = 0x000d

///|
/// The supported_versions extension type (RFC 8446 §4.2.1).
pub let tls_ext_supported_versions : Int = 0x002b

///|
/// The version TLS 1.3 leaves in the legacy_version field of a ClientHello and ServerHello
/// (RFC 8446 §4.1.2, §4.1.3): TLS 1.2's number, kept so middleboxes pass the record through.
pub let tls_legacy_version : Int = 0x0303

///|
/// TLS 1.3's version number (RFC 8446 §4.2.1), which travels in supported_versions rather than
/// in legacy_version.
pub let tls_version_13 : Int = 0x0304

///|
/// The secp256r1 named group (RFC 8446 §4.2.7).
pub let tls_group_secp256r1 : Int = 0x0017

///|
/// The secp384r1 named group (RFC 8446 §4.2.7).
pub let tls_group_secp384r1 : Int = 0x0018

///|
/// The rsa_pss_rsae_sha256 SignatureScheme (RFC 8446 §4.2.3).
pub let tls_sig_rsa_pss_rsae_sha256 : Int = 0x0804

///|
/// Encode a two-byte-length-prefixed list of 16-bit codes — the body shape supported_groups
/// and signature_algorithms share.
fn tls_encode_u16_list(values : Array[Int]) -> Bytes {
  let inner = Buffer()
  for v in values {
    ks_write_u16(inner, v)
  }
  let body = inner.to_bytes()
  let buf = Buffer()
  ks_write_u16(buf, body.length())
  buf.write_bytes(body[:])
  buf.to_bytes()
}

///|
/// Decode a two-byte-length-prefixed list of 16-bit codes, stopping at the declared length or
/// a truncated entry.
fn tls_decode_u16_list(view : BytesView) -> Array[Int] {
  let values : Array[Int] = []
  if view.length() < 2 {
    return values
  }
  let declared = 2 + ks_read_u16(view, 0)
  let end = if declared < view.length() { declared } else { view.length() }
  let mut off = 2
  while off + 2 <= end {
    values.push(ks_read_u16(view, off))
    off = off + 2
  }
  values
}

///|
/// Encode a ClientHello's supported_versions body (RFC 8446 §4.2.1): a one-byte length, then
/// each version as two bytes, most preferred first.
pub fn tls_encode_supported_versions_client(versions : Array[Int]) -> Bytes {
  let inner = Buffer()
  for v in versions {
    ks_write_u16(inner, v)
  }
  let body = inner.to_bytes()
  let buf = Buffer()
  buf.write_byte((body.length() & 0xff).to_byte())
  buf.write_bytes(body[:])
  buf.to_bytes()
}

///|
/// Decode a ClientHello's supported_versions body into the versions it offers, in order.
pub fn tls_decode_supported_versions_client(view : BytesView) -> Array[Int] {
  let versions : Array[Int] = []
  if view.length() < 1 {
    return versions
  }
  let declared = 1 + view[0].to_int()
  let end = if declared < view.length() { declared } else { view.length() }
  let mut off = 1
  while off + 2 <= end {
    versions.push(ks_read_u16(view, off))
    off = off + 2
  }
  versions
}

///|
/// Encode a ServerHello's supported_versions body (RFC 8446 §4.2.1): the selected version
/// alone, with no list prefix.
pub fn tls_encode_supported_versions_server(version : Int) -> Bytes {
  let buf = Buffer()
  ks_write_u16(buf, version)
  buf.to_bytes()
}

///|
/// The version a ServerHello's supported_versions body selected, or `None` if it is not two
/// bytes.
pub fn tls_decode_supported_versions_server(view : BytesView) -> Int? {
  if view.length() != 2 {
    return None
  }
  Some(ks_read_u16(view, 0))
}

///|
/// A ClientHello supported_versions extension offering `versions`.
pub fn tls_supported_versions_extension(versions : Array[Int]) -> TlsExtension {
  {
    ext_type: tls_ext_supported_versions,
    data: tls_encode_supported_versions_client(versions),
  }
}

///|
/// A ServerHello or HelloRetryRequest supported_versions extension naming `version`.
pub fn tls_selected_version_extension(version : Int) -> TlsExtension {
  {
    ext_type: tls_ext_supported_versions,
    data: tls_encode_supported_versions_server(version),
  }
}

///|
/// The versions a ClientHello's supported_versions extension offers (empty if it has none).
pub fn tls_client_hello_versions(
  extensions : Array[TlsExtension],
) -> Array[Int] {
  match tls_find_extension(extensions, tls_ext_supported_versions) {
    Some(ext) => tls_decode_supported_versions_client(ext.data[:])
    None => []
  }
}

///|
/// The version a ServerHello selected (RFC 8446 §4.2.1) — the field a client must read
/// instead of legacy_version — or `None` if it carries no supported_versions.
pub fn tls_server_hello_version(sh : TlsServerHello) -> Int? {
  match tls_find_extension(sh.extensions, tls_ext_supported_versions) {
    Some(ext) => tls_decode_supported_versions_server(ext.data[:])
    None => None
  }
}

///|
/// Encode a supported_groups body (RFC 8446 §4.2.7): a two-byte list length, then each named
/// group as two bytes, most preferred first.
pub fn tls_encode_supported_groups(groups : Array[Int]) -> Bytes {
  tls_encode_u16_list(groups)
}

///|
/// Decode a supported_groups body into the named groups it offers, in order.
pub fn tls_decode_supported_groups(view : BytesView) -> Array[Int] {
  tls_decode_u16_list(view)
}

///|
/// A supported_groups extension offering `groups`.
pub fn tls_supported_groups_extension(groups : Array[Int]) -> TlsExtension {
  {
    ext_type: tls_ext_supported_groups,
    data: tls_encode_supported_groups(groups),
  }
}

///|
/// The named groups a ClientHello offers for key exchange (empty if it has no
/// supported_groups).
pub fn tls_client_hello_groups(extensions : Array[TlsExtension]) -> Array[Int] {
  match tls_find_extension(extensions, tls_ext_supported_groups) {
    Some(ext) => tls_decode_supported_groups(ext.data[:])
    None => []
  }
}

///|
/// Encode a signature_algorithms body (RFC 8446 §4.2.3): a two-byte list length, then each
/// SignatureScheme as two bytes, most preferred first.
pub fn tls_encode_signature_algorithms(schemes : Array[Int]) -> Bytes {
  tls_encode_u16_list(schemes)
}

///|
/// Decode a signature_algorithms body into the schemes it offers, in order.
pub fn tls_decode_signature_algorithms(view : BytesView) -> Array[Int] {
  tls_decode_u16_list(view)
}

///|
/// A signature_algorithms extension offering `schemes`.
pub fn tls_signature_algorithms_extension(schemes : Array[Int]) -> TlsExtension {
  {
    ext_type: tls_ext_signature_algorithms,
    data: tls_encode_signature_algorithms(schemes),
  }
}

///|
/// The signature schemes a ClientHello will accept on a certificate (empty if it has no
/// signature_algorithms).
pub fn tls_client_hello_signature_algorithms(
  extensions : Array[TlsExtension],
) -> Array[Int] {
  match tls_find_extension(extensions, tls_ext_signature_algorithms) {
    Some(ext) => tls_decode_signature_algorithms(ext.data[:])
    None => []
  }
}

///|
/// A ServerHello carrying the supported_versions RFC 8446 §9.2 requires, ahead of
/// `extensions`. The message's legacy_version stays 0x0303 — `encode_server_hello` writes it —
/// because §4.1.3 puts the negotiated version here instead.
pub fn tls13_server_hello(
  random : Bytes,
  session_id : Bytes,
  cipher_suite : Int,
  extensions : Array[TlsExtension],
) -> TlsServerHello {
  {
    random,
    session_id,
    cipher_suite,
    extensions: [tls_selected_version_extension(tls_version_13), ..extensions],
  }
}

///|
/// The named groups this build can run a key exchange for. `x25519.mbt` is the only group
/// implemented, so offering more would be a lie the handshake could not honour.
pub fn tls13_groups() -> Array[Int] {
  [tls_group_x25519]
}

///|
/// The signature schemes this build can sign a CertificateVerify with. `ecdsa.mbt` implements
/// P-256 with SHA-256 and nothing else.
pub fn tls13_signature_schemes() -> Array[Int] {
  [tls_sig_ecdsa_secp256r1_sha256]
}

///|
/// The x25519 public key length (RFC 7748 §5): a Montgomery-u coordinate.
let x25519_key_length : Int = 32

///|
/// What a server settled on after reading a ClientHello.
pub(all) enum TlsNegotiation {
  /// The client offered a group this build runs and a key share for it: the group, the
  /// client's public key, and the scheme the CertificateVerify will sign under.
  Negotiated(group~ : Int, client_key~ : Bytes, scheme~ : Int)
  /// The client offered a group this build runs but no key share for it, so it has to send
  /// the ClientHello again with one (RFC 8446 §4.1.4).
  Retry(Int)
} derive(Eq, Debug)

///|
/// The first of `offered` that also appears in `ours`, honouring the client's preference order
/// as RFC 8446 §4.2.7 allows a server to.
fn tls_first_common(offered : Array[Int], ours : Array[Int]) -> Int? {
  offered.iter().find_first(fn(v) { ours.contains(v) })
}

///|
/// Negotiate a decoded ClientHello (RFC 8446 §4.1.1): check it offers TLS 1.3, pick the first
/// group and signature scheme it lists that this build runs, and take its key share for the
/// chosen group. Raises the alert §6 names for each way that fails — a missing mandatory
/// extension, a client that does not speak 1.3, nothing in common, or a key share that
/// contradicts the rest of the message — and returns `Retry` when the chosen group is one the
/// client offered but sent no share for.
pub fn tls13_negotiate(ch : TlsClientHello) -> TlsNegotiation raise TlsAlert {
  // RFC 8446 §9.2: a ClientHello missing any of these cannot be a 1.3 handshake at all.
  guard tls_find_extension(ch.extensions, tls_ext_supported_versions) is Some(_) else {
    raise tls_fatal(tls_alert_missing_extension)
  }
  guard tls_find_extension(ch.extensions, tls_ext_supported_groups) is Some(_) else {
    raise tls_fatal(tls_alert_missing_extension)
  }
  guard tls_find_extension(ch.extensions, tls_ext_signature_algorithms)
    is Some(_) else {
    raise tls_fatal(tls_alert_missing_extension)
  }
  guard tls_find_extension(ch.extensions, tls_ext_key_share) is Some(_) else {
    raise tls_fatal(tls_alert_missing_extension)
  }
  if !tls_client_hello_versions(ch.extensions).contains(tls_version_13) {
    raise tls_fatal(tls_alert_protocol_version)
  }
  let offered_groups = tls_client_hello_groups(ch.extensions)
  guard tls_first_common(offered_groups, tls13_groups()) is Some(group) else {
    raise tls_fatal(tls_alert_handshake_failure)
  }
  guard tls_first_common(
      tls_client_hello_signature_algorithms(ch.extensions),
      tls13_signature_schemes(),
    )
    is Some(scheme) else {
    raise tls_fatal(tls_alert_handshake_failure)
  }
  let shares = tls_client_hello_key_shares(ch.extensions)
  // §4.2.8: a share for a group the client did not list in supported_groups contradicts its
  // own offer, and a server that notices must say so rather than use it.
  for s in shares {
    if !offered_groups.contains(s.0) {
      raise tls_fatal(tls_alert_illegal_parameter)
    }
  }
  match shares.iter().find_first(fn(s) { s.0 == group }) {
    Some((_, key)) => {
      if group == tls_group_x25519 && key.length() != x25519_key_length {
        raise tls_fatal(tls_alert_illegal_parameter)
      }
      Negotiated(group~, client_key=key, scheme~)
    }
    // §4.1.4: the group is usable, only the share is missing — worth one more round trip.
    None => Retry(group)
  }
}

///|
/// Negotiate a raw ClientHello handshake message: frame it, decode it, and run
/// `tls13_negotiate`. A message that is not a well-formed ClientHello raises decode_error
/// (RFC 8446 §6.2) rather than vanishing into a `None`.
pub fn tls13_negotiate_client_hello(
  message : Bytes,
) -> TlsNegotiation raise TlsAlert {
  guard tls_parse_handshake(message[:]) is Some((msg_type, body)) else {
    raise tls_fatal(tls_alert_decode_error)
  }
  if msg_type != tls_client_hello {
    raise tls_fatal(tls_alert_unexpected_message)
  }
  guard decode_client_hello(body[:]) is Some(ch) else {
    raise tls_fatal(tls_alert_decode_error)
  }
  tls13_negotiate(ch)
}