From 2700d7383b5681ea749b692f17730ef82db6f184 Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Mon, 23 Oct 2023 14:29:50 -0700 Subject: [PATCH] refactor!: Use pure Rust rather than link to OpenSSL All the tests still pass, so this is at least equivalent to the previous content, but without the OpenSSL dependency. BREAKING CHANGE: This changes the type of `EncodingError::InvalidKeyError` and adds another variant. --- Cargo.toml | 6 ++++-- README.md | 20 +------------------- src/lib.rs | 36 ++++++++++++++++++++++-------------- 3 files changed, 27 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5479b7d..61b3aa1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,5 +13,7 @@ readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -openssl = { version = "0.10", features = ["vendored"] } -thiserror = "1.0.24" \ No newline at end of file +base64 = "0.21" +rsa = { version = "0.9", features = ["sha1"] } +sha1 = "0.10" +thiserror = "1.0.24" diff --git a/README.md b/README.md index dc7b013..6932d3f 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,6 @@ # AWS CloudFront Sign Utility Generating signed URLs for CloudFront links is a little more tricky than for S3. It's because signature generation for S3 URLs is handled a bit differently than CloudFront URLs. The Rusoto library is in maintenance mode and not accepting more features. Therefore we created this simple utility library to sign CloudFront URLs in Rust. -## Requirements -OpenSSL need to be installed. - -``` -# macOS -$ brew install openssl@1.1 - -# Arch Linux -$ sudo pacman -S pkg-config openssl - -# Debian and Ubuntu -$ sudo apt-get install pkg-config libssl-dev - -# Fedora -$ sudo dnf install pkg-config openssl-devel -``` - - ## Examples Getting signed cookies. @@ -45,4 +27,4 @@ let options = SignedOptions { ..Default::default() }; let signed_url = get_signed_url("https://example.com", &options).unwrap(); -``` \ No newline at end of file +``` diff --git a/src/lib.rs b/src/lib.rs index 51d72d5..d03eb1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,18 @@ -use openssl::{error::ErrorStack, hash::MessageDigest, pkey::PKey, rsa::Rsa, sign::Signer}; -use std::{ - collections::HashMap, - time::{SystemTime, UNIX_EPOCH}, - u64, -}; +use std::collections::HashMap; +use std::time::{SystemTime, UNIX_EPOCH}; + +use base64::{engine::general_purpose::STANDARD, Engine}; +use rsa::pkcs1::DecodeRsaPrivateKey; +use sha1::{Digest, Sha1}; use thiserror::Error; /// Possible errors encoding signed CloudFront URLS #[derive(Error, Debug)] pub enum EncodingError { #[error("invalid key provided")] - InvalidKeyError(#[from] ErrorStack), + InvalidKeyError(#[from] rsa::pkcs1::Error), + #[error("failed to sign sha1 digest with rsa")] + RsaError(#[from] rsa::Error), #[error("unknown error")] Unknown, } @@ -82,7 +84,7 @@ pub fn get_signed_cookie( let mut headers: HashMap = HashMap::new(); let policy = get_custom_policy(url, options); let signature = create_policy_signature(&policy, &options.private_key)?; - let policy_string = openssl::base64::encode_block(policy.as_bytes()); + let policy_string = STANDARD.encode(policy.as_bytes()); headers.insert( String::from("CloudFront-Policy"), @@ -102,11 +104,17 @@ pub fn get_signed_cookie( /// Create signature for a given policy and private key PEM-encoded PKCS#1 fn create_policy_signature(policy: &str, private_key: &str) -> Result { - let rsa = Rsa::private_key_from_pem(private_key.as_bytes())?; - let keypair = PKey::from_rsa(rsa)?; - let mut signer = Signer::new(MessageDigest::sha1(), &keypair)?; - signer.update(policy.as_bytes())?; - Ok(openssl::base64::encode_block(&signer.sign_to_vec()?)) + let rsa = rsa::RsaPrivateKey::from_pkcs1_pem(private_key)?; + + let sha1_digest = { + let mut hasher = Sha1::new(); + hasher.update(policy.as_bytes()); + hasher.finalize() + }; + + let signed = rsa.sign(rsa::Pkcs1v15Sign::new::(), &sha1_digest)?; + + Ok(STANDARD.encode(signed)) } /// Create a URL safe Base64 encoded string. @@ -140,7 +148,7 @@ pub fn get_signed_url(url: &str, options: &SignedOptions) -> Result