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

///|
/// An ephemeral key share for `group`: the private scalar and the public value sent in key_share.
/// A NIST scalar drawn outside the curve order is drawn again.
fn share(
  group : Int32,
  rand : (Int32) -> Bytes,
) -> (Bytes, Bytes) raise @alert.Alert {
  for _ in 0..<8 {
    let secret = rand(scalar_size(group))
    let public = public_of(group, secret) catch { _ => continue }
    return (secret, public)
  }
  raise @alert.fatal(InternalError)
}

///|
fn scalar_size(group : Int32) -> Int32 {
  if group == @ext.secp384r1 {
    48
  } else {
    32
  }
}

///|
fn public_of(group : Int32, secret : Bytes) -> Bytes raise {
  match group {
    g if g == @ext.x25519 => @x25519.public(secret[:])
    g if g == @ext.secp256r1 => @ecdh.public(secret[:], curve=P256)
    g if g == @ext.secp384r1 => @ecdh.public(secret[:], curve=P384)
    _ => raise @alert.fatal(IllegalParameter)
  }
}

///|
/// The ECDHE shared secret (RFC 8446 ยง7.4); a peer share that is off the curve or of low order
/// is an `IllegalParameter`.
fn agree(
  group : Int32,
  secret : Bytes,
  peer : Bytes,
) -> Bytes raise @alert.Alert {
  shared_of(group, secret, peer) catch {
    _ => raise @alert.fatal(IllegalParameter)
  }
}

///|
fn shared_of(group : Int32, secret : Bytes, peer : Bytes) -> Bytes raise {
  match group {
    g if g == @ext.x25519 => @x25519.shared(secret[:], peer[:])
    g if g == @ext.secp256r1 => @ecdh.shared(secret[:], peer[:], curve=P256)
    g if g == @ext.secp384r1 => @ecdh.shared(secret[:], peer[:], curve=P384)
    _ => raise @alert.fatal(IllegalParameter)
  }
}