From b418a8c53a6efe1e0c2ca90c1c255f9516753d49 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Wed, 25 Oct 2023 11:41:15 +0300 Subject: [PATCH] Change from secstr to secure-string. --- Cargo.lock | 16 +++--------- Cargo.toml | 4 +-- backend/api_commands/Cargo.toml | 2 +- backend/api_commands/src/lib.rs | 9 +++---- backend/integration-tests/Cargo.toml | 3 +++ backend/integration-tests/src/main.rs | 5 ++-- backend/rvoc-backend/Cargo.toml | 2 +- backend/rvoc-backend/src/cli/mod.rs | 11 ++++---- backend/rvoc-backend/src/configuration/mod.rs | 13 ++++------ backend/rvoc-backend/src/main.rs | 3 --- .../src/model/user/password_hash.rs | 26 ++++++++++--------- 11 files changed, 40 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2c4259..34ec024 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,7 +102,7 @@ dependencies = [ name = "api_commands" version = "0.0.1" dependencies = [ - "secstr", + "secure-string", "serde", "serde_json", ] @@ -1052,6 +1052,7 @@ dependencies = [ "api_commands", "log", "reqwest", + "secure-string", "serde", "serde_json", "simplelog", @@ -1812,7 +1813,7 @@ dependencies = [ "opentelemetry-otlp", "password-hash", "rand", - "secstr", + "secure-string", "serde", "strum", "thiserror", @@ -1857,16 +1858,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "secstr" -version = "0.5.1" -source = "git+https://codeberg.org/ISibboI/secstr.git?rev=841f3d5c66b6057bcd19288d10206e1b1cbd76c1#841f3d5c66b6057bcd19288d10206e1b1cbd76c1" -dependencies = [ - "libc", - "serde", - "zeroize", -] - [[package]] name = "secure-string" version = "0.3.0" @@ -1874,6 +1865,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "548ba8c9ff631f7bb3a64de1e8ad73fe20f6d04090724f2b496ed45314ad7488" dependencies = [ "libc", + "serde", "zeroize", ] diff --git a/Cargo.toml b/Cargo.toml index 8235baa..abbdeb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,4 @@ publish = false [workspace.dependencies] # sensitive data handling -secstr = { git = "https://codeberg.org/ISibboI/secstr.git", rev = "841f3d5c66b6057bcd19288d10206e1b1cbd76c1", features = [ - "serde", -] } +secure-string = { version = "0.3.0", features = ["serde"] } diff --git a/backend/api_commands/Cargo.toml b/backend/api_commands/Cargo.toml index b15e401..199184c 100644 --- a/backend/api_commands/Cargo.toml +++ b/backend/api_commands/Cargo.toml @@ -17,4 +17,4 @@ serde = { version = "1.0.186", features = ["derive"] } serde_json = "1.0.105" # sensitive data handling -secstr.workspace = true +secure-string.workspace = true diff --git a/backend/api_commands/src/lib.rs b/backend/api_commands/src/lib.rs index 19e9d50..06cad8f 100644 --- a/backend/api_commands/src/lib.rs +++ b/backend/api_commands/src/lib.rs @@ -1,19 +1,16 @@ -use secstr::SecVec; - -pub type SecBytes = SecVec; - +use secure_string::SecureBytes; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] pub struct CreateAccount { pub username: String, - pub password: SecBytes, + pub password: SecureBytes, } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] pub struct Login { pub username: String, - pub password: SecBytes, + pub password: SecureBytes, } #[cfg(test)] diff --git a/backend/integration-tests/Cargo.toml b/backend/integration-tests/Cargo.toml index 407f1c7..3526854 100644 --- a/backend/integration-tests/Cargo.toml +++ b/backend/integration-tests/Cargo.toml @@ -27,5 +27,8 @@ serde_json = "1.0.105" # web api api_commands = { path = "../api_commands" } +# sensitive data handling +secure-string.workspace = true + # errors anyhow = { version = "1.0.75", features = ["backtrace"] } diff --git a/backend/integration-tests/src/main.rs b/backend/integration-tests/src/main.rs index f3fe257..def8b33 100644 --- a/backend/integration-tests/src/main.rs +++ b/backend/integration-tests/src/main.rs @@ -1,7 +1,8 @@ use anyhow::{bail, Context}; -use api_commands::{CreateAccount, Login, SecBytes}; +use api_commands::{CreateAccount, Login}; use log::{debug, error, info}; use reqwest::StatusCode; +use secure_string::SecureBytes; use simplelog::TermLogger; use tokio::spawn; @@ -151,7 +152,7 @@ async fn test_user_account_deletion() -> anyhow::Result<()> { async fn test_login_logout() -> anyhow::Result<()> { let client = HttpClient::new().await?; - let password = SecBytes::from("waldπŸ˜€πŸ˜€πŸ˜€πŸ˜€".to_owned()); + let password = SecureBytes::from("waldπŸ˜€πŸ˜€πŸ˜€πŸ˜€".to_owned()); let response = client .post( diff --git a/backend/rvoc-backend/Cargo.toml b/backend/rvoc-backend/Cargo.toml index 91887f7..28899a1 100644 --- a/backend/rvoc-backend/Cargo.toml +++ b/backend/rvoc-backend/Cargo.toml @@ -59,7 +59,7 @@ argon2 = { version = "0.5.1", features = ["std"] } rand = "0.8.5" # sensitive data handling -secstr.workspace = true +secure-string.workspace = true # web api api_commands = { path = "../api_commands" } diff --git a/backend/rvoc-backend/src/cli/mod.rs b/backend/rvoc-backend/src/cli/mod.rs index bff6e73..51b332d 100644 --- a/backend/rvoc-backend/src/cli/mod.rs +++ b/backend/rvoc-backend/src/cli/mod.rs @@ -1,9 +1,8 @@ use std::sync::{atomic, Arc}; -use api_commands::SecBytes; use clap::Parser; use diesel_async::RunQueryDsl; -use secstr::SecUtf8; +use secure_string::{SecureBytes, SecureString}; use tokio::io::{stdin, AsyncReadExt}; use tracing::{debug, info, instrument}; @@ -51,7 +50,7 @@ enum Cli { /// The new password. /// If not given, then it is read from stdin. #[arg(short, long)] - password: Option, + password: Option, }, } @@ -160,7 +159,7 @@ async fn expire_all_passwords(configuration: &Configuration) -> RVocResult<()> { #[instrument(err, skip(configuration))] async fn set_password( username: String, - password: Option, + password: Option, configuration: &Configuration, ) -> RVocResult<()> { let password = if let Some(password) = password { @@ -172,11 +171,11 @@ async fn set_password( source: Box::new(error), } })?; - SecBytes::from(password) + SecureBytes::from(password) }; let password_hash = PasswordHash::new(password, configuration)?; - let password_hash = Option::::from(password_hash).expect( + let password_hash = Option::::from(password_hash).expect( "creating a password hash from a password should never return an empty password hash", ); diff --git a/backend/rvoc-backend/src/configuration/mod.rs b/backend/rvoc-backend/src/configuration/mod.rs index 9a9b209..e790f82 100644 --- a/backend/rvoc-backend/src/configuration/mod.rs +++ b/backend/rvoc-backend/src/configuration/mod.rs @@ -1,11 +1,8 @@ use std::{env::VarError, error::Error, net::SocketAddr, path::PathBuf, str::FromStr}; -use crate::{ - error::{RVocError, RVocResult, UserError}, - SecBytes, -}; +use crate::error::{RVocError, RVocResult, UserError}; use chrono::Duration; -use secstr::SecUtf8; +use secure_string::{SecureBytes, SecureString}; /// The configuration of the application. #[derive(Debug, Clone)] @@ -16,7 +13,7 @@ pub struct Configuration { pub integration_test_mode: bool, /// The url to access postgres. - pub postgres_url: SecUtf8, + pub postgres_url: SecureString, /// The url to send opentelemetry to. pub opentelemetry_url: Option, @@ -50,7 +47,7 @@ pub struct Configuration { pub maximum_password_length: usize, /// An additional salt that is shared between all passwords, but not stored in the database. - pub password_pepper: SecBytes, + pub password_pepper: SecureBytes, /// The minimum memory parameter of the Argon2id password hash function. /// See the [OWASP password storage cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id) @@ -252,7 +249,7 @@ impl Configuration { } } - pub fn verify_password_length(&self, password: &SecBytes) -> RVocResult<()> { + pub fn verify_password_length(&self, password: &SecureBytes) -> RVocResult<()> { let unsecure_password = password.unsecure(); if unsecure_password.len() < self.minimum_password_length || unsecure_password.len() > self.maximum_password_length diff --git a/backend/rvoc-backend/src/main.rs b/backend/rvoc-backend/src/main.rs index 84c680d..d0012fd 100644 --- a/backend/rvoc-backend/src/main.rs +++ b/backend/rvoc-backend/src/main.rs @@ -1,7 +1,6 @@ use crate::error::RVocResult; use crate::{configuration::Configuration, error::RVocError}; use cli::run_cli_command; -use secstr::SecVec; use tracing::{info, instrument, Level}; use tracing_subscriber::filter::FilterFn; use tracing_subscriber::Layer; @@ -14,8 +13,6 @@ mod job_queue; mod model; mod web; -type SecBytes = SecVec; - #[instrument(err, skip(configuration))] fn setup_tracing_subscriber(configuration: &Configuration) -> RVocResult<()> { use opentelemetry::sdk::Resource; diff --git a/backend/rvoc-backend/src/model/user/password_hash.rs b/backend/rvoc-backend/src/model/user/password_hash.rs index a236767..87d9847 100644 --- a/backend/rvoc-backend/src/model/user/password_hash.rs +++ b/backend/rvoc-backend/src/model/user/password_hash.rs @@ -2,9 +2,9 @@ use argon2::Argon2; use argon2::PasswordHasher; use password_hash::PasswordVerifier; use password_hash::{rand_core::OsRng, SaltString}; -use secstr::SecUtf8; +use secure_string::SecureBytes; +use secure_string::SecureString; -use crate::SecBytes; use crate::{ configuration::Configuration, error::{RVocError, RVocResult}, @@ -15,7 +15,7 @@ static HASH_ALGORITHM_VERSION: argon2::Version = argon2::Version::V0x13; #[derive(Clone, Debug)] pub struct PasswordHash { - argon_hash: Option, + argon_hash: Option, } #[must_use] @@ -30,7 +30,7 @@ pub struct VerifyPasswordResult { impl PasswordHash { pub fn new( - plaintext_password: SecBytes, + plaintext_password: SecureBytes, configuration: impl AsRef, ) -> RVocResult { let configuration = configuration.as_ref(); @@ -55,7 +55,7 @@ impl PasswordHash { pub fn verify( &mut self, - plaintext_password: SecBytes, + plaintext_password: SecureBytes, configuration: impl AsRef, ) -> RVocResult { let Some(argon_hash) = &self.argon_hash else { @@ -153,11 +153,11 @@ impl PasswordHash { impl From for Option { fn from(value: PasswordHash) -> Self { - value.argon_hash.map(SecUtf8::into_unsecure) + value.argon_hash.map(SecureString::into_unsecure) } } -impl From for Option { +impl From for Option { fn from(value: PasswordHash) -> Self { value.argon_hash } @@ -171,8 +171,8 @@ impl From> for PasswordHash { } } -impl From> for PasswordHash { - fn from(value: Option) -> Self { +impl From> for PasswordHash { + fn from(value: Option) -> Self { Self { argon_hash: value } } } @@ -187,8 +187,10 @@ impl From for PasswordHash { #[cfg(test)] mod tests { + use secure_string::SecureBytes; + use super::{PasswordHash, VerifyPasswordResult, HASH_ALGORITHM, HASH_ALGORITHM_VERSION}; - use crate::{configuration::Configuration, SecBytes}; + use crate::configuration::Configuration; #[test] fn test_password_check() { @@ -201,7 +203,7 @@ mod tests { configuration.build_argon2_parameters().unwrap() ); - let password = SecBytes::from("mypassword"); + let password = SecureBytes::from("mypassword"); let mut password_hash = PasswordHash::new(password.clone(), &configuration).unwrap(); let verify_password_result = password_hash.verify(password.clone(), &configuration); @@ -245,7 +247,7 @@ mod tests { configuration.build_argon2_parameters().unwrap() ); - let password = SecBytes::from("mypassword"); + let password = SecureBytes::from("mypassword"); let mut password_hash = PasswordHash::new(password.clone(), &configuration).unwrap(); let verify_password_result = password_hash.verify(password.clone(), &configuration);