///|
/// An algorithm identifier (RFC 5280, 4.1.1.2).
pub struct AlgorithmIdentifier {
  algorithm : @asn1.ObjectIdentifier
  /// The algorithm parameters, which are algorithm specific and may be absent.
  parameters : @asn1.Der?
}

///|
/// The display name of the algorithm.
pub fn AlgorithmIdentifier::name(self : AlgorithmIdentifier) -> String {
  algorithm_display_name(self.algorithm)
}

///|
/// A validity period (RFC 5280, 4.1.2.5).
pub struct Validity {
  not_before : @asn1.Asn1Time
  not_after : @asn1.Asn1Time
}

///|
/// A subject public key (RFC 5280, 4.1.2.7).
pub struct SubjectPublicKeyInfo {
  algorithm : AlgorithmIdentifier
  /// The BIT STRING payload, with the unused trailing bits already cleared.
  public_key : Bytes
  unused_bits : Int
}

///|
/// The number of bits in the public key BIT STRING payload.
///
/// For RSA keys this is the size of the encoded `RSAPublicKey` structure, not
/// the key size; use `rsa_modulus_bits` for that.
pub fn SubjectPublicKeyInfo::public_key_bits(
  self : SubjectPublicKeyInfo,
) -> Int {
  self.public_key.length() * 8 - self.unused_bits
}

///|
/// For an RSA key, the modulus size in bits.
///
/// The modulus lives in the PKCS#1 `RSAPublicKey` structure carried inside the
/// BIT STRING.
pub fn SubjectPublicKeyInfo::rsa_modulus_bits(
  self : SubjectPublicKeyInfo,
) -> Int raise X509Error {
  let value = @asn1.DerReader::from_bytes(self.public_key).read_single() catch {
    err => raise InvalidDer(err.to_string())
  }
  let children = value.as_sequence() catch {
    err => raise InvalidDer(err.to_string())
  }
  if children.length() == 0 {
    raise Malformed("RSAPublicKey must contain a modulus")
  }
  let modulus = children[0].as_integer() catch {
    err => raise InvalidDer(err.to_string())
  }
  modulus.bit_length()
}

///|
/// A certificate extension (RFC 5280, 4.1.2.9).
pub struct Extension {
  oid : @asn1.ObjectIdentifier
  critical : Bool
  /// The DER content of `extnValue`.
  value : Bytes
}

///|
/// Builds an extension value.
///
/// `value` is the DER content of `extnValue`, as it appears on a certificate.
pub fn Extension::new(
  oid : @asn1.ObjectIdentifier,
  value : Bytes,
  critical? : Bool = false,
) -> Extension {
  { oid, critical, value, }
}

///|
/// The display name of the extension.
pub fn Extension::name(self : Extension) -> String {
  extension_display_name(self.oid)
}

///|
/// Decodes the extension value, whose shape depends on the extension.
pub fn Extension::decode_value(self : Extension) -> @asn1.Der raise X509Error {
  @asn1.DerReader::from_bytes(self.value).read_single() catch {
    err => raise InvalidDer(err.to_string())
  }
}

///|
/// The signed body of a certificate (RFC 5280, 4.1.2).
pub struct TbsCertificate {
  version : Int
  serial_number : @bigint.BigInt
  signature_algorithm : AlgorithmIdentifier
  issuer : Name
  validity : Validity
  subject : Name
  subject_public_key_info : SubjectPublicKeyInfo
  extensions : Array[Extension]
}

///|
/// A parsed certificate (RFC 5280, 4.1).
pub struct Certificate {
  /// The original DER encoding, so a certificate can be round tripped.
  raw : Bytes
  tbs : TbsCertificate
  signature_algorithm : AlgorithmIdentifier
  signature : Bytes
}

///|
/// Whether the issuer and subject names are identical.
pub fn Certificate::is_self_signed(self : Certificate) -> Bool {
  self.tbs.issuer.to_string() == self.tbs.subject.to_string()
}

///|
/// Parses an algorithm identifier.
fn parse_algorithm_identifier(
  value : @asn1.Der,
) -> AlgorithmIdentifier raise X509Error {
  let children = value.as_sequence() catch {
    err => raise InvalidDer(err.to_string())
  }
  if children.length() == 0 || children.length() > 2 {
    raise Malformed("AlgorithmIdentifier must have one or two elements")
  }
  let algorithm = children[0].as_object_identifier() catch {
    err => raise InvalidDer(err.to_string())
  }
  {
    algorithm,
    parameters: if children.length() == 2 {
      Some(children[1])
    } else {
      None
    },
  }
}

///|
/// Renders an attribute value, falling back to the RFC 4514 `#` hexadecimal
/// form for value types that are not strings.
fn attribute_value_text(value : @asn1.Der) -> String {
  value.as_string() catch {
    _ => {
      let encoded = value.encode() catch { _ => b"" }
      "#" + @hex.encode(encoded)
    }
  }
}

///|
/// Parses an X.501 name (an RDNSequence).
fn parse_name(value : @asn1.Der) -> Name raise X509Error {
  let rdn_elements = value.as_sequence() catch {
    err => raise InvalidDer(err.to_string())
  }
  let rdns : Array[RelativeDistinguishedName] = []
  for rdn_element in rdn_elements {
    let atv_elements = rdn_element.as_sequence() catch {
      err => raise InvalidDer(err.to_string())
    }
    let attributes : Array[AttributeTypeAndValue] = []
    for atv in atv_elements {
      let parts = atv.as_sequence() catch {
        err => raise InvalidDer(err.to_string())
      }
      if parts.length() != 2 {
        raise Malformed("AttributeTypeAndValue must have two elements")
      }
      let oid = parts[0].as_object_identifier() catch {
        err => raise InvalidDer(err.to_string())
      }
      attributes.push({ oid, value: attribute_value_text(parts[1]), })
    }
    rdns.push({ attributes, })
  }
  { rdns, }
}

///|
/// Parses a validity period.
fn parse_validity(value : @asn1.Der) -> Validity raise X509Error {
  let children = value.as_sequence() catch {
    err => raise InvalidDer(err.to_string())
  }
  if children.length() != 2 {
    raise Malformed("Validity must contain exactly two times")
  }
  {
    not_before: children[0].as_time() catch {
      err => raise InvalidDer(err.to_string())
    },
    not_after: children[1].as_time() catch {
      err => raise InvalidDer(err.to_string())
    },
  }
}

///|
/// Parses a subject public key info structure.
fn parse_subject_public_key_info(
  value : @asn1.Der,
) -> SubjectPublicKeyInfo raise X509Error {
  let children = value.as_sequence() catch {
    err => raise InvalidDer(err.to_string())
  }
  if children.length() != 2 {
    raise Malformed("SubjectPublicKeyInfo must have two elements")
  }
  let algorithm = parse_algorithm_identifier(children[0])
  let (unused_bits, public_key) = children[1].as_bit_string() catch {
    err => raise InvalidDer(err.to_string())
  }
  { algorithm, public_key, unused_bits, }
}

///|
/// Parses a DER `SEQUENCE OF Extension`.
fn parse_extension_list(value : @asn1.Der) -> Array[Extension] raise X509Error {
  let items = value.as_sequence() catch {
    err => raise InvalidDer(err.to_string())
  }
  let extensions : Array[Extension] = []
  for item in items {
    let parts = item.as_sequence() catch {
      err => raise InvalidDer(err.to_string())
    }
    if parts.length() < 2 || parts.length() > 3 {
      raise Malformed("Extension must have two or three elements")
    }
    let oid = parts[0].as_object_identifier() catch {
      err => raise InvalidDer(err.to_string())
    }
    let mut critical = false
    let mut index = 1
    if parts.length() == 3 {
      critical = parts[1].as_boolean() catch {
        err => raise InvalidDer(err.to_string())
      }
      index = 2
    }
    extensions.push({
      oid,
      critical,
      value: parts[index].as_octet_string() catch {
        err => raise InvalidDer(err.to_string())
      },
    })
  }
  extensions
}

///|
/// Parses the `[3] EXPLICIT` Extensions field of a certificate.
fn parse_extensions(value : @asn1.Der) -> Array[Extension] raise X509Error {
  let list = value.as_explicit(3) catch {
    err => raise InvalidDer(err.to_string())
  }
  parse_extension_list(list)
}

///|
/// Parses the TBSCertificate.
fn parse_tbs(value : @asn1.Der) -> TbsCertificate raise X509Error {
  let children = value.as_sequence() catch {
    err => raise InvalidDer(err.to_string())
  }
  let mut index = 0
  let mut version = 1
  if children[0].tag().cls == @asn1.ContextSpecific {
    let inner = children[0].as_explicit(0) catch {
      err => raise InvalidDer(err.to_string())
    }
    let encoded = inner.as_integer() catch {
      err => raise InvalidDer(err.to_string())
    }
    version = encoded.to_int() + 1
    if version < 1 || version > 3 {
      raise Malformed("unsupported certificate version")
    }
    index = 1
  }
  if children.length() < index + 6 {
    raise Malformed("TBSCertificate is missing required fields")
  }
  let serial_number = children[index].as_integer() catch {
    err => raise InvalidDer(err.to_string())
  }
  index = index + 1
  let signature_algorithm = parse_algorithm_identifier(children[index])
  index = index + 1
  let issuer = parse_name(children[index])
  index = index + 1
  let validity = parse_validity(children[index])
  index = index + 1
  let subject = parse_name(children[index])
  index = index + 1
  let subject_public_key_info = parse_subject_public_key_info(children[index])
  index = index + 1
  let mut extensions : Array[Extension] = []
  while index < children.length() {
    let extra = children[index]
    // [1] issuerUniqueID and [2] subjectUniqueID are ignored.
    if extra.tag().cls == @asn1.ContextSpecific && extra.tag().number == 3 {
      extensions = parse_extensions(extra)
    }
    index = index + 1
  }
  {
    version,
    serial_number,
    signature_algorithm,
    issuer,
    validity,
    subject,
    subject_public_key_info,
    extensions,
  }
}

///|
/// Parses a certificate from its DER encoding.
pub fn parse_certificate(bytes : BytesView) -> Certificate raise X509Error {
  let raw = @buffer.from_bytes(bytes).to_bytes()
  let value = @asn1.DerReader::new(bytes).read_single() catch {
    err => raise InvalidDer(err.to_string())
  }
  parse_certificate_value(value, raw~)
}

///|
/// Parses a certificate from an already decoded DER value.
///
/// `raw` is the encoding that the value was decoded from, kept so the original
/// bytes can be recovered.
pub fn parse_certificate_value(
  value : @asn1.Der,
  raw? : Bytes = b"",
) -> Certificate raise X509Error {
  let children = value.as_sequence() catch {
    err => raise InvalidDer(err.to_string())
  }
  if children.length() != 3 {
    raise Malformed("Certificate must contain three elements")
  }
  let tbs = parse_tbs(children[0])
  let signature_algorithm = parse_algorithm_identifier(children[1])
  let (unused_bits, signature) = children[2].as_bit_string() catch {
    err => raise InvalidDer(err.to_string())
  }
  if unused_bits != 0 {
    raise Malformed("signature BIT STRING must have no unused bits")
  }
  { raw, tbs, signature_algorithm, signature, }
}

///|
/// Parses a certificate from PEM text.
pub fn parse_pem_certificate(text : StringView) -> Certificate raise X509Error {
  let block = @pem.decode_pem_single(text) catch {
    err => raise InvalidPem(err.to_string())
  }
  parse_certificate(block.data[:])
}

///|
/// Describes the public key algorithm and, for RSA, the modulus size.
fn key_description(info : SubjectPublicKeyInfo) -> String {
  let name = info.algorithm.name()
  let bits = if info.algorithm.algorithm.to_string() == "1.2.840.113549.1.1.1" {
    info.rsa_modulus_bits() catch {
      _ => info.public_key_bits()
    }
  } else {
    info.public_key_bits()
  }
  name + ", " + bits.to_string() + " bits"
}

///|
/// Renders a multi-line summary of the certificate, similar to the output of
/// `openssl x509 -text`.
pub fn Certificate::to_string(self : Certificate) -> String {
  let buffer = StringBuilder()
  buffer.write_string("Certificate\n")
  buffer.write_string("  Version: v")
  buffer.write_string(self.tbs.version.to_string())
  buffer.write_string("\n")
  buffer.write_string("  Serial number: ")
  buffer.write_string(self.tbs.serial_number.to_string())
  buffer.write_string("\n")
  buffer.write_string("  Signature algorithm: ")
  buffer.write_string(self.signature_algorithm.name())
  buffer.write_string(" (")
  buffer.write_string(self.signature_algorithm.algorithm.to_string())
  buffer.write_string(")\n")
  buffer.write_string("  Issuer: ")
  buffer.write_string(self.tbs.issuer.to_string())
  buffer.write_string("\n")
  buffer.write_string("  Validity\n")
  buffer.write_string("    Not before: ")
  buffer.write_string(self.tbs.validity.not_before.to_string())
  buffer.write_string("\n")
  buffer.write_string("    Not after: ")
  buffer.write_string(self.tbs.validity.not_after.to_string())
  buffer.write_string("\n")
  buffer.write_string("  Subject: ")
  buffer.write_string(self.tbs.subject.to_string())
  buffer.write_string("\n")
  buffer.write_string("  Public key: ")
  buffer.write_string(key_description(self.tbs.subject_public_key_info))
  buffer.write_string("\n")
  buffer.write_string("  Extensions (")
  buffer.write_string(self.tbs.extensions.length().to_string())
  buffer.write_string(")\n")
  for extension in self.tbs.extensions {
    buffer.write_string("    ")
    buffer.write_string(extension.name())
    buffer.write_string(" (")
    buffer.write_string(extension.oid.to_string())
    buffer.write_string(") critical=")
    buffer.write_string(if extension.critical { "true" } else { "false" })
    buffer.write_string("\n")
  }
  let alternative_names = self.subject_alt_names()
  if alternative_names.length() > 0 {
    buffer.write_string("  Subject alternative names (")
    buffer.write_string(alternative_names.length().to_string())
    buffer.write_string(")\n")
    for name in alternative_names {
      buffer.write_string("    ")
      buffer.write_string(name.kind())
      buffer.write_string(": ")
      buffer.write_string(name.to_string())
      buffer.write_string("\n")
    }
  }
  buffer.to_string()
}