diff --git a/Cargo.lock b/Cargo.lock index 8053b412f5..adfce2fc4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -431,6 +431,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base32" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" + [[package]] name = "base64" version = "0.13.1" @@ -806,9 +812,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] @@ -986,6 +992,7 @@ dependencies = [ "axum 0.7.5", "axum-client-ip", "axum-extra", + "base32", "base64 0.22.1", "bytes", "chrono", @@ -2178,9 +2185,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -3927,9 +3934,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.206" +version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" +checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" dependencies = [ "serde_derive", ] @@ -3965,9 +3972,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.206" +version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" +checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" dependencies = [ "proc-macro2", "quote", @@ -5359,19 +5366,20 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", @@ -5384,9 +5392,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -5396,9 +5404,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5406,9 +5414,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", @@ -5419,9 +5427,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-streams" @@ -5438,9 +5446,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 681ce8f56b..a5317a6e6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ axum-extra = { version = "0.9", features = [ "cookie-private", "typed-header", ] } +base32 = "0.5" base64 = "0.22" chrono = { version = "0.4", default-features = false, features = [ "clock", diff --git a/Dockerfile b/Dockerfile index 8762ea0045..56e3aafd99 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ COPY web/ . RUN pnpm run generate-translation-types RUN pnpm build -FROM rust:1.77 as chef +FROM rust:1.80 as chef WORKDIR /build diff --git a/docker-compose.e2e.yaml b/docker-compose.e2e.yaml index 7b126e6dfa..4d639dbf05 100644 --- a/docker-compose.e2e.yaml +++ b/docker-compose.e2e.yaml @@ -1,4 +1,3 @@ -version: "3.9" services: core: image: ghcr.io/defguard/defguard:current diff --git a/docker-compose.yaml b/docker-compose.yaml index 546b45be6e..d65f90e405 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,5 +1,3 @@ -version: "3" - services: core: image: ghcr.io/defguard/defguard:latest diff --git a/src/auth/mod.rs b/src/auth/mod.rs index 9fdc7f679a..a87d85527e 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -28,6 +28,9 @@ pub static JWT_ISSUER: &str = "DefGuard"; pub static AUTH_SECRET_ENV: &str = "DEFGUARD_AUTH_SECRET"; pub static GATEWAY_SECRET_ENV: &str = "DEFGUARD_GATEWAY_SECRET"; pub static YUBIBRIDGE_SECRET_ENV: &str = "DEFGUARD_YUBIBRIDGE_SECRET"; +pub const TOTP_CODE_VALIDITY_PERIOD: u64 = 30; +pub const EMAIL_CODE_DIGITS: u32 = 6; +pub const TOTP_CODE_DIGITS: u32 = 6; #[derive(Clone, Copy, Default)] pub enum ClaimsType { diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 76257d191b..b9b5948652 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -10,7 +10,7 @@ use argon2::{ use axum::http::StatusCode; use model_derive::Model; use sqlx::{query, query_as, query_scalar, Error as SqlxError, PgExecutor, Type}; -use totp_lite::{totp, totp_custom, Sha1}; +use totp_lite::{totp_custom, Sha1}; use super::{ device::{Device, UserDevice}, @@ -20,15 +20,14 @@ use super::{ DbPool, MFAInfo, OAuth2AuthorizedAppInfo, SecurityKey, WalletInfo, }; use crate::{ + auth::{EMAIL_CODE_DIGITS, TOTP_CODE_DIGITS, TOTP_CODE_VALIDITY_PERIOD}, db::Session, error::WebError, - hex::to_lower_hex, random::{gen_alphanumeric, gen_totp_secret}, server_config, }; const RECOVERY_CODES_COUNT: usize = 8; -const EMAIL_CODE_DIGITS: u32 = 6; #[derive(Clone, Deserialize, Serialize, PartialEq, Type, Debug)] #[sqlx(type_name = "mfa_method", rename_all = "snake_case")] @@ -181,7 +180,7 @@ impl User { .execute(executor) .await?; } - let secret_base32 = to_lower_hex(&secret); + let secret_base32 = base32::encode(base32::Alphabet::Rfc4648 { padding: false }, &secret); self.totp_secret = Some(secret); Ok(secret_base32) } @@ -514,7 +513,12 @@ impl User { pub fn verify_totp_code(&self, code: &str) -> bool { if let Some(totp_secret) = &self.totp_secret { if let Ok(timestamp) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { - let expected_code = totp::(totp_secret, timestamp.as_secs()); + let expected_code = totp_custom::( + TOTP_CODE_VALIDITY_PERIOD, + TOTP_CODE_DIGITS, + totp_secret, + timestamp.as_secs(), + ); eprintln!("{expected_code} ?? {code}"); return code == expected_code; } diff --git a/tests/auth.rs b/tests/auth.rs index c19f2c83df..c44afe1b3f 100644 --- a/tests/auth.rs +++ b/tests/auth.rs @@ -6,11 +6,12 @@ use chrono::NaiveDateTime; use claims::assert_err; use common::fetch_user_details; use defguard::{ + auth::{TOTP_CODE_DIGITS, TOTP_CODE_VALIDITY_PERIOD}, db::{ models::wallet::keccak256, DbPool, MFAInfo, MFAMethod, Settings, User, UserDetails, Wallet, }, handlers::{Auth, AuthCode, AuthResponse, AuthTotp, WalletChallenge}, - hex::{hex_decode, to_lower_hex}, + hex::to_lower_hex, secret::SecretString, }; use ethers_core::types::transaction::eip712::{Eip712, TypedData}; @@ -19,7 +20,7 @@ use secp256k1::{rand::rngs::OsRng, All, Message, Secp256k1, SecretKey}; use serde::Deserialize; use serde_json::json; use sqlx::query; -use totp_lite::{totp, Sha1}; +use totp_lite::{totp_custom, Sha1}; use webauthn_authenticator_rs::{prelude::Url, softpasskey::SoftPasskey, WebauthnAuthenticator}; use webauthn_rs::prelude::{CreationChallengeResponse, RequestChallengeResponse}; @@ -190,8 +191,17 @@ fn totp_code(auth_totp: &AuthTotp) -> AuthCode { let timestamp = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap(); - let secret = hex_decode(&auth_totp.secret).unwrap(); - let code = totp::(&secret, timestamp.as_secs()); + let secret = base32::decode( + base32::Alphabet::Rfc4648 { padding: false }, + &auth_totp.secret, + ) + .unwrap(); + let code = totp_custom::( + TOTP_CODE_VALIDITY_PERIOD, + TOTP_CODE_DIGITS, + &secret, + timestamp.as_secs(), + ); AuthCode::new(code) } diff --git a/web/src/i18n/pl/index.ts b/web/src/i18n/pl/index.ts index 1b3cd5ffda..58c00f6d59 100644 --- a/web/src/i18n/pl/index.ts +++ b/web/src/i18n/pl/index.ts @@ -202,7 +202,7 @@ const pl: Translation = { totpCopied: 'Ścieżka TOTP skopiowana.', success: 'TOTP Enabled', }, - copyPath: 'Skopiowana ścieżka TOTP', + copyPath: 'Kopiuj ścieżkę TOTP', form: { fields: { code: { diff --git a/web/src/pages/auth/MFARoute/MFAEmail/MFAEmail.tsx b/web/src/pages/auth/MFARoute/MFAEmail/MFAEmail.tsx index 3bb1762bd3..63c9547e06 100644 --- a/web/src/pages/auth/MFARoute/MFAEmail/MFAEmail.tsx +++ b/web/src/pages/auth/MFARoute/MFAEmail/MFAEmail.tsx @@ -92,7 +92,7 @@ export const MFAEmail = () => { const handleValidSubmit: SubmitHandler = (data) => { const trimmed = trimObjectStrings(data); verifyMutate({ - code: Number.parseInt(trimmed.code), + code: String(trimmed.code), }); }; diff --git a/web/src/pages/auth/MFARoute/MFATOTPAuth/MFATOTPAuth.tsx b/web/src/pages/auth/MFARoute/MFATOTPAuth/MFATOTPAuth.tsx index 8b3b3ceef0..ee401ca167 100644 --- a/web/src/pages/auth/MFARoute/MFATOTPAuth/MFATOTPAuth.tsx +++ b/web/src/pages/auth/MFARoute/MFATOTPAuth/MFATOTPAuth.tsx @@ -65,7 +65,7 @@ export const MFATOTPAuth = () => { const handleValidSubmit: SubmitHandler = (values) => { const trimmed = trimObjectStrings(values); - mutate({ code: Number(trimmed.code) }); + mutate({ code: String(trimmed.code) }); }; useEffect(() => { diff --git a/web/src/pages/users/UserProfile/UserAuthInfo/modals/RegisterEmailMFAModal/components/RegisterMFAEmailForm/RegisterMFAEmailForm.tsx b/web/src/pages/users/UserProfile/UserAuthInfo/modals/RegisterEmailMFAModal/components/RegisterMFAEmailForm/RegisterMFAEmailForm.tsx index c19593c58a..5e63916595 100644 --- a/web/src/pages/users/UserProfile/UserAuthInfo/modals/RegisterEmailMFAModal/components/RegisterMFAEmailForm/RegisterMFAEmailForm.tsx +++ b/web/src/pages/users/UserProfile/UserAuthInfo/modals/RegisterEmailMFAModal/components/RegisterMFAEmailForm/RegisterMFAEmailForm.tsx @@ -107,7 +107,7 @@ export const RegisterMFAEmailForm = () => { const handleValidSubmit: SubmitHandler = (data) => { data = trimObjectStrings(data); mutateFinish({ - code: Number.parseInt(data.code), + code: String(data.code), }); }; diff --git a/web/src/pages/users/UserProfile/UserAuthInfo/modals/RegisterTOTPModal/RegisterTOTPModal.tsx b/web/src/pages/users/UserProfile/UserAuthInfo/modals/RegisterTOTPModal/RegisterTOTPModal.tsx index 7d26412858..bcf3ac2e0f 100644 --- a/web/src/pages/users/UserProfile/UserAuthInfo/modals/RegisterTOTPModal/RegisterTOTPModal.tsx +++ b/web/src/pages/users/UserProfile/UserAuthInfo/modals/RegisterTOTPModal/RegisterTOTPModal.tsx @@ -161,7 +161,7 @@ const TOTPRegisterForm = () => { const onValidSubmit: SubmitHandler = (values) => { values = trimObjectStrings(values); mutate({ - code: Number(values.code), + code: String(values.code), }); }; return ( diff --git a/web/src/shared/types.ts b/web/src/shared/types.ts index 2a641e8223..4c1f5634d2 100644 --- a/web/src/shared/types.ts +++ b/web/src/shared/types.ts @@ -383,7 +383,7 @@ export type ChangePasswordSelfRequest = { }; export type AuthCodeRequest = { - code: number; + code: string; }; export type AuthenticationKeyInfo = { @@ -997,7 +997,7 @@ export interface Web3StartRequest { } export interface TOTPRequest { - code: number; + code: string; } export interface WebAuthnRegistrationRequest {