From 2cbe4864c36c2b775756b851486a93427081c9aa Mon Sep 17 00:00:00 2001 From: asonix Date: Fri, 4 Aug 2023 18:34:42 -0500 Subject: [PATCH] Switch to ring for crypto --- Cargo.lock | 8 ++++---- Cargo.toml | 7 ++++--- src/config.rs | 3 +-- src/error.rs | 11 ++++++---- src/middleware/verifier.rs | 21 +++++++++---------- src/requests.rs | 41 +++++++++++++++++++++++++------------- 6 files changed, 54 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a96ba69..92028b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -423,6 +423,7 @@ dependencies = [ "pin-project-lite", "quanta", "rand", + "ring", "rsa", "rsa-magic-public-key", "ructe", @@ -1460,9 +1461,9 @@ dependencies = [ [[package]] name = "http-signature-normalization-actix" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7978cadb8c73ab0d7115a4260e8a64188212ce44d11f0b0109c4beebc6bbb5c9" +checksum = "f16b1af0de27fe75ea5c1da8da1ce826274cc623bda49eb5e9395aa4ea0b34d4" dependencies = [ "actix-http", "actix-rt", @@ -1471,7 +1472,7 @@ dependencies = [ "base64 0.13.1", "futures-util", "http-signature-normalization", - "sha2", + "ring", "thiserror", "tokio", "tracing", @@ -2743,7 +2744,6 @@ dependencies = [ "pkcs1", "pkcs8", "rand_core", - "sha2", "signature", "spki", "subtle", diff --git a/Cargo.toml b/Cargo.toml index 46efd66..0e6c638 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,8 @@ opentelemetry-otlp = "0.12" pin-project-lite = "0.2.9" quanta = "0.11.0" rand = "0.8" -rsa = { version = "0.9", features = ["sha2"] } +ring = "0.16.20" +rsa = { version = "0.9" } rsa-magic-public-key = "0.8.0" rustls = "0.20.7" rustls-pemfile = "1.0.1" @@ -89,9 +90,9 @@ default-features = false features = ["background-jobs-actix", "error-logging"] [dependencies.http-signature-normalization-actix] -version = "0.10.0" +version = "0.10.1" default-features = false -features = ["client", "server", "sha-2"] +features = ["client", "server", "ring"] [dependencies.tracing-actix-web] version = "0.7.5" diff --git a/src/config.rs b/src/config.rs index 3cbf79d..9dfa589 100644 --- a/src/config.rs +++ b/src/config.rs @@ -11,8 +11,7 @@ use activitystreams::{ }, }; use config::Environment; -use http_signature_normalization_actix::prelude::VerifyDigest; -use rsa::sha2::{Digest, Sha256}; +use http_signature_normalization_actix::{digest::ring::Sha256, prelude::VerifyDigest}; use rustls::{Certificate, PrivateKey}; use std::{ io::BufReader, diff --git a/src/error.rs b/src/error.rs index d03c995..e009a7c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -81,6 +81,9 @@ pub(crate) enum ErrorKind { #[error("Couldn't encode public key, {0}")] Spki(#[from] rsa::pkcs8::spki::Error), + #[error("Couldn't sign request")] + SignRequest, + #[error("Couldn't parse IRI, {0}")] ParseIri(#[from] activitystreams::iri_string::validate::Error), @@ -105,11 +108,11 @@ pub(crate) enum ErrorKind { #[error("Couldn't sign digest")] Signature(#[from] rsa::signature::Error), - #[error("Couldn't read signature")] - ReadSignature(rsa::signature::Error), - #[error("Couldn't verify signature")] - VerifySignature(rsa::signature::Error), + VerifySignature, + + #[error("Failed to encode key der")] + DerEncode, #[error("Couldn't parse the signature header")] HeaderValidation(#[from] actix_web::http::header::InvalidHeaderValue), diff --git a/src/middleware/verifier.rs b/src/middleware/verifier.rs index f5cfad7..8fa8bb0 100644 --- a/src/middleware/verifier.rs +++ b/src/middleware/verifier.rs @@ -8,10 +8,7 @@ use crate::{ use activitystreams::{base::BaseExt, iri, iri_string::types::IriString}; use base64::{engine::general_purpose::STANDARD, Engine}; use http_signature_normalization_actix::{prelude::*, verify::DeprecatedAlgorithm, Spawn}; -use rsa::{ - pkcs1v15::Signature, pkcs1v15::VerifyingKey, pkcs8::DecodePublicKey, sha2::Sha256, - signature::Verifier, RsaPublicKey, -}; +use rsa::{pkcs1::EncodeRsaPublicKey, pkcs8::DecodePublicKey, RsaPublicKey}; use std::{future::Future, pin::Pin}; #[derive(Clone, Debug)] @@ -128,19 +125,23 @@ async fn do_verify( signing_string: String, ) -> Result<(), Error> { let public_key = RsaPublicKey::from_public_key_pem(public_key.trim())?; + let public_key_der = public_key + .to_pkcs1_der() + .map_err(|_| ErrorKind::DerEncode)?; + let public_key = ring::signature::UnparsedPublicKey::new( + &ring::signature::RSA_PKCS1_2048_8192_SHA256, + public_key_der, + ); let span = tracing::Span::current(); spawner .spawn_blocking(move || { span.in_scope(|| { let decoded = STANDARD.decode(signature)?; - let signature = - Signature::try_from(decoded.as_slice()).map_err(ErrorKind::ReadSignature)?; - let verifying_key = VerifyingKey::::new(public_key); - verifying_key - .verify(signing_string.as_bytes(), &signature) - .map_err(ErrorKind::VerifySignature)?; + public_key + .verify(signing_string.as_bytes(), decoded.as_slice()) + .map_err(|_| ErrorKind::VerifySignature)?; Ok(()) as Result<(), Error> }) diff --git a/src/requests.rs b/src/requests.rs index e693c31..8b77028 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -8,14 +8,12 @@ use actix_web::http::header::Date; use awc::{error::SendRequestError, Client, ClientResponse, Connector}; use base64::{engine::general_purpose::STANDARD, Engine}; use dashmap::DashMap; -use http_signature_normalization_actix::prelude::*; -use rand::thread_rng; -use rsa::{ - pkcs1v15::SigningKey, - sha2::{Digest, Sha256}, - signature::{RandomizedSigner, SignatureEncoding}, - RsaPrivateKey, +use http_signature_normalization_actix::{digest::ring::Sha256, prelude::*}; +use ring::{ + rand::SystemRandom, + signature::{RsaKeyPair, RSA_PKCS1_SHA256}, }; +use rsa::{pkcs1::EncodeRsaPrivateKey, RsaPrivateKey}; use std::{ sync::Arc, time::{Duration, SystemTime}, @@ -145,7 +143,8 @@ pub(crate) struct Requests { client: Client, key_id: String, user_agent: String, - private_key: RsaPrivateKey, + private_key: Arc, + rng: SystemRandom, config: Config, breakers: Breakers, last_online: Arc, @@ -196,12 +195,16 @@ impl Requests { timeout_seconds: u64, spawner: Spawner, ) -> Self { + let private_key_der = private_key.to_pkcs1_der().expect("Can encode der"); + let private_key = ring::signature::RsaKeyPair::from_der(private_key_der.as_bytes()) + .expect("Key is valid"); Requests { pool_size, client: build_client(&user_agent, pool_size, timeout_seconds), key_id, user_agent, - private_key, + private_key: Arc::new(private_key), + rng: SystemRandom::new(), config: Config::new().mastodon_compat().spawner(spawner), breakers, last_online, @@ -407,19 +410,29 @@ impl Requests { fn signer(&self) -> Signer { Signer { private_key: self.private_key.clone(), + rng: self.rng.clone(), } } } struct Signer { - private_key: RsaPrivateKey, + private_key: Arc, + rng: SystemRandom, } impl Signer { fn sign(&self, signing_string: &str) -> Result { - let signing_key = SigningKey::::new(self.private_key.clone()); - let signature = - signing_key.try_sign_with_rng(&mut thread_rng(), signing_string.as_bytes())?; - Ok(STANDARD.encode(signature.to_bytes().as_ref())) + let mut signature = vec![0; self.private_key.public_modulus_len()]; + + self.private_key + .sign( + &RSA_PKCS1_SHA256, + &self.rng, + signing_string.as_bytes(), + &mut signature, + ) + .map_err(|_| ErrorKind::SignRequest)?; + + Ok(STANDARD.encode(&signature)) } }