// The RSA decrypter a content-security key set is built from.
//
// The envelope is sealed with RSAES-PKCS1-v1_5, the scheme the reference
// implementation's `NewRsaDecrypter` uses: `EM = 00 || 02 || PS || 00 || M` with
// PS at least eight non-zero octets. The private operation is raw RSA, and the
// padding is stripped here because the seal is not a message the library parser
// can see.

///|
/// A content-security key: an RSA private key and the PKCS#1 v1.5 unpad the
/// envelope needs.
pub(all) struct RsaDecrypter {
  key : @rsa.PrivateKey
}

///|
/// Wrap an RSA private key as a decrypter.
pub fn RsaDecrypter::new(key : @rsa.PrivateKey) -> RsaDecrypter {
  { key, }
}

///|
/// Build a decrypter from a PKCS#1 `RSAPrivateKey` DER structure, the body of a
/// `-----BEGIN RSA PRIVATE KEY-----` block.
pub fn RsaDecrypter::from_der(der : BytesView) -> RsaDecrypter raise {
  let parsed = @key.parse_rsa_private(der)
  RsaDecrypter::new(
    @rsa.PrivateKey::new(
      parsed.modulus.to_octets(),
      parsed.public_exponent.to_octets(),
      parsed.private_exponent.to_octets(),
    ),
  )
}

///|
/// Build a decrypter from a **PEM** document: a `RSA PRIVATE KEY` or a
/// `PRIVATE KEY` (PKCS#8) block. The reader is a pure function of the text, so
/// which file to read and how stays the caller's decision.
pub fn RsaDecrypter::from_pem(pem : StringView) -> RsaDecrypter raise {
  let (label, body) = @key.dearmor(pem)
  let der = if label == "RSA PRIVATE KEY" {
    body
  } else {
    @key.parse_pkcs8(body[:])
  }
  RsaDecrypter::from_der(der[:])
}

///|
/// Strip PKCS#1 v1.5 encryption padding: `00 02 PS 00 M`, with `PS` at least
/// eight non-zero octets. A block that does not start that way is not a sealed
/// envelope, and one whose separator never comes is refused rather than read
/// past.
fn pkcs1v15_unpad(block : Bytes) -> Bytes raise {
  guard block.length() > 11 else { raise Failure("rsa block too short") }
  guard block[0].to_int() == 0 && block[1].to_int() == 2 else {
    raise Failure("rsa block is not PKCS#1 v1.5 type 2")
  }
  let mut at = 2
  while at < block.length() && block[at].to_int() != 0 {
    at = at + 1
  }
  guard at >= 10 && at < block.length() else {
    raise Failure("rsa block has no padding separator")
  }
  block[at + 1:].to_owned()
}

///|
/// The one failure a decrypter reports: the sealed block is not one.
pub(all) suberror Failure {
  Failure(String)
}

///|
pub extend RsaDecrypter with Decrypter::{decrypt}

///|
impl Decrypter for RsaDecrypter with fn decrypt(self, block) {
  pkcs1v15_unpad(self.key.rsadp(block))
}