From 62658108f872ce8558be002a7079e52c04a1292b Mon Sep 17 00:00:00 2001 From: Elizabeth Myers Date: Sun, 31 Mar 2024 18:46:27 -0700 Subject: [PATCH] Move blocking operations to spawn_blocking Signed-off-by: Elizabeth Myers --- src/csrf.rs | 62 ++++++++++++++++++++++++++----------------- src/generate.rs | 11 +++++++- src/web/submission.rs | 2 +- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/csrf.rs b/src/csrf.rs index 93242de..7227114 100644 --- a/src/csrf.rs +++ b/src/csrf.rs @@ -20,6 +20,7 @@ use rand::{ use serde::{Deserialize, Serialize}; use subtle::ConstantTimeEq; use time::{Duration, OffsetDateTime}; +use tokio::task::{spawn_blocking, JoinError}; use tower_sessions::Session; use tracing::debug; @@ -40,6 +41,9 @@ pub enum SessionError { #[error(transparent)] Session(#[from] tower_sessions::session::Error), + #[error(transparent)] + Join(#[from] JoinError), + #[error("No CSRF token")] NoToken, @@ -51,36 +55,44 @@ pub enum SessionError { } impl SessionData { - fn new() -> Self { + async fn new() -> Self { let len_distr = Lazy::new(|| Uniform::new(24usize, 48usize)); - let mut rng = thread_rng(); - let len = (*len_distr).sample(&mut rng); - Self { - token: WebsafeAlphabet.sample_string(&mut rng, len), - time: OffsetDateTime::now_utc(), - } + spawn_blocking(move || { + let mut rng = thread_rng(); + let len = (*len_distr).sample(&mut rng); + Self { + token: WebsafeAlphabet.sample_string(&mut rng, len), + time: OffsetDateTime::now_utc(), + } + }) + .await + .expect("Unexpectedly failed to create session data") } - fn cmp(&self, token: &str) -> Result<(), SessionError> { - if token.as_bytes().ct_ne(self.token.as_bytes()).into() { - debug!("CSRF tokens mismatched: {token} != {}", self.token); - return Err(SessionError::Mismatch); - } - - // This isn't sensitive info, so it's okay not to compare in constant time - if OffsetDateTime::now_utc() - self.time > MAX_DURATION { - debug!( - "CSRF token expired: {}", - OffsetDateTime::now_utc() - self.time - ); - return Err(SessionError::Expired); - } - - Ok(()) + async fn cmp(self, token: &str) -> Result<(), SessionError> { + let token = token.to_owned(); + spawn_blocking(move || { + if token.as_bytes().ct_ne(self.token.as_bytes()).into() { + debug!("CSRF tokens mismatched"); + return Err(SessionError::Mismatch); + } + + // This isn't sensitive info, so it's okay not to compare in constant time + if OffsetDateTime::now_utc() - self.time > MAX_DURATION { + debug!( + "CSRF token expired: {}", + OffsetDateTime::now_utc() - self.time + ); + return Err(SessionError::Expired); + } + + Ok(()) + }) + .await? } pub async fn new_into_session(session: &Session) -> Result { - let data = Self::new(); + let data = Self::new().await; let token = data.token.clone(); session.insert(SESSION_KEY, data).await?; @@ -96,6 +108,6 @@ impl SessionData { .ok_or(SessionError::NoToken)?; // Verify the token - data.cmp(token) + data.cmp(token).await } } diff --git a/src/generate.rs b/src/generate.rs index 5ed27ae..a5469f7 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -17,6 +17,7 @@ use rand::{ distributions::{DistString, Uniform}, prelude::*, }; +use tokio::task::spawn_blocking; use crate::util::{macros::arr, string::WebsafeAlphabet}; @@ -166,7 +167,7 @@ impl Generator { } // Create a shady-looking filename for the URL - pub(crate) fn shady_filename() -> String { + fn generate_shady_filename() -> String { arr!(const SEPS: [&str; _] = ["!", "_", "+", "~"]); let mut rng = thread_rng(); @@ -230,6 +231,14 @@ impl Generator { out.into_iter().collect() } + + // async wrapper around generate_shady_filename + pub(crate) async fn shady_filename() -> String { + + spawn_blocking(Self::generate_shady_filename) + .await + .expect("shady_filename task unexpectedly failed") + } } mod strings { diff --git a/src/web/submission.rs b/src/web/submission.rs index 05d9108..332c645 100644 --- a/src/web/submission.rs +++ b/src/web/submission.rs @@ -120,7 +120,7 @@ mod post { )); } - let shady = Generator::shady_filename(); + let shady = Generator::shady_filename().await; Mutation::create_url(&state.db, &url_form.url, &shady, Some(addr.to_string())).await?; debug!("URL created: {} -> {shady}", url_form.url);