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

// Writing: the certificate a TLS server presents when it has nothing better โ€” a P-256 key, an
// ecdsa-with-SHA256 signature over its own TBSCertificate, a commonName.

///|
/// The ecdsa-with-SHA256 AlgorithmIdentifier (RFC 5758): a SEQUENCE of just the OID, ECDSA
/// taking no parameters.
pub fn alg_ecdsa_sha256() -> Bytes {
  @asn1.sequence([@asn1.oid([1, 2, 840, 10045, 4, 3, 2])])
}

///|
/// The id-ecPublicKey with the prime256v1 (P-256) curve AlgorithmIdentifier (RFC 5480).
pub fn alg_ec_key() -> Bytes {
  @asn1.sequence([
    @asn1.oid([1, 2, 840, 10045, 2, 1]),
    @asn1.oid([1, 2, 840, 10045, 3, 1, 7]),
  ])
}

///|
/// A Name with a single commonName attribute: RDNSequence โ†’ RelativeDistinguishedName (SET) โ†’
/// AttributeTypeAndValue (SEQUENCE of the commonName OID 2.5.4.3 and the UTF8String value).
pub fn common_name(cn : String) -> Bytes {
  @asn1.sequence([
    @asn1.set_of([
      @asn1.sequence([@asn1.oid([2, 5, 4, 3]), @asn1.utf8_string(cn)]),
    ]),
  ])
}

///|
/// A Validity: notBefore and notAfter as UTCTime `YYMMDDHHMMSSZ`.
pub fn validity_of(not_before : String, not_after : String) -> Bytes {
  @asn1.sequence([@asn1.utc_time(not_before), @asn1.utc_time(not_after)])
}

///|
/// A SubjectPublicKeyInfo for a P-256 key: the ecPublicKey/prime256v1 algorithm and the
/// uncompressed point as a BIT STRING.
pub fn spki_of(pub_key : @ecdsa.PublicKey) -> Bytes {
  @asn1.sequence([alg_ec_key(), @asn1.bit_string(pub_key.bytes())])
}

///|
/// The TBSCertificate (RFC 5280 ยง4.1.2): version v3 `[0] EXPLICIT INTEGER 2`, the serial number,
/// the signature algorithm, the issuer, the validity, the subject, and the SubjectPublicKeyInfo.
/// Self-signed, so issuer and subject are the same commonName. Extensions are omitted (a valid,
/// minimal profile).
pub fn tbs_of(
  serial : Bytes,
  cn : String,
  not_before : String,
  not_after : String,
  pub_key : @ecdsa.PublicKey,
) -> Bytes {
  @asn1.sequence([
    @asn1.explicit(0, @asn1.integer(b"\x02")),
    @asn1.integer(serial),
    alg_ecdsa_sha256(),
    common_name(cn),
    validity_of(not_before, not_after),
    common_name(cn),
    spki_of(pub_key),
  ])
}

///|
/// A self-signed X.509 certificate: build the TBSCertificate for `key`'s public key, sign its
/// DER with ES256, DER-encode the `r`/`s` signature (RFC 5280 requires the ECDSA-Sig-Value
/// SEQUENCE, not the raw concatenation), and wrap TBS + algorithm + signature in the outer
/// Certificate SEQUENCE.
pub fn self_signed(
  key : @ecdsa.PrivateKey,
  serial : Bytes,
  cn : String,
  not_before : String,
  not_after : String,
) -> Bytes {
  let pub_key = key.public()
  let tbs = tbs_of(serial, cn, not_before, not_after, pub_key)
  let raw = key.sign(tbs[:])
  let sig = @asn1.sequence([
    @asn1.integer(raw[0:32].to_owned()),
    @asn1.integer(raw[32:64].to_owned()),
  ])
  @asn1.sequence([tbs, alg_ecdsa_sha256(), @asn1.bit_string(sig)])
}