//! Read and Write PEM files from RSA types
//!
//! This library will be useless after the next release of the RSA crate, which should have this
//! functionality baked in.
//!
//! ### Examples
//! ```rust
//! use rsa_pem::KeyExt;
//! use rsa::RSAPrivateKey;
//! #
//! # let mut rng = rand::thread_rng();
//! # let private_key = RSAPrivateKey::new(&mut rng, 2048).unwrap();
//!
//! let pkcs8_string = private_key.to_pem_pkcs8().unwrap();
//! let private_key = RSAPrivateKey::from_pem_pkcs8(&pkcs8_string).unwrap();
//! ```
use num_bigint_dig::{BigInt, BigUint, Sign};
use thiserror::Error;

mod private;
mod public;

const RSA_OID: [u64; 7] = [1, 2, 840, 113_549, 1, 1, 1];

/// Extensions to keys for formatting and reading PKCS1 and PKCS8 PEM strings
pub trait KeyExt {
    /// Write a PKCS8 pem string
    fn to_pem_pkcs8(&self) -> Result<String, KeyError>;

    /// Read a PKCS8 pem string
    fn from_pem_pkcs8(pem: &str) -> Result<Self, KeyError>
    where
        Self: Sized;

    /// Write a PKCS1 pem string
    fn to_pem_pkcs1(&self) -> Result<String, KeyError>;

    /// Read a PKCS1 pem string
    fn from_pem_pkcs1(pem: &str) -> Result<Self, KeyError>
    where
        Self: Sized;
}

/// Errors produced when serializing or deserializing keys
#[derive(Debug, Error)]
pub enum KeyError {
    /// The PEM wrapper has the wrong name
    #[error("Invalid key kind supplied")]
    Kind,

    /// The file isn't PEM encoded
    #[error("Key not PEM-formatted")]
    Pem,

    /// Parsing the DER bytes failed
    #[error("Error parsing key, {}", .0)]
    Parse(rsa::errors::Error),

    /// The private key's fields don't make sense
    #[error("Constructed key is invalid")]
    Validate,

    /// Failed to serialize to DER bytes
    #[error("Could not serialize key")]
    Serialize,
}

fn from_dig(biguint: &BigUint) -> num_bigint::BigUint {
    num_bigint::BigUint::from_bytes_be(&biguint.to_bytes_be())
}

fn int_from_dig(bigint: &BigInt) -> num_bigint::BigInt {
    let (sign, bytes) = bigint.to_bytes_be();
    num_bigint::BigInt::from_bytes_be(sign_from_dig(sign), &bytes)
}

fn sign_from_dig(sign: Sign) -> num_bigint::Sign {
    match sign {
        Sign::Minus => num_bigint::Sign::Minus,
        Sign::NoSign => num_bigint::Sign::NoSign,
        Sign::Plus => num_bigint::Sign::Plus,
    }
}

#[cfg(test)]
mod tests {
    use crate::KeyExt;
    use rsa::{hash::Hash, padding::PaddingScheme, PublicKey, RSAPrivateKey, RSAPublicKey};
    use sha2::{Digest, Sha256};

    static SIGNING_STRING: &[u8] = b"Hewwo, Mr Obama";

    fn padding() -> PaddingScheme {
        PaddingScheme::PKCS1v15Sign {
            hash: Some(Hash::SHA2_256),
        }
    }

    #[test]
    fn priv_can_complete_cycle_pkcs1() {
        let mut rng = rand::thread_rng();
        let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
        let hashed = Sha256::digest(SIGNING_STRING);
        let sig = rsa.sign(padding(), &hashed).unwrap();

        let string = rsa.to_pem_pkcs1().unwrap();

        let res = RSAPrivateKey::from_pem_pkcs1(&string);

        let pubkey = res.unwrap().to_public_key();
        pubkey.verify(padding(), &hashed, &sig).unwrap();
    }

    #[test]
    fn pub_can_complete_cycle_pkcs1() {
        let mut rng = rand::thread_rng();
        let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
        let hashed = Sha256::digest(SIGNING_STRING);
        let sig = rsa.sign(padding(), &hashed).unwrap();

        let rsa = rsa.to_public_key();
        let string = rsa.to_pem_pkcs1().unwrap();

        let res = RSAPublicKey::from_pem_pkcs1(&string);

        let pubkey = res.unwrap();
        pubkey.verify(padding(), &hashed, &sig).unwrap();
    }

    #[test]
    fn priv_can_complete_cycle_pkcs8() {
        let mut rng = rand::thread_rng();
        let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
        let hashed = Sha256::digest(SIGNING_STRING);
        let sig = rsa.sign(padding(), &hashed).unwrap();

        let string = rsa.to_pem_pkcs8().unwrap();

        let res = RSAPrivateKey::from_pem_pkcs8(&string);

        let pubkey = res.unwrap().to_public_key();
        pubkey.verify(padding(), &hashed, &sig).unwrap();
    }

    #[test]
    fn pub_can_complete_cycle_pkcs8() {
        let mut rng = rand::thread_rng();
        let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
        let hashed = Sha256::digest(SIGNING_STRING);
        let sig = rsa.sign(padding(), &hashed).unwrap();

        let rsa = rsa.to_public_key();
        let string = rsa.to_pem_pkcs8().unwrap();

        let res = RSAPublicKey::from_pem_pkcs8(&string);

        let pubkey = res.unwrap();
        pubkey.verify(padding(), &hashed, &sig).unwrap();
    }
}
