Skip to content

Commit

Permalink
pkcs1: add From*/To* traits for RsaPrivateKey/RsaPublicKey (#540)
Browse files Browse the repository at this point in the history
Adds traits similar to the ones in the `pkcs8` crate for decoding and
encoding RSA private/public keys, including loading and saving them from
disk when the `std` feature is enabled.
  • Loading branch information
tarcieri authored Jul 25, 2021
1 parent 42711c9 commit 3964b6b
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 3 deletions.
11 changes: 8 additions & 3 deletions pkcs1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,27 @@ extern crate std;
mod error;
mod private_key;
mod public_key;
mod traits;
mod version;

#[cfg(feature = "alloc")]
mod document;

pub use der::{self, asn1::UIntBytes};

#[cfg(feature = "alloc")]
pub use crate::document::{private_key::RsaPrivateKeyDocument, public_key::RsaPublicKeyDocument};

#[cfg(feature = "pem")]
use pem_rfc7468 as pem;

pub use self::{
error::{Error, Result},
private_key::RsaPrivateKey,
public_key::RsaPublicKey,
traits::{FromRsaPrivateKey, FromRsaPublicKey},
version::Version,
};

#[cfg(feature = "alloc")]
pub use crate::{
document::{private_key::RsaPrivateKeyDocument, public_key::RsaPublicKeyDocument},
traits::{ToRsaPrivateKey, ToRsaPublicKey},
};
164 changes: 164 additions & 0 deletions pkcs1/src/traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
//! Traits for parsing objects from PKCS#1 encoded documents
use crate::{Result, RsaPrivateKey, RsaPublicKey};
use core::convert::TryFrom;

#[cfg(feature = "alloc")]
use crate::{RsaPrivateKeyDocument, RsaPublicKeyDocument};

#[cfg(feature = "pem")]
use alloc::string::String;

#[cfg(feature = "std")]
use std::path::Path;

#[cfg(any(feature = "pem", feature = "std"))]
use zeroize::Zeroizing;

/// Parse an [`RsaPrivateKey`] from a PKCS#1-encoded document.
pub trait FromRsaPrivateKey: Sized {
/// Parse the [`RsaPrivateKey`] from a PKCS#1-encoded document.
fn from_pkcs1_private_key(private_key: RsaPrivateKey<'_>) -> Result<Self>;

/// Deserialize PKCS#1 private key from ASN.1 DER-encoded data
/// (binary format).
fn from_pkcs1_der(bytes: &[u8]) -> Result<Self> {
Self::from_pkcs1_private_key(RsaPrivateKey::try_from(bytes)?)
}

/// Deserialize PKCS#1-encoded private key from PEM.
///
/// Keys in this format begin with the following:
///
/// ```text
/// -----BEGIN RSA PRIVATE KEY-----
/// ```
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
fn from_pkcs1_pem(s: &str) -> Result<Self> {
RsaPrivateKeyDocument::from_pem(s)
.and_then(|doc| Self::from_pkcs1_private_key(doc.private_key()))
}

/// Load PKCS#1 private key from an ASN.1 DER-encoded file on the local
/// filesystem (binary format).
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn read_pkcs1_der_file(path: &Path) -> Result<Self> {
RsaPrivateKeyDocument::read_der_file(path)
.and_then(|doc| Self::from_pkcs1_private_key(doc.private_key()))
}

/// Load PKCS#1 private key from a PEM-encoded file on the local filesystem.
#[cfg(all(feature = "pem", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn read_pkcs1_pem_file(path: &Path) -> Result<Self> {
RsaPrivateKeyDocument::read_pem_file(path)
.and_then(|doc| Self::from_pkcs1_private_key(doc.private_key()))
}
}

/// Parse a [`RsaPublicKey`] from a PKCS#1-encoded document.
pub trait FromRsaPublicKey: Sized {
/// Parse [`RsaPublicKey`] into a [`RsaPublicKey`].
fn from_pkcs1_public_key(public_key: RsaPublicKey<'_>) -> Result<Self>;

/// Deserialize object from ASN.1 DER-encoded [`RsaPublicKey`]
/// (binary format).
fn from_pkcs1_der(bytes: &[u8]) -> Result<Self> {
Self::from_pkcs1_public_key(RsaPublicKey::try_from(bytes)?)
}

/// Deserialize PEM-encoded [`RsaPublicKey`].
///
/// Keys in this format begin with the following:
///
/// ```text
/// -----BEGIN RSA PUBLIC KEY-----
/// ```
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
fn from_pkcs1_pem(s: &str) -> Result<Self> {
RsaPublicKeyDocument::from_pem(s)
.and_then(|doc| Self::from_pkcs1_public_key(doc.public_key()))
}

/// Load [`RsaPublicKey`] from an ASN.1 DER-encoded file on the local
/// filesystem (binary format).
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn read_pkcs1_der_file(path: &Path) -> Result<Self> {
RsaPublicKeyDocument::read_der_file(path)
.and_then(|doc| Self::from_pkcs1_public_key(doc.public_key()))
}

/// Load [`RsaPublicKey`] from a PEM-encoded file on the local filesystem.
#[cfg(all(feature = "pem", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn read_pkcs1_pem_file(path: &Path) -> Result<Self> {
RsaPublicKeyDocument::read_pem_file(path)
.and_then(|doc| Self::from_pkcs1_public_key(doc.public_key()))
}
}

/// Serialize a [`RsaPrivateKey`] to a PKCS#1 encoded document.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub trait ToRsaPrivateKey {
/// Serialize a [`RsaPrivateKeyDocument`] containing a PKCS#1-encoded private key.
fn to_pkcs1_der(&self) -> Result<RsaPrivateKeyDocument>;

/// Serialize this private key as PEM-encoded PKCS#1.
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
fn to_pkcs1_pem(&self) -> Result<Zeroizing<String>> {
Ok(self.to_pkcs1_der()?.to_pem())
}

/// Write ASN.1 DER-encoded PKCS#1 private key to the given path
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn write_pkcs1_der_file(&self, path: &Path) -> Result<()> {
self.to_pkcs1_der()?.write_der_file(path)
}

/// Write ASN.1 DER-encoded PKCS#1 private key to the given path
#[cfg(all(feature = "pem", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn write_pkcs1_pem_file(&self, path: &Path) -> Result<()> {
self.to_pkcs1_der()?.write_pem_file(path)
}
}

/// Serialize a [`RsaPublicKey`] to a PKCS#1-encoded document.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub trait ToRsaPublicKey {
/// Serialize a [`RsaPublicKeyDocument`] containing a PKCS#1-encoded public key.
fn to_pkcs1_der(&self) -> Result<RsaPublicKeyDocument>;

/// Serialize this public key as PEM-encoded PKCS#1.
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
fn to_pkcs1_pem(&self) -> Result<String> {
Ok(self.to_pkcs1_der()?.to_pem())
}

/// Write ASN.1 DER-encoded public key to the given path
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn write_pkcs1_der_file(&self, path: &Path) -> Result<()> {
self.to_pkcs1_der()?.write_der_file(path)
}

/// Write ASN.1 DER-encoded public key to the given path
#[cfg(all(feature = "pem", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn write_pkcs1_pem_file(&self, path: &Path) -> Result<()> {
self.to_pkcs1_der()?.write_pem_file(path)
}
}

0 comments on commit 3964b6b

Please sign in to comment.