///|
/// The smallest modulus this package will accept.
///
/// 1024-bit RSA is considered broken for signatures and 2048 is the current
/// floor in every serious guideline. Refusing a short key is a security control
/// in its own right: a key that is too small verifies signatures perfectly well
/// and offers no protection.
pub let minimum_modulus_bits : Int32 = 2048

///|
/// An RSA public key that verifies RSASSA-PKCS1-v1_5 signatures over SHA-256.
pub struct PublicKey {
  modulus : @bigint.BigInt
  exponent : @bigint.BigInt
  /// Length of the modulus in bytes. This is `k` in RFC 8017 and fixes the
  /// exact length every signature and encoded message must have.
  size : Int32
}

///|
/// Parses a PEM-encoded RSA public key.
///
/// Accepts SubjectPublicKeyInfo (`PUBLIC KEY`, produced by `openssl pkey -pubout`)
/// and PKCS#1 (`RSA PUBLIC KEY`). Private keys and certificates are not accepted.
pub fn PublicKey::parse(text : String) -> PublicKey raise KeyError {
  let block = @pem.decode_pem_single(text[:]) catch {
    error => raise Malformed(detail=error.to_string())
  }
  let (modulus, exponent) = parse_parameters(block) catch {
    error => raise Malformed(detail=error.to_string())
  }
  guard modulus.compare_int(0) > 0 else {
    raise Malformed(detail="RSA modulus must be positive")
  }
  let bits = modulus.bit_length()
  guard bits >= minimum_modulus_bits else {
    raise ModulusTooSmall(bits~, minimum=minimum_modulus_bits)
  }
  // e = 1 would make the signature equal to the encoded message, so anyone
  // could produce one. Even exponents are not valid RSA exponents at all.
  guard exponent.compare_int(3) >= 0 else {
    raise InvalidExponent(detail="must be at least 3")
  }
  guard exponent.compare(modulus) < 0 else {
    raise InvalidExponent(detail="must be smaller than the modulus")
  }
  guard exponent.is_odd() else { raise InvalidExponent(detail="must be odd") }
  PublicKey::{ modulus, exponent, size: (bits + 7) / 8, }
}

///|
/// Uses the DER library for framing and integer decoding; this layer only
/// selects RSA and checks the public-key structure.
fn parse_parameters(
  block : @pem.PemBlock,
) -> (@bigint.BigInt, @bigint.BigInt) raise @asn1.DerError {
  let encoded = match block.label {
    "RSA PUBLIC KEY" => block.data
    "PUBLIC KEY" => {
      let fields = @asn1.DerReader::from_bytes(block.data)
        .read_single()
        .as_sequence()
      guard fields is [algorithm, key] else {
        raise @asn1.InvalidValue("expected SubjectPublicKeyInfo")
      }
      let algorithm = algorithm.as_sequence()
      guard algorithm is [oid, parameters] else {
        raise @asn1.InvalidValue(
          "expected RSA algorithm identifier and NULL parameters",
        )
      }
      let oid = oid.as_object_identifier().to_string()
      guard oid == "1.2.840.113549.1.1.1" else {
        raise @asn1.InvalidValue("unsupported public key algorithm: \{oid}")
      }
      parameters.as_null()
      let (unused_bits, encoded) = key.as_bit_string()
      guard unused_bits == 0 else {
        raise @asn1.InvalidValue("RSA public key must contain complete octets")
      }
      encoded
    }
    _ => raise @asn1.InvalidValue("expected PUBLIC KEY or RSA PUBLIC KEY PEM")
  }
  let fields = @asn1.DerReader::from_bytes(encoded).read_single().as_sequence()
  guard fields is [modulus, exponent] else {
    raise @asn1.InvalidValue("expected RSA modulus and public exponent")
  }
  (modulus.as_integer(), exponent.as_integer())
}

///|
/// Returns the modulus length in bytes.
pub fn PublicKey::size(self : PublicKey) -> Int32 {
  self.size
}

///|
/// Returns the modulus length in bits.
pub fn PublicKey::bits(self : PublicKey) -> Int32 {
  self.modulus.bit_length()
}

///|
fn @bigint.BigInt::is_odd(self : @bigint.BigInt) -> Bool {
  (self % @bigint.BigInt::from_int(2)).compare_int(0) != 0
}