rsa-pem/src/lib.rs
2020-07-25 09:31:04 -05:00

161 lines
4.6 KiB
Rust

//! 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();
}
}