Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
595 changes: 495 additions & 100 deletions Cargo.lock

Large diffs are not rendered by default.

20 changes: 17 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ doctest = false
crate-type = ["staticlib", "rlib"]

[features]
default = ["openssl", "xmlsec"]
openssl = ["dep:openssl", "openssl-sys", "openssl-probe"]
rustcrypto = ["rsa", "sha2", "x509-cert"]
xmlsec = ["libc", "lazy_static", "libxml"]

[build-dependencies]
pkg-config = "^0.3.17"
bindgen = "^0.69.1"

[dependencies]
openssl = "^0.10.0"
openssl-sys = "^0.9.0"
openssl-probe = "^0.1.2"
url = "^2.1.1"
quick-xml = { version = "^0.31.0", features = ["serialize"] }
serde = { version = "^1.0.0", features = ["derive"] }
Expand All @@ -41,3 +41,17 @@ data-encoding = "2.2.0"
libc = { version = "^0.2.66", optional = true }
lazy_static = { version = "^1.4.0", optional = true }
thiserror = "^1.0.40"

# openssl
openssl = { version = "0.10", optional = true }
openssl-probe = { version = "0.1", optional = true }
openssl-sys = { version = "0.9", optional = true }

# rustcrypto
rsa = { version = "0.10.0-pre.1", features = ["sha2"], optional = true }
sha2 = { version = "0.11.0-pre.3", optional = true }
x509-cert = { git = "https://github.com/RustCrypto/formats.git", rev = "809df65b20d61e88afb7f514b5cfdd3d1958a40f", features = [
"pem",
"builder",
"std",
], optional = true, default-features = false }
60 changes: 31 additions & 29 deletions src/crypto.rs → src/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
use base64::{engine::general_purpose, Engine as _};
use std::collections::HashMap;
use std::convert::TryInto;
// use std::convert::TryInto;
#[cfg(feature = "xmlsec")]
use std::ffi::CString;

use std::str::FromStr;
use thiserror::Error;

#[cfg(not(any(feature = "rustcrypto", feature = "openssl")))]
compile_error!("No crypto backend is enabled! Please enable either rustcrypto or openssl.");

#[cfg(all(feature = "rustcrypto", feature = "openssl"))]
compile_error!("Only one crypto backend may be enabled!");

pub mod rsa;
pub mod x509;

#[cfg(feature = "xmlsec")]
use crate::xmlsec::{self, XmlSecKey, XmlSecKeyFormat, XmlSecSignatureContext};
#[cfg(feature = "xmlsec")]
use libxml::parser::Parser as XmlParser;

use self::{rsa::PublicKeyLike, x509::CertificateLike};

#[cfg(feature = "xmlsec")]
const XMLNS_XML_DSIG: &str = "http://www.w3.org/2000/09/xmldsig#";
#[cfg(feature = "xmlsec")]
Expand Down Expand Up @@ -61,7 +74,7 @@ pub enum Error {
error: Box<dyn std::error::Error + Send + Sync>,
},

#[cfg(feature = "xmlsec")]
#[cfg(all(feature = "xmlsec", feature = "openssl"))]
#[error("OpenSSL error stack: {}", error)]
OpenSSLError {
#[from]
Expand Down Expand Up @@ -403,7 +416,7 @@ fn remove_unverified_elements(node: &mut libxml::tree::Node) {
#[cfg(feature = "xmlsec")]
pub(crate) fn reduce_xml_to_signed(
xml_str: &str,
certs: &[openssl::x509::X509],
certs: &Vec<x509::Certificate>,
) -> Result<String, Error> {
let mut xml = XmlParser::default().parse_string(xml_str)?;
let mut root_elem = xml.get_root_element().ok_or(Error::XmlMissingRootElement)?;
Expand All @@ -418,7 +431,7 @@ pub(crate) fn reduce_xml_to_signed(
let mut verified = false;
for openssl_key in certs {
let mut sig_ctx = XmlSecSignatureContext::new()?;
let key_data = openssl_key.to_der()?;
let key_data = openssl_key.public_key();
let key = XmlSecKey::from_memory(&key_data, XmlSecKeyFormat::CertDer)?;
sig_ctx.insert_key(key);
verified = sig_ctx.verify_node(&sig_node)?;
Expand Down Expand Up @@ -509,32 +522,30 @@ pub enum UrlVerifierError {
}

pub struct UrlVerifier {
keypair: openssl::pkey::PKey<openssl::pkey::Public>,
keypair: rsa::PublicKey,
}

impl UrlVerifier {
pub fn from_rsa_pem(public_key_pem: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
let public = openssl::rsa::Rsa::public_key_from_pem(public_key_pem)?;
let keypair = openssl::pkey::PKey::from_rsa(public)?;
let keypair =rsa::PublicKey::from_pem(public_key_pem)?;
Ok(Self { keypair })
}

pub fn from_rsa_der(public_key_der: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
let public = openssl::rsa::Rsa::public_key_from_der(public_key_der)?;
let keypair = openssl::pkey::PKey::from_rsa(public)?;
let keypair = rsa::PublicKey::from_der(public_key_der)?;
Ok(Self { keypair })
}

pub fn from_x509_cert_pem(public_cert_pem: &str) -> Result<Self, Box<dyn std::error::Error>> {
let x509 = openssl::x509::X509::from_pem(public_cert_pem.as_bytes())?;
let keypair = x509.public_key()?;
let pubkey = x509::Certificate::from_pem(public_cert_pem.as_bytes()).unwrap().public_key();
let keypair = rsa::PublicKey::from_der(pubkey)?;
Ok(Self { keypair })
}

pub fn from_x509(
public_cert: &openssl::x509::X509,
public_cert: &x509::Certificate,
) -> Result<Self, Box<dyn std::error::Error>> {
let keypair = public_cert.public_key()?;
let keypair = rsa::PublicKey::from_pem(public_cert.public_key())?;
Ok(Self { keypair })
}

Expand Down Expand Up @@ -622,7 +633,7 @@ impl UrlVerifier {
signed_url.scheme(),
signed_url.host_str().unwrap(),
)
.as_str(),
.as_str(),
)?;

// Section 3.4.4.1 of
Expand Down Expand Up @@ -654,20 +665,11 @@ impl UrlVerifier {
fn verify_signature(
&self,
data: &[u8],
#[allow(unused_variables)]
sig_alg: SigAlg,
signature: &[u8],
) -> Result<bool, Box<dyn std::error::Error>> {
let mut verifier = openssl::sign::Verifier::new(
match sig_alg {
SigAlg::RsaSha256 => openssl::hash::MessageDigest::sha256(),
_ => panic!("sig_alg is bad!"),
},
&self.keypair,
)?;

verifier.update(data)?;

Ok(verifier.verify(signature)?)
self.keypair.verify_sha256(signature, data)
}
}

Expand All @@ -680,13 +682,13 @@ mod test {
#[test]
fn test_verify_uri() {
let private_key = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/test_vectors/private.der"
env!("CARGO_MANIFEST_DIR"),
"/test_vectors/private.der"
));

let idp_metadata_xml = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/test_vectors/idp_2_metadata.xml"
env!("CARGO_MANIFEST_DIR"),
"/test_vectors/idp_2_metadata.xml"
));

let response_instant = "2014-07-17T01:01:48Z".parse::<DateTime<Utc>>().unwrap();
Expand Down
42 changes: 42 additions & 0 deletions src/crypto/rsa/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#[cfg(feature = "openssl")]
mod openssl;
#[cfg(feature = "openssl")]
pub use self::openssl::*;

#[cfg(feature = "rustcrypto")]
pub mod rustcrypto;

#[cfg(feature = "rustcrypto")]
pub use rustcrypto::*;

pub trait PrivateKeyLike
where
Self: Sized,
{
fn new(bit_size: usize) -> Result<Self, Box<dyn std::error::Error>>;

fn from_pem(pem: &str) -> Result<Self, Box<dyn std::error::Error>>;

fn from_der(der: &[u8]) -> Result<Self, Box<dyn std::error::Error>>;

fn to_der(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>>;

fn sign_sha256(&self, content_to_sign: String) -> Result<Vec<u8>, Box<dyn std::error::Error>>;
}

pub trait PublicKeyLike
where
Self: Sized,
{
fn from_pem(pem: &[u8]) -> Result<Self, Box<dyn std::error::Error>>;

fn from_der(der: &[u8]) -> Result<Self, Box<dyn std::error::Error>>;

fn to_der(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>>;

fn verify_sha256(
&self,
signature: &[u8],
data: &[u8],
) -> Result<bool, Box<dyn std::error::Error>>;
}
60 changes: 60 additions & 0 deletions src/crypto/rsa/openssl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use openssl::hash::MessageDigest;
use openssl::pkey::{PKey, Private, Public};
use openssl::rsa::Rsa;
use openssl::sign::Signer;
use openssl::sign::Verifier;

use crate::crypto::rsa::{PrivateKeyLike, PublicKeyLike};

pub type PrivateKey = Rsa<Private>;
pub type PublicKey = Rsa<Public>;

impl PrivateKeyLike for PrivateKey {
fn new(bit_size: usize) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self::generate(bit_size as u32)?)
}

fn from_pem(pem: &str) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self::private_key_from_pem(pem.as_bytes())?)
}

fn from_der(der: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self::private_key_from_der(der)?)
}

fn to_der(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
Ok(self.private_key_to_der()?)
}

fn sign_sha256(&self, content_to_sign: String) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let pkey = PKey::from_rsa(self.clone())?;
let mut signer = Signer::new(MessageDigest::sha256(), pkey.as_ref())?;
signer.update(content_to_sign.as_bytes())?;
Ok(signer.sign_to_vec()?)
}
}

impl PublicKeyLike for PublicKey {
fn from_pem(pem: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Rsa::public_key_from_pem(pem)?)
}

fn from_der(der: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Rsa::public_key_from_der(der)?)
}

fn to_der(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
Ok(self.public_key_to_der()?)
}

fn verify_sha256(
&self,
signature: &[u8],
data: &[u8],
) -> Result<bool, Box<dyn std::error::Error>> {
let pkey = PKey::from_rsa(self.clone())?;
let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey)?;
verifier.update(data)?;
Ok(verifier.verify(signature)?)
}
}
66 changes: 66 additions & 0 deletions src/crypto/rsa/rustcrypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use rsa::{
pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey},
pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey},
};

use rsa::{Pkcs1v15Sign};
use sha2::{Digest, Sha256};

use super::{PrivateKeyLike, PublicKeyLike};

pub use rsa::RsaPrivateKey as PrivateKey;
pub use rsa::RsaPublicKey as PublicKey;

impl PrivateKeyLike for PrivateKey {
fn new(bit_size: usize) -> Result<Self, Box<dyn std::error::Error>> {
let mut rng = rand::thread_rng();

Ok(PrivateKey::new(&mut rng, bit_size)?)
}

fn from_pem(pem: &str) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self::from_pkcs8_pem(pem).or_else(|_| Self::from_pkcs1_pem(pem))?)
}

fn from_der(der: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self::from_pkcs8_der(der).or_else(|_| Self::from_pkcs1_der(der))?)
}

fn to_der(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
Ok(self.to_pkcs8_der()?.to_bytes().to_vec())
}

fn sign_sha256(&self, content_to_sign: String) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let digest = Sha256::digest(content_to_sign.as_bytes());
let scheme = Pkcs1v15Sign::new::<Sha256>();
let signature = self.sign(scheme,&digest[..])?;
Ok(signature)
}
}

impl PublicKeyLike for PublicKey {
fn from_pem(pem: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self::from_public_key_pem(std::str::from_utf8(pem)?)
.or_else(|_| Self::from_pkcs1_der(pem))?)
}

fn from_der(der: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self::from_public_key_der(der).or_else(|_| Self::from_pkcs1_der(der))?)
}

fn to_der(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
Ok(self.to_public_key_der()?.as_ref().to_vec())
}

fn verify_sha256(
&self,
signature: &[u8],
data: &[u8],
) -> Result<bool, Box<dyn std::error::Error>> {
//let padding = PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA2_256));
let scheme = Pkcs1v15Sign::new::<Sha256>();
self.verify(scheme, data, signature)?;

Ok(true)
}
}
33 changes: 33 additions & 0 deletions src/crypto/x509/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::crypto::rsa;
use crate::idp::CertificateParams;

#[cfg(feature = "openssl")]
mod openssl;
#[cfg(feature = "openssl")]
pub use self::openssl::*;

#[cfg(feature = "rustcrypto")]
mod rustcrypto;

#[cfg(feature = "rustcrypto")]
pub use rustcrypto::*;

pub trait CertificateLike<Key: rsa::PrivateKeyLike>
where
Self: Sized,
{
fn new(
private_key: &Key,
params: &CertificateParams,
) -> Result<Self, Box<dyn std::error::Error>>;

fn to_vec(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>>;

fn from_der(der: &[u8]) -> Result<Self, Box<dyn std::error::Error>>;

fn public_key(&self) -> &[u8];

fn from_pem(pem: &[u8]) -> Result<Self, Box<dyn std::error::Error>>;

fn to_der(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>>;
}
Loading