2020-04-21 22:30:57 +00:00
|
|
|
//! 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();
|
|
|
|
//! ```
|
2019-10-01 02:42:37 +00:00
|
|
|
use num_bigint_dig::{BigInt, BigUint, Sign};
|
2020-03-16 02:36:06 +00:00
|
|
|
use thiserror::Error;
|
2019-10-01 02:42:37 +00:00
|
|
|
|
|
|
|
mod private;
|
|
|
|
mod public;
|
|
|
|
|
|
|
|
const RSA_OID: [u64; 7] = [1, 2, 840, 113549, 1, 1, 1];
|
|
|
|
|
2020-04-21 22:30:57 +00:00
|
|
|
/// Extensions to keys for formatting and reading PKCS1 and PKCS8 PEM strings
|
2019-10-01 02:42:37 +00:00
|
|
|
pub trait KeyExt {
|
2020-04-21 22:30:57 +00:00
|
|
|
/// Write a PKCS8 pem string
|
2019-10-01 02:42:37 +00:00
|
|
|
fn to_pem_pkcs8(&self) -> Result<String, KeyError>;
|
2020-04-21 22:30:57 +00:00
|
|
|
|
|
|
|
/// Read a PKCS8 pem string
|
2019-10-01 02:42:37 +00:00
|
|
|
fn from_pem_pkcs8(pem: &str) -> Result<Self, KeyError>
|
|
|
|
where
|
|
|
|
Self: Sized;
|
2020-04-21 22:30:57 +00:00
|
|
|
|
|
|
|
/// Write a PKCS1 pem string
|
2019-10-01 02:42:37 +00:00
|
|
|
fn to_pem_pkcs1(&self) -> Result<String, KeyError>;
|
2020-04-21 22:30:57 +00:00
|
|
|
|
|
|
|
/// Read a PKCS1 pem string
|
2019-10-01 02:42:37 +00:00
|
|
|
fn from_pem_pkcs1(pem: &str) -> Result<Self, KeyError>
|
|
|
|
where
|
|
|
|
Self: Sized;
|
|
|
|
}
|
|
|
|
|
2020-04-21 22:30:57 +00:00
|
|
|
/// Errors produced when serializing or deserializing keys
|
2020-03-16 02:36:06 +00:00
|
|
|
#[derive(Clone, Debug, Error)]
|
2019-10-01 02:42:37 +00:00
|
|
|
pub enum KeyError {
|
2020-04-21 22:30:57 +00:00
|
|
|
/// The PEM wrapper has the wrong name
|
2019-12-31 03:13:18 +00:00
|
|
|
#[error("Invalid key kind supplied")]
|
2019-10-01 02:42:37 +00:00
|
|
|
Kind,
|
|
|
|
|
2020-04-21 22:30:57 +00:00
|
|
|
/// The file isn't PEM encoded
|
2019-12-31 03:13:18 +00:00
|
|
|
#[error("Key not PEM-formatted")]
|
2019-10-01 02:42:37 +00:00
|
|
|
Pem,
|
|
|
|
|
2020-04-21 22:30:57 +00:00
|
|
|
/// Parsing the DER bytes failed
|
2019-12-31 03:13:18 +00:00
|
|
|
#[error("Error parsing key, {}", .0)]
|
|
|
|
Parse(#[from] yasna::ASN1Error),
|
2019-10-01 02:42:37 +00:00
|
|
|
|
2020-04-21 22:30:57 +00:00
|
|
|
/// The private key's fields don't make sense
|
2019-12-31 03:13:18 +00:00
|
|
|
#[error("Constructed key is invalid")]
|
2019-10-01 02:42:37 +00:00
|
|
|
Validate,
|
|
|
|
|
2020-04-21 22:30:57 +00:00
|
|
|
/// Failed to serialize to DER bytes
|
2019-12-31 03:13:18 +00:00
|
|
|
#[error("Could not serialize key")]
|
2019-10-01 02:42:37 +00:00
|
|
|
Serialize,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_dig(biguint: &num_bigint::BigUint) -> num_bigint_dig::BigUint {
|
|
|
|
BigUint::from_bytes_be(&biguint.to_bytes_be())
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2020-03-17 01:26:54 +00:00
|
|
|
use rsa::{hash::Hashes, padding::PaddingScheme, PublicKey, RSAPrivateKey, RSAPublicKey};
|
|
|
|
use sha2::{Digest, Sha256};
|
|
|
|
|
|
|
|
static SIGNING_STRING: &[u8] = b"Hewwo, Mr Obama";
|
|
|
|
static HASH: Option<&Hashes> = Some(&Hashes::SHA2_256);
|
|
|
|
static PADDING: PaddingScheme = PaddingScheme::PKCS1v15;
|
2019-10-01 02:42:37 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn priv_can_complete_cycle_pkcs1() {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
2020-03-17 01:26:54 +00:00
|
|
|
let hashed = Sha256::digest(SIGNING_STRING);
|
|
|
|
let sig = rsa.sign(PADDING, HASH, &hashed).unwrap();
|
2019-10-01 02:42:37 +00:00
|
|
|
|
|
|
|
let string = rsa.to_pem_pkcs1().unwrap();
|
|
|
|
|
|
|
|
let res = RSAPrivateKey::from_pem_pkcs1(&string);
|
|
|
|
|
2020-03-17 01:26:54 +00:00
|
|
|
let pubkey = res.unwrap().to_public_key();
|
|
|
|
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
|
2019-10-01 02:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn pub_can_complete_cycle_pkcs1() {
|
|
|
|
let mut rng = rand::thread_rng();
|
2020-03-17 01:26:54 +00:00
|
|
|
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
|
|
|
let hashed = Sha256::digest(SIGNING_STRING);
|
|
|
|
let sig = rsa.sign(PADDING, HASH, &hashed).unwrap();
|
2019-10-01 02:42:37 +00:00
|
|
|
|
2020-03-17 01:26:54 +00:00
|
|
|
let rsa = rsa.to_public_key();
|
2019-10-01 02:42:37 +00:00
|
|
|
let string = rsa.to_pem_pkcs1().unwrap();
|
|
|
|
|
|
|
|
let res = RSAPublicKey::from_pem_pkcs1(&string);
|
|
|
|
|
2020-03-17 01:26:54 +00:00
|
|
|
let pubkey = res.unwrap();
|
|
|
|
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
|
2019-10-01 02:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn priv_can_complete_cycle_pkcs8() {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
2020-03-17 01:26:54 +00:00
|
|
|
let hashed = Sha256::digest(SIGNING_STRING);
|
|
|
|
let sig = rsa.sign(PADDING, HASH, &hashed).unwrap();
|
2019-10-01 02:42:37 +00:00
|
|
|
|
|
|
|
let string = rsa.to_pem_pkcs8().unwrap();
|
|
|
|
|
|
|
|
let res = RSAPrivateKey::from_pem_pkcs8(&string);
|
|
|
|
|
2020-03-17 01:26:54 +00:00
|
|
|
let pubkey = res.unwrap().to_public_key();
|
|
|
|
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
|
2019-10-01 02:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn pub_can_complete_cycle_pkcs8() {
|
|
|
|
let mut rng = rand::thread_rng();
|
2020-03-17 01:26:54 +00:00
|
|
|
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
|
|
|
let hashed = Sha256::digest(SIGNING_STRING);
|
|
|
|
let sig = rsa.sign(PADDING, HASH, &hashed).unwrap();
|
2019-10-01 02:42:37 +00:00
|
|
|
|
2020-03-17 01:26:54 +00:00
|
|
|
let rsa = rsa.to_public_key();
|
2019-10-01 02:42:37 +00:00
|
|
|
let string = rsa.to_pem_pkcs8().unwrap();
|
|
|
|
|
|
|
|
let res = RSAPublicKey::from_pem_pkcs8(&string);
|
|
|
|
|
2020-03-17 01:26:54 +00:00
|
|
|
let pubkey = res.unwrap();
|
|
|
|
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
|
2019-10-01 02:42:37 +00:00
|
|
|
}
|
|
|
|
}
|