diff --git a/Cargo.lock b/Cargo.lock index 96d504f2..7050f248 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,6 +166,18 @@ dependencies = [ "password-hash", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "askama" version = "0.15.4" @@ -523,6 +535,20 @@ dependencies = [ "digest", ] +[[package]] +name = "blake3" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.2.17", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -807,6 +833,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + [[package]] name = "convert_case" version = "0.10.0" @@ -871,6 +903,7 @@ dependencies = [ "askama", "async-trait", "axum", + "blake3", "bytes", "chrono", "chrono-tz", @@ -882,7 +915,6 @@ dependencies = [ "deadpool-redis", "derive_builder", "derive_more", - "digest", "email_address", "fake", "fantoccini", @@ -891,7 +923,6 @@ dependencies = [ "futures-util", "grass", "hex", - "hmac", "http 1.4.0", "http-body-util", "humantime", @@ -913,7 +944,6 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sha2", "sqlx", "subtle", "swagger-ui-redist", diff --git a/Cargo.toml b/Cargo.toml index b445f0bb..7d3193af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ async-stream = "0.3" async-trait = "0.1" axum = { version = "0.8", default-features = false } backtrace = "0.3.76" +blake3 = "1.8.3" bytes = "1.11" cargo_toml = "0.22" chrono = { version = "0.4.44", default-features = false } @@ -85,7 +86,6 @@ darling = "0.23" deadpool-redis = { version = "0.23", default-features = false } derive_builder = "0.20" derive_more = "2" -digest = "0.10" email_address = "0.2.9" fake = "4" fantoccini = "0.22" @@ -97,7 +97,6 @@ glob = "0.3" grass = { version = "0.13.4", default-features = false } heck = "0.5" hex = "0.4" -hmac = "0.12" http = "1.4" http-body = "1" http-body-util = "0.1.3" @@ -130,7 +129,6 @@ serde_html_form = { version = "0.4", default-features = false } serde_json = "1" serde_path_to_error = "0.1.20" serde_urlencoded = "0.7" -sha2 = "0.10" sqlx = { version = "0.8", default-features = false } subtle = { version = "2", default-features = false } swagger-ui-redist = { version = "0.1" } diff --git a/cot/Cargo.toml b/cot/Cargo.toml index 9e83834b..4900c27b 100644 --- a/cot/Cargo.toml +++ b/cot/Cargo.toml @@ -20,6 +20,7 @@ aide = { workspace = true, optional = true } askama = { workspace = true, features = ["std"] } async-trait.workspace = true axum = { workspace = true, features = ["http1", "tokio"] } +blake3.workspace = true bytes.workspace = true chrono = { workspace = true, features = ["alloc", "serde", "clock"] } chrono-tz.workspace = true @@ -30,14 +31,12 @@ cot_macros.workspace = true deadpool-redis = { workspace = true, features = ["tokio-comp", "rt_tokio_1"], optional = true } derive_builder.workspace = true derive_more = { workspace = true, features = ["debug", "deref", "display", "from"] } -digest.workspace = true email_address.workspace = true fake = { workspace = true, optional = true, features = ["derive", "chrono"] } form_urlencoded.workspace = true futures-core.workspace = true futures-util.workspace = true hex.workspace = true -hmac.workspace = true http-body-util.workspace = true http.workspace = true humantime.workspace = true @@ -55,7 +54,6 @@ sea-query = { workspace = true, optional = true } sea-query-binder = { workspace = true, features = ["with-chrono", "runtime-tokio"], optional = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, optional = true } -sha2.workspace = true sqlx = { workspace = true, features = ["runtime-tokio", "chrono"], optional = true } subtle = { workspace = true, features = ["std"] } swagger-ui-redist = { workspace = true, optional = true } diff --git a/cot/src/auth.rs b/cot/src/auth.rs index e6bc9acc..0ee95c4f 100644 --- a/cot/src/auth.rs +++ b/cot/src/auth.rs @@ -180,16 +180,12 @@ pub trait User { /// use cot::auth::{SessionAuthHash, User, UserId}; /// use cot::common_types::Password; /// use cot::config::SecretKey; - /// use hmac::{Hmac, Mac}; - /// use sha2::Sha512; /// /// struct MyUser { /// id: i64, /// password: Password, /// } /// - /// type SessionAuthHmac = Hmac; - /// /// impl User for MyUser { /// fn id(&self) -> Option { /// Some(UserId::Int(self.id)) @@ -211,12 +207,12 @@ pub trait User { /// // thanks to this, the session hash is invalidated when the user changes their password /// // and the user is automatically logged out /// - /// let mut mac = SessionAuthHmac::new_from_slice(secret_key.as_bytes()) - /// .expect("HMAC can take key of any size"); - /// mac.update(self.password.as_str().as_bytes()); - /// let hmac_data = mac.finalize().into_bytes(); + /// const SESSION_AUTH_HASH_CONTEXT: &'static str = "cot.rs session auth hash v1"; + /// + /// let key = blake3::derive_key(SESSION_AUTH_HASH_CONTEXT, secret_key.as_bytes()); + /// let hash = blake3::keyed_hash(&key, self.password.as_str().as_bytes()); /// - /// Some(SessionAuthHash::new(&hmac_data)) + /// Some(SessionAuthHash::new(hash.as_slice())) /// } /// } /// ``` @@ -350,16 +346,12 @@ impl User for AnonymousUser {} /// use cot::auth::{SessionAuthHash, User, UserId}; /// use cot::common_types::Password; /// use cot::config::SecretKey; -/// use hmac::{Hmac, Mac}; -/// use sha2::Sha512; /// /// struct MyUser { /// id: i64, /// password: Password, /// } /// -/// type SessionAuthHmac = Hmac; -/// /// impl User for MyUser { /// fn id(&self) -> Option { /// Some(UserId::Int(self.id)) @@ -381,12 +373,12 @@ impl User for AnonymousUser {} /// // thanks to this, the session hash is invalidated when the user changes their password /// // and the user is automatically logged out /// -/// let mut mac = SessionAuthHmac::new_from_slice(secret_key.as_bytes()) -/// .expect("HMAC can take key of any size"); -/// mac.update(self.password.as_str().as_bytes()); -/// let hmac_data = mac.finalize().into_bytes(); +/// const SESSION_AUTH_HASH_CONTEXT: &'static str = "cot.rs session auth hash v1"; +/// +/// let key = blake3::derive_key(SESSION_AUTH_HASH_CONTEXT, secret_key.as_bytes()); +/// let hash = blake3::keyed_hash(&key, self.password.as_str().as_bytes()); /// -/// Some(SessionAuthHash::new(&hmac_data)) +/// Some(SessionAuthHash::new(hash.as_slice())) /// } /// } /// ``` diff --git a/cot/src/auth/db.rs b/cot/src/auth/db.rs index b7cf90b2..8eb6190f 100644 --- a/cot/src/auth/db.rs +++ b/cot/src/auth/db.rs @@ -12,8 +12,6 @@ use async_trait::async_trait; // can figure out it's an autogenerated field use cot::db::Auto; use cot_macros::AdminModel; -use hmac::{Hmac, Mac}; -use sha2::Sha512; use thiserror::Error; use crate::App; @@ -345,8 +343,6 @@ impl DatabaseUser { } } -type SessionAuthHmac = Hmac; - impl User for DatabaseUser { fn id(&self) -> Option { Some(UserId::Int(self.id())) @@ -365,12 +361,12 @@ impl User for DatabaseUser { } fn session_auth_hash(&self, secret_key: &SecretKey) -> Option { - let mut mac = SessionAuthHmac::new_from_slice(secret_key.as_bytes()) - .expect("HMAC can take key of any size"); - mac.update(self.password.as_str().as_bytes()); - let hmac_data = mac.finalize().into_bytes(); + const SESSION_AUTH_HASH_CONTEXT: &str = "cot.rs session auth hash v1"; + + let key = blake3::derive_key(SESSION_AUTH_HASH_CONTEXT, secret_key.as_bytes()); + let hash = blake3::keyed_hash(&key, self.password.as_str().as_bytes()); - Some(SessionAuthHash::new(&hmac_data)) + Some(SessionAuthHash::new(hash.as_slice())) } } diff --git a/cot/src/static_files.rs b/cot/src/static_files.rs index f1504106..720bc4cd 100644 --- a/cot/src/static_files.rs +++ b/cot/src/static_files.rs @@ -13,7 +13,6 @@ use std::time::Duration; use bytes::Bytes; use cot_core::error::impl_into_cot_error; -use digest::Digest; use futures_core::ready; use http::{Request, header}; use pin_project_lite::pin_project; @@ -128,7 +127,7 @@ impl StaticFiles { #[must_use] fn file_hash(file: &StaticFile) -> String { - hex::encode(&sha2::Sha256::digest(&file.content).as_slice()[0..6]) + hex::encode(&blake3::hash(file.content.as_ref()).as_slice()[0..6]) } #[must_use] diff --git a/deny.toml b/deny.toml index 9ebe4dfe..44e84bbb 100644 --- a/deny.toml +++ b/deny.toml @@ -17,6 +17,7 @@ allow = [ "0BSD", "Apache-2.0 WITH LLVM-exception", "Apache-2.0", + "BSD-2-Clause", "BSD-3-Clause", "BSL-1.0", "CDLA-Permissive-2.0",