From 8dc04bd060d7993058c120f5cbfa654890113614 Mon Sep 17 00:00:00 2001 From: asonix Date: Mon, 16 Mar 2020 20:26:54 -0500 Subject: [PATCH] Fix parsing PKCS8 public keys --- Cargo.toml | 6 +++++- examples/parse.rs | 20 ++++++++++++++++++++ src/lib.rs | 33 ++++++++++++++++++++++++++------- src/private.rs | 40 ++++++++++++++++++++++++++++++++++++++++ src/public.rs | 28 ++++++++++++++++++++-------- 5 files changed, 111 insertions(+), 16 deletions(-) create mode 100644 examples/parse.rs diff --git a/Cargo.toml b/Cargo.toml index 795635c..84c518d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,18 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bit-vec = "0.6" +log = "0.4" num-bigint = "0.2" num-bigint-dig = "0.6" num-traits = "0.2" pem = "0.7" rsa = "0.2.0" thiserror = "1.0.9" -yasna = { version = "0.3", features = ["num-bigint"] } +yasna = { version = "0.3", features = ["num-bigint", "bit-vec"] } [dev-dependencies] +pretty_env_logger = "0.4" anyhow = "1.0" rand = "0.7" +sha2 = "0.8" diff --git a/examples/parse.rs b/examples/parse.rs new file mode 100644 index 0000000..aaa8675 --- /dev/null +++ b/examples/parse.rs @@ -0,0 +1,20 @@ +use anyhow::Error; +use rsa::RSAPublicKey; +use rsa_pem::KeyExt; + +static ASONIX_PEM: &str = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwEvEsHqM3twoC2F3KYMQ\n9YOialfVQX4StkLvhLUwFv8qpmY7ZSHHl2TWpnzlo5iWS5Pi2vC41HUGYz9XT5G7\n4IUOyuIkjTL1FIcPJDcUFCzQjN3QZcHLPJPJVNOOOEiOk8//paOyrqJTq9ikcJDM\nJ8KTWQgk1leOxUVEN5uaQ+p9IBFbXC76+RqabfEoqLZagVMDSOfeC2uR9xZ1q5Hk\nFveRTGs84QLR7FJVvx078nszx4UQGnmP0M+0sOeRJGK17IoJmhaok1XBpP6XFQ45\nvYeIRiaFj0Pc9GNISCW70dVXKMhv+K07orQJm6PwP8USyhq4tLkq6tcPbGRqEk3Z\nXwIDAQAB\n-----END PUBLIC KEY-----\n"; + +#[derive(Clone, Debug, thiserror::Error)] +#[error("Error generating key")] +pub struct MyError; + +fn main() -> Result<(), Error> { + std::env::set_var("RUST_LOG", "debug"); + pretty_env_logger::init(); + log::info!("Parsing:\n{}", ASONIX_PEM); + RSAPublicKey::from_pem_pkcs8(ASONIX_PEM).map_err(|e| { + eprintln!("Error, {}, {:?}", e, e); + MyError + })?; + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 54f1902..44020e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,53 +59,72 @@ fn sign_from_dig(sign: Sign) -> num_bigint::Sign { #[cfg(test)] mod tests { use crate::KeyExt; - use rsa::{RSAPrivateKey, RSAPublicKey}; + 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; #[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, HASH, &hashed).unwrap(); let string = rsa.to_pem_pkcs1().unwrap(); let res = RSAPrivateKey::from_pem_pkcs1(&string); - res.unwrap(); + let pubkey = res.unwrap().to_public_key(); + pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap(); } #[test] fn pub_can_complete_cycle_pkcs1() { let mut rng = rand::thread_rng(); - let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap().to_public_key(); + let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap(); + let hashed = Sha256::digest(SIGNING_STRING); + let sig = rsa.sign(PADDING, HASH, &hashed).unwrap(); + let rsa = rsa.to_public_key(); let string = rsa.to_pem_pkcs1().unwrap(); let res = RSAPublicKey::from_pem_pkcs1(&string); - res.unwrap(); + let pubkey = res.unwrap(); + pubkey.verify(PADDING, HASH, &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, HASH, &hashed).unwrap(); let string = rsa.to_pem_pkcs8().unwrap(); let res = RSAPrivateKey::from_pem_pkcs8(&string); - res.unwrap(); + let pubkey = res.unwrap().to_public_key(); + pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap(); } #[test] fn pub_can_complete_cycle_pkcs8() { let mut rng = rand::thread_rng(); - let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap().to_public_key(); + let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap(); + let hashed = Sha256::digest(SIGNING_STRING); + let sig = rsa.sign(PADDING, HASH, &hashed).unwrap(); + let rsa = rsa.to_public_key(); let string = rsa.to_pem_pkcs8().unwrap(); let res = RSAPublicKey::from_pem_pkcs8(&string); - res.unwrap(); + let pubkey = res.unwrap(); + pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap(); } } diff --git a/src/private.rs b/src/private.rs index 9398425..cfacd14 100644 --- a/src/private.rs +++ b/src/private.rs @@ -1,3 +1,35 @@ +//! ```ignore +//! // Private-key information shall have ASN.1 type PrivateKeyInfo +//! PrivateKeyInfo ::= SEQUENCE { +//! version Version, +//! privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, +//! privateKey PrivateKey, +//! attributes [0] IMPLICIT Attributes OPTIONAL +//! } +//! +//! // version is the syntax version number, for compatibility with future revisions of the spec. +//! // It shall be 0 for this version. +//! Version ::= INTEGER +//! +//! // privateKeyAlgorithm identifies the private-key algorithm. One example of a private-key +//! // algorithm is PKCS #1's rsaEncryption [PKCS#1] +//! // +//! // A type that identifies an algorithm (by object identifier) and any associated parameters. +//! // This type is defined in [X.509](https://tools.ietf.org/html/rfc5208#ref-X.509) +//! PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier +//! +//! // privateKey is an octet string whose contents are the value of the private key. The +//! // interpretation of the contents is defined in the registration of the private-key algorithm. +//! // For an RSA private key, for example, the ocntents are a BER encodeing of a value of type +//! // RSAPrivateKey. +//! PrivateKey ::= OCTET STRING +//! +//! // attributes is a set of attributes. These are the extended information that is encrypted +//! // along with the private-key information. +//! Attributes ::= SET OF Attribute +//! ``` + +use log::debug; use num_bigint_dig::{traits::ModInverse, BigUint}; use num_traits::identities::One; use rsa::{PublicKey, RSAPrivateKey}; @@ -39,6 +71,8 @@ impl KeyExt for RSAPrivateKey { let pkey = yasna::parse_der(&data.contents, |reader| { // Read the outer DER-encoded value reader.read_sequence(|reader| { + // Read Version ::= INTEGER + debug!("Parsing algorithm ID"); let version = reader.next().read_i64()?; // PKCS#8 defines version == 0 @@ -46,6 +80,8 @@ impl KeyExt for RSAPrivateKey { return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid)); } + // Read AlgorithmIdentifier ::= AlgorithmIdentifier + debug!("Parsing oid"); let oid = reader .next() .read_sequence(|reader| reader.next().read_oid())?; @@ -55,8 +91,12 @@ impl KeyExt for RSAPrivateKey { return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid)); } + // Read PrivateKey ::= OCTET STRING + debug!("Parsing bytes"); let bytes = reader.next().read_bytes()?; + // TODO: Read Attributes ::= SET OF Attribute + parse_pkcs1(&bytes) }) })?; diff --git a/src/public.rs b/src/public.rs index 5922d10..4252da7 100644 --- a/src/public.rs +++ b/src/public.rs @@ -1,3 +1,4 @@ +use log::debug; use rsa::{PublicKey, RSAPublicKey}; use super::*; @@ -11,8 +12,11 @@ impl KeyExt for RSAPublicKey { writer.write_sequence(|writer| { writer.next().write_sequence(|writer| { writer.next().write_oid(&oid); + writer.next().write_null(); }); - writer.next().write_bytes(&bytes); + writer + .next() + .write_bitvec(&bit_vec::BitVec::from_bytes(&bytes)); }); }); @@ -34,19 +38,27 @@ impl KeyExt for RSAPublicKey { let expected_oid = yasna::models::ObjectIdentifier::from_slice(&RSA_OID); let pkey = yasna::parse_der(&data.contents, |reader| { - reader.read_sequence(|reader| { - let oid = reader - .next() - .read_sequence(|reader| reader.next().read_oid())?; + let o = reader.read_sequence(|reader| { + debug!("Parse OID"); + let oid = reader.next().read_sequence(|reader| { + // TODO: parse more in here + let oid = reader.next().read_oid()?; + reader.next().read_null()?; + Ok(oid) + })?; if oid != expected_oid { + debug!("OID was unexpected, {}", oid); return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid)); } - let bytes = reader.next().read_bytes()?; + debug!("Parse bytes"); + let bitvec = reader.next().read_bitvec()?; - parse_pkcs1(&bytes) - }) + parse_pkcs1(&bitvec.to_bytes()) + })?; + debug!("Parsed sequence"); + Ok(o) })?; Ok(pkey)