Skip to content
Draft
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
550 changes: 500 additions & 50 deletions Cargo.lock

Large diffs are not rendered by default.

36 changes: 23 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,37 @@ build = "bindings.rs"
doctest = false

[features]
default = ["openssl"]
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.60.1"

[dependencies]
openssl = "0.10"
openssl-sys = "0.9"
openssl-probe = "0.1.2"
url = "2.1.1"
quick-xml = { version = "0.23.0", features = [ "serialize" ] }
serde = { version = "1.0", features = ["derive"] }
chrono = { version = "0.4", features = ["serde"] }
base64 = "0.13"
flate2 = "1.0"
snafu = "0.7"
rand = "0.8.4"
chrono = { version = "0.4", features = ["serde"] }
data-encoding = "2.2.0"
derive_builder = "0.11.2"
flate2 = "1.0"
lazy_static = {version = "^1.4.0", optional = true}
libc = {version = "^0.2.66", optional = true}
libxml = { version = "0.3.0", optional = true}
quick-xml = { version = "0.22.0", features = [ "serialize" ] }
rand = "0.8.4"
serde = { version = "1.0", features = ["derive"] }
snafu = "0.7"
url = "2.1.1"
uuid = { version = ">=0.8.0, <2.0.0", features = [ "v4" ] }
data-encoding = "2.2.0"
libc = {version = "^0.2.66", optional = true}
lazy_static = {version = "^1.4.0", optional = true}

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

# rustcrypto
rsa = { version = "0.6.1", optional = true }
sha2 = { version = "0.9", optional = true }
x509-cert = { version = "^0.1.0", features = [ "pem", "std" ], optional = true }
1 change: 0 additions & 1 deletion src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]

#![allow(improper_ctypes)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
65 changes: 32 additions & 33 deletions src/crypto.rs → src/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
use crate::crypto::x509::CertificateLike;
use std::collections::HashMap;
use std::convert::TryInto;
use std::ffi::CString;
use std::str::FromStr;

use snafu::Snafu;

#[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;

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

#[cfg(feature = "xmlsec")]
#[cfg(all(feature = "xmlsec", feature = "openssl"))]
#[snafu(display("OpenSSL error stack: {}", error))]
OpenSSLError {
error: openssl::error::ErrorStack,
Expand All @@ -81,7 +93,7 @@ impl From<libxml::parser::XmlParseError> for Error {
}
}

#[cfg(feature = "xmlsec")]
#[cfg(all(feature = "xmlsec", feature = "openssl"))]
impl From<openssl::error::ErrorStack> for Error {
fn from(error: openssl::error::ErrorStack) -> Self {
Error::OpenSSLError { error }
Expand Down Expand Up @@ -422,7 +434,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: &[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 @@ -436,8 +448,8 @@ pub(crate) fn reduce_xml_to_signed(
for sig_node in signature_nodes.drain(..) {
let mut sig_ctx = XmlSecSignatureContext::new()?;
let mut verified = false;
for openssl_key in certs {
let key_data = openssl_key.to_der()?;
for cert in certs {
let key_data = cert.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 @@ -528,34 +540,31 @@ pub enum UrlVerifierError {
SigAlgUnimplemented { sigalg: String },
}

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

impl UrlVerifier {
impl UrlVerifier<rsa::PublicKey> {
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::PublicKeyLike::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::PublicKeyLike::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()?;
Ok(Self { keypair })
pub fn from_x509(public_cert: &x509::Certificate) -> Result<Self, Box<dyn std::error::Error>> {
Self::from_rsa_der(public_cert.public_key())
}

pub fn from_x509(
public_cert: &openssl::x509::X509,
) -> Result<Self, Box<dyn std::error::Error>> {
let keypair = public_cert.public_key()?;
Ok(Self { keypair })
pub fn from_x509_cert_pem(public_cert_pem: &str) -> Result<Self, Box<dyn std::error::Error>> {
let x509 = x509::CertificateLike::from_der(public_cert_pem.as_bytes())?;
Self::from_x509(&x509)
}

// Signed url should look like:
Expand Down Expand Up @@ -674,20 +683,10 @@ impl UrlVerifier {
fn verify_signature(
&self,
data: &[u8],
sig_alg: SigAlg,
_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)?)
Ok(self.keypair.verify_sha256(signature, data)?)
}
}

Expand Down
41 changes: 41 additions & 0 deletions src/crypto/rsa/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#[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 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>>;
}
64 changes: 64 additions & 0 deletions src/crypto/rsa/openssl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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.to_pkcs8_der()?.as_ref().to_vec())
}

fn sign_sha256(&self, content_to_sign: String) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let pkey = PKey::from_rsa(self.0)?;

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.0.to_public_key_der()?.as_ref().to_vec())
}

fn verify_sha256(
&self,
signature: &[u8],
data: &[u8],
) -> Result<bool, Box<dyn std::error::Error>> {
let mut verifier = Verifier::new(MessageDigest::sha256(), self.0)?;

verifier.update(data)?;

Ok(verifier.verify(signature)?)
}
}
69 changes: 69 additions & 0 deletions src/crypto/rsa/rustcrypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// use rsa::pkcs1::{TryFrom, RsaPrivateKey as FromRsaPrivateKey, RsaPublicKey as FromRsaPublicKey};
// use rsa::pkcs8::PublicKey;
use rsa::{
pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey},
pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey},
};

use rsa::{Hash, PaddingScheme, PublicKey as FromPublicKey};
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()?.as_ref().to_vec())
}

fn sign_sha256(&self, content_to_sign: String) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let padding = PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA2_256));
let hashed = Sha256::digest(content_to_sign.as_bytes());
let signature = self.sign(padding, &hashed[..])?;

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));

self.verify(padding, data, signature)?;

Ok(true)
}
}
Loading