// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

// What is left of TLS here, now that the protocol is `moontls`.
//
// Two things a protocol library will not do, because doing them would mean
// holding a key or choosing an algorithm:
//
//   * the ECDHE that turns a peer's key share into a handshake secret, which
//     joins `mooncrypt`'s x25519 to `moontls`' key schedule;
//   * signing and checking a CertificateVerify, which joins `mooncrypt`'s
//     ECDSA to the signed content `moontls` frames.
//
// Both are this server's business: it is the one with the key.

///|
/// The ephemeral x25519 public key for a private scalar (RFC 7748 §5).
pub fn tls13_x25519_public(private_key : Bytes) -> Bytes {
  x25519_raw(private_key, @x25519.base)
}

///|
/// The x25519 shared secret with a peer's public key.
///
/// A low-order peer share makes the secret all zeroes, which `mooncrypt`
/// refuses; here that becomes an empty answer, because the handshake's response
/// to it is an alert rather than a crash.
pub fn tls13_ecdhe_shared(private_key : Bytes, peer_public : Bytes) -> Bytes {
  x25519_raw(private_key, peer_public)
}

///|
/// The Handshake Secret from our scalar and the peer's key share: run the ECDHE,
/// then RFC 8446 §7.1's ladder.
pub fn tls13_handshake_secret_from_ecdhe(
  private_key : Bytes,
  peer_public : Bytes,
) -> Bytes {
  let shared = tls13_ecdhe_shared(private_key, peer_public)
  @keys.handshake(@keys.early()[:], shared[:])
}

///|
/// A server's handshake traffic secret from the ClientHello it received: pull
/// the client's x25519 `key_share` and run the ECDHE with `server_private` over
/// the ClientHello..ServerHello transcript.
///
/// `None` if the message is not a ClientHello or offers no x25519 share — the
/// case a server answers with a HelloRetryRequest rather than a secret.
pub fn tls13_server_hs_secret_from_client_hello(
  client_hello : Bytes,
  server_private : Bytes,
  transcript_hash : Bytes,
) -> Bytes? {
  match tls13_client_hello_x25519(client_hello) {
    Some(peer) =>
      Some(
        tls13_ecdhe_handshake_traffic_secret(
          server_private, peer, transcript_hash, false,
        ),
      )
    None => None
  }
}

///|
/// Sign a CertificateVerify over the transcript with an ES256 key
/// (RFC 8446 §4.4.3): the message body, its scheme and signature framed by
/// `moontls`, over the signed content `moontls` builds.
pub fn tls13_certificate_verify_sign(
  key : @ecdsa.PrivateKey,
  context : String,
  transcript_hash : Bytes,
) -> Bytes {
  let signature = ecdsa_sign(@msg.signed(context, transcript_hash[:]), key)
  @msg.certificate_verify(signature[:], @ext.ecdsa_secp256r1_sha256)
}

///|
/// Check a CertificateVerify body against the peer's ES256 public key.
///
/// False on a scheme this server does not verify, a malformed body, or a
/// signature that does not check out — one answer for every way of being wrong,
/// because which way it was wrong is not the peer's business.
pub fn tls13_certificate_verify_check(
  key : @ecdsa.PublicKey,
  context : String,
  transcript_hash : Bytes,
  body : Bytes,
) -> Bool {
  guard @msg.read_certificate_verify(body[:]) is Some((scheme, signature)) else {
    return false
  }
  if scheme != @ext.ecdsa_secp256r1_sha256 {
    return false
  }
  ecdsa_verify(@msg.signed(context, transcript_hash[:]), signature, key)
}

///|
/// A QUIC server's EncryptedExtensions body: the negotiated ALPN protocol and
/// the server's transport parameters.
///
/// RFC 9001 §8.2 makes `quic_transport_parameters` mandatory for a QUIC server,
/// and the parameters themselves are QUIC's — `moontls` carries the extension
/// without reading it, so encoding them is this side's job.
pub fn tls13_quic_encrypted_extensions(
  alpn : String,
  params : Array[@conn.Param],
) -> Bytes {
  let alpn_ext : @ext.Ext = { kind: Alpn, data: @ext.protocols([alpn][:]), }
  let quic_ext : @ext.Ext = {
    kind: QuicTransportParameters,
    data: @conn.encode(params),
  }
  @msg.encrypted_extensions([alpn_ext, quic_ext][:])
}

///|
/// The QUIC transport parameters an extension list carries, in order.
///
/// Empty when the extension is absent or its block is truncated: a QUIC
/// endpoint treats a missing block as no parameters and falls back to the
/// defaults RFC 9000 §18.2 gives.
pub fn tls_hello_quic_transport_params(
  extensions : Array[@ext.Ext],
) -> Array[@conn.Param] {
  match @ext.find(extensions[:], QuicTransportParameters) {
    Some(e) =>
      match @conn.decode(e.data[:]) {
        Some(params) => params
        None => []
      }
    None => []
  }
}

///|
/// A handshake traffic secret straight from an ECDHE exchange and a transcript.
///
/// `is_client` picks which side's secret: the two are derived from the same
/// handshake secret under different labels (RFC 8446 §7.1), so one call serves
/// both ends of a test and both ends of a connection.
pub fn tls13_ecdhe_handshake_traffic_secret(
  private_key : Bytes,
  peer_public : Bytes,
  transcript_hash : Bytes,
  is_client : Bool,
) -> Bytes {
  let handshake = tls13_handshake_secret_from_ecdhe(private_key, peer_public)
  if is_client {
    @keys.client_handshake(handshake[:], transcript_hash[:])
  } else {
    @keys.server_handshake(handshake[:], transcript_hash[:])
  }
}

///|
/// The x25519 public key a ClientHello's `key_share` offers, or `None` when it
/// carries none for that group.
pub fn tls13_client_hello_x25519(client_hello : Bytes) -> Bytes? {
  guard @msg.unframe(client_hello[:]) is Some((kind, body)) else { return None }
  if kind != ClientHello {
    return None
  }
  guard @msg.read_hello(body[:]) is Some(ch) else { return None }
  guard @ext.find(ch.extensions[:], KeyShare) is Some(e) else { return None }
  for share in @ext.read_shares(e.data[:]) {
    if share.0 == @ext.x25519 {
      return Some(share.1)
    }
  }
  None
}

///|
/// Both handshake traffic secrets from a ClientHello's key share: the server's
/// and the client's, in that order.
///
/// `None` when the ClientHello offers no x25519 share, which is the one case a
/// server has to answer with a HelloRetryRequest rather than a secret.
pub fn tls13_ecdhe_secrets_from_client_hello(
  client_hello : Bytes,
  server_private : Bytes,
  transcript_hash : Bytes,
) -> (Bytes, Bytes)? {
  match tls13_client_hello_x25519(client_hello) {
    Some(peer) =>
      Some(
        (
          tls13_ecdhe_handshake_traffic_secret(
            server_private, peer, transcript_hash, false,
          ),
          tls13_ecdhe_handshake_traffic_secret(
            server_private, peer, transcript_hash, true,
          ),
        ),
      )
    None => None
  }
}

///|
/// The group and key a ServerHello's `key_share` selected.
pub fn tls_server_hello_key_share(sh : @msg.Server) -> (Int, Bytes)? {
  match @ext.find(sh.extensions[:], KeyShare) {
    Some(e) => @ext.read_selected_share(e.data[:])
    None => None
  }
}

///|
/// The single ALPN protocol a server's extension list selected, or `None`.
pub fn tls_selected_alpn(extensions : Array[@ext.Ext]) -> String? {
  match @ext.find(extensions[:], Alpn) {
    Some(e) => {
      let names = @ext.read_protocols(e.data[:])
      if names.length() == 1 {
        Some(names[0])
      } else {
        None
      }
    }
    None => None
  }
}