From 648d073e1b8f4838f147c0520024bd453921a25c Mon Sep 17 00:00:00 2001 From: Joris Guyonvarch Date: Fri, 17 Apr 2026 22:53:02 +0200 Subject: Remove signing login token It’s enough to use a safe crypto lib. But augment the token size to upper bound. --- src/crypto/mod.rs | 1 - src/crypto/signed.rs | 71 ---------------------------------------------------- src/main.rs | 1 - src/model/config.rs | 2 -- src/routes.rs | 5 ++-- src/utils/cookie.rs | 18 +++++++------ 6 files changed, 12 insertions(+), 86 deletions(-) delete mode 100644 src/crypto/mod.rs delete mode 100644 src/crypto/signed.rs (limited to 'src') diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs deleted file mode 100644 index 41e9259..0000000 --- a/src/crypto/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod signed; diff --git a/src/crypto/signed.rs b/src/crypto/signed.rs deleted file mode 100644 index 436f3d1..0000000 --- a/src/crypto/signed.rs +++ /dev/null @@ -1,71 +0,0 @@ -use hex; -use hmac::{Hmac, Mac}; -use sha2::Sha256; -use std::str; -use std::time::{SystemTime, UNIX_EPOCH}; - -const SEP: &str = "-"; - -pub fn sign(key: &str, raw: &str) -> Result { - let nonce = get_nonce()?; - let joined = format!("{nonce}{SEP}{raw}"); - let signature = get_signature(key, &joined)?; - Ok(format!("{signature}{SEP}{joined}")) -} - -pub fn verify(key: &str, signed: &str) -> Result { - let mut iter = signed.split(SEP); - match (iter.next(), iter.next()) { - (Some(signature), Some(nonce)) => { - let raw = iter.collect::>().join(SEP); - if signature == get_signature(key, &format!("{nonce}{SEP}{raw}"))? { - Ok(raw) - } else { - Err("Signature does not match".to_string()) - } - } - _ => Err("Malformed signed".to_string()), - } -} - -fn get_signature(key: &str, message: &str) -> Result { - let mut mac = Hmac::::new_from_slice(key.as_bytes()) - .map_err(|e| format!("Error initializing MAC: {e}"))?; - mac.update(message.as_bytes()); - let result = mac.finalize(); - Ok(hex::encode(result.into_bytes())) -} - -fn get_nonce() -> Result { - Ok(SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|e| format!("Failure getting unix expoch: {e}"))? - .as_millis() - .to_string()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn sign_and_validate() { - let key = "xagrlBUobnTj32Rm8tvmsZ6mh8qLfip5".to_string(); - assert_eq!(verify(&key, &sign(&key, "").unwrap()), Ok("".to_string())); - assert_eq!( - verify(&key, &sign(&key, "hello").unwrap()), - Ok("hello".to_string()) - ); - assert_eq!( - verify(&key, &sign(&key, "with-sep").unwrap()), - Ok("with-sep".to_string()) - ); - } - - #[test] - fn fail_when_key_mismatch() { - let key1 = "xagrlBUobnTj32Rm8tvmsZ6mh8qLfip5".to_string(); - let key2 = "8KJBK6axEr9wQ390GgdWA8Pjn8FwILDa".to_string(); - assert!(verify(&key1, &sign(&key2, "hello").unwrap()).is_err()); - } -} diff --git a/src/main.rs b/src/main.rs index 70bac81..1cc2384 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,6 @@ use tokio::net::TcpListener; mod assets; mod controller; -mod crypto; mod db; mod jobs; mod model; diff --git a/src/model/config.rs b/src/model/config.rs index f40b0fb..ba923d6 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -4,7 +4,6 @@ use std::str::FromStr; #[derive(Clone)] pub struct Config { - pub auth_secret: String, pub db_path: String, pub secure_cookies: bool, pub socket_address: SocketAddr, @@ -12,7 +11,6 @@ pub struct Config { pub fn from_env() -> Result { Ok(Config { - auth_secret: read_string("AUTH_SECRET")?, db_path: read_string("DB_PATH")?, secure_cookies: read_bool("SECURE_COOKIES")?, socket_address: read_socket_address("SOCKET_ADDRESS")?, diff --git a/src/routes.rs b/src/routes.rs index 7107a60..8abe1b4 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -49,7 +49,7 @@ pub async fn routes( "icon.png" => file("assets/icon.png", "image/png").await, _ => controller::utils::not_found(), }, - _ => match connected_user(&config, &db_conn, &request).await { + _ => match connected_user(&db_conn, &request).await { Some(user) => { let wallet = Wallet { db_conn, @@ -67,12 +67,11 @@ pub async fn routes( } async fn connected_user( - config: &Config, db_conn: &Connection, request: &Request, ) -> Option { let cookie = request.headers().get("COOKIE")?.to_str().ok()?; - let login_token = cookie::extract_token(config, cookie).ok()?; + let login_token = cookie::extract_token(cookie).ok()?; db::users::get_by_login_token(db_conn, login_token.to_string()).await } diff --git a/src/utils/cookie.rs b/src/utils/cookie.rs index e21e7d4..1ca3b73 100644 --- a/src/utils/cookie.rs +++ b/src/utils/cookie.rs @@ -1,25 +1,27 @@ use hex; use rand_core::{OsRng, TryRngCore}; -use crate::crypto::signed; use crate::model::config::Config; -const TOKEN_BYTES: usize = 20; +// We consider that it’s unfeasible to guess a token from 128 bit long (=16 bytes) to 256 bit (=32 bytes) with safe margin. +const TOKEN_BYTES: usize = 32; pub fn login(config: &Config, token: &str) -> Result { - let signed_token = signed::sign(&config.auth_secret, token)?; - Ok(cookie(config, &signed_token, 365 * 24 * 60 * 60)) + Ok(cookie(config, &token, 365 * 24 * 60 * 60)) } pub fn logout(config: &Config) -> String { cookie(config, "", 0) } -pub fn extract_token(config: &Config, cookie: &str) -> Result { +pub fn extract_token(cookie: &str) -> Result { let mut xs = cookie.split('='); - xs.next(); - let signed_cookie = xs.next().ok_or("Error extracting cookie")?; - signed::verify(&config.auth_secret, signed_cookie) + if xs.next() != Some("TOKEN") { + Err("Error extracting cookie".to_string()) + } else { + let token = xs.next().ok_or("Error extracting cookie")?; + Ok(token.to_string()) + } } pub fn generate_token() -> Result { -- cgit v1.2.3