From f7bd45dff3bfe8b4033cc5b87f372e93a5497f81 Mon Sep 17 00:00:00 2001 From: c0repwn3r Date: Mon, 3 Apr 2023 13:28:12 -0400 Subject: [PATCH] yo es un dumbass --- .idea/dataSources.xml | 4 +- .idea/trifid.iml | 2 + Cargo.lock | 105 ++++++++++- trifid-api/Cargo.toml | 5 +- trifid-api/src/main.rs | 1 + trifid-api/src/routes/v1/auth/mod.rs | 3 +- trifid-api/src/routes/v1/auth/totp.rs | 169 ++++++++++++++++++ .../src/routes/v1/auth/verify_magic_link.rs | 2 +- .../routes/v1/verify_totp_authenticators.rs | 2 +- .../trifid_api_entities/src/entity/mod.rs | 1 + .../trifid_api_entities/src/entity/prelude.rs | 1 + .../src/entity/signing_ca.rs | 22 +++ trifid-api/trifid_api_migration/src/lib.rs | 2 + ...0230403_142517_create_table_signing_cas.rs | 37 ++++ 14 files changed, 348 insertions(+), 8 deletions(-) create mode 100644 trifid-api/src/routes/v1/auth/totp.rs create mode 100644 trifid-api/trifid_api_entities/src/entity/signing_ca.rs create mode 100644 trifid-api/trifid_api_migration/src/m20230403_142517_create_table_signing_cas.rs diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index bbb37d6..7074b84 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,11 +1,11 @@ - + postgresql true org.postgresql.Driver - jdbc:postgresql://localhost:5432/trifidapi + jdbc:postgresql://localhost:5432/trifid $ProjectFileDir$ diff --git a/.idea/trifid.iml b/.idea/trifid.iml index ecc578a..2b729a4 100644 --- a/.idea/trifid.iml +++ b/.idea/trifid.iml @@ -6,6 +6,8 @@ + + diff --git a/Cargo.lock b/Cargo.lock index b132d83..b6904c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,6 +206,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + [[package]] name = "ahash" version = "0.7.6" @@ -669,6 +679,30 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + [[package]] name = "chrono" version = "0.4.24" @@ -685,6 +719,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + [[package]] name = "clap" version = "3.2.23" @@ -877,6 +922,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -1069,7 +1115,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "trifid-pki", + "trifid-pki 0.1.9", "url", ] @@ -1602,6 +1648,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -1920,6 +1975,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "openssl" version = "0.10.45" @@ -2139,6 +2200,17 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -3128,7 +3200,7 @@ dependencies = [ "tar", "tempfile", "toml 0.7.3", - "trifid-pki", + "trifid-pki 0.1.9", "url", ] @@ -3401,6 +3473,7 @@ version = "0.1.0" dependencies = [ "actix-request-identifier", "actix-web", + "chacha20poly1305", "hex", "log", "once_cell", @@ -3410,6 +3483,7 @@ dependencies = [ "simple_logger", "toml 0.7.3", "totp-rs", + "trifid-pki 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "trifid_api_entities", "trifid_api_migration", ] @@ -3430,6 +3504,23 @@ dependencies = [ "x25519-dalek", ] +[[package]] +name = "trifid-pki" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20f0b2d11a5219f67b42a0af7f07b10f1c6c1ea57f8cb9f1115d394176b64c6b" +dependencies = [ + "ed25519-dalek", + "hex", + "ipnet", + "pem", + "quick-protobuf", + "rand", + "rand_core 0.6.4", + "sha2", + "x25519-dalek", +] + [[package]] name = "trifid_api_entities" version = "0.1.0" @@ -3503,6 +3594,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "unsafe-libyaml" version = "0.2.7" diff --git a/trifid-api/Cargo.toml b/trifid-api/Cargo.toml index 5b08380..25034a5 100644 --- a/trifid-api/Cargo.toml +++ b/trifid-api/Cargo.toml @@ -23,4 +23,7 @@ trifid_api_entities = { version = "0.1.0", path = "trifid_api_entities" } rand = "0.8" # Misc. hex = "0.4" # Misc. -totp-rs = { version = "5.0.1", features = ["gen_secret", "otpauth"] } # Misc. \ No newline at end of file +totp-rs = { version = "5.0.1", features = ["gen_secret", "otpauth"] } # Misc. + +trifid-pki = { version = "0.1.9" } # Cryptography +chacha20poly1305 = "0.10.1" # Cryptography \ No newline at end of file diff --git a/trifid-api/src/main.rs b/trifid-api/src/main.rs index f54aca5..2c65ca2 100644 --- a/trifid-api/src/main.rs +++ b/trifid-api/src/main.rs @@ -67,6 +67,7 @@ async fn main() -> Result<(), Box> { .service(routes::v1::auth::verify_magic_link::verify_magic_link_request) .service(routes::v1::totp_authenticators::totp_authenticators_request) .service(routes::v1::verify_totp_authenticators::verify_totp_authenticators_request) + .service(routes::v1::auth::totp::totp_request) }).bind(CONFIG.server.bind)?.run().await?; Ok(()) diff --git a/trifid-api/src/routes/v1/auth/mod.rs b/trifid-api/src/routes/v1/auth/mod.rs index 63d2a9f..29a43ea 100644 --- a/trifid-api/src/routes/v1/auth/mod.rs +++ b/trifid-api/src/routes/v1/auth/mod.rs @@ -1,2 +1,3 @@ pub mod magic_link; -pub mod verify_magic_link; \ No newline at end of file +pub mod verify_magic_link; +pub mod totp; \ No newline at end of file diff --git a/trifid-api/src/routes/v1/auth/totp.rs b/trifid-api/src/routes/v1/auth/totp.rs new file mode 100644 index 0000000..bc95d2c --- /dev/null +++ b/trifid-api/src/routes/v1/auth/totp.rs @@ -0,0 +1,169 @@ +use actix_web::{HttpRequest, HttpResponse, post}; +use actix_web::web::{Data, Json}; +use log::{debug, error}; +use serde::{Serialize, Deserialize}; +use trifid_api_entities::entity::totp_authenticator; +use crate::AppState; +use crate::auth_tokens::{enforce_session, TokenInfo}; +use crate::error::{APIError, APIErrorsResponse}; +use sea_orm::{EntityTrait, QueryFilter, ColumnTrait, IntoActiveModel, ActiveModelTrait}; +use sea_orm::ActiveValue::Set; +use totp_rs::{Algorithm, Secret, TOTP}; +use trifid_api_entities::entity::auth_token; +use crate::config::CONFIG; +use crate::timers::expires_in_seconds; +use crate::tokens::random_token; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct TotpRequest { + pub code: String +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct TotpResponse { + pub data: TotpResponseData, + pub metadata: TotpResponseMetadata +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct TotpResponseData { + #[serde(rename = "authToken")] + pub auth_token: String +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct TotpResponseMetadata {} + +#[post("/v1/auth/totp")] +pub async fn totp_request(req: Json, req_data: HttpRequest, db: Data) -> HttpResponse { + // require a user session + let session_token = match enforce_session(&req_data, &db.conn).await { + Ok(r) => { + match r { + TokenInfo::SessionToken(i) => i, + _ => unreachable!() + } + } + Err(e) => { + error!("error enforcing session: {}", e); + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_UNAUTHORIZED".to_string(), + message: "Unauthorized".to_string(), + path: None, + } + ], + }); + } + }; + + // determine if the user has a totp authenticator + let auther = match totp_authenticator::Entity::find().filter(totp_authenticator::Column::User.eq(&session_token.user.id)).one(&db.conn).await { + Ok(r) => r, + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error with the database request, please try again later.".to_string(), + path: None, + } + ], + }); + } + }; + let auther = match auther { + Some(a) => { + a + }, + None => { + return HttpResponse::BadRequest().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_USER_NO_TOTP".to_string(), + message: "This user does not have a totp authenticator".to_string(), + path: None, + } + ] + }); + } + }; + + let secret = Secret::Encoded(auther.secret.clone()); + let totpmachine = match TOTP::from_url(auther.url.clone()) { + Ok(m) => m, + Err(e) => { + error!("totp url error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_SECRET_ERROR".to_string(), + message: "There was an error parsing the totpmachine. Please try again later.".to_string(), + path: None, + } + ], + }); + } + }; + + + let valid = match totpmachine.check_current(&req.code) { + Ok(valid) => valid, + Err(e) => { + error!("system time error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_TIME_ERROR".to_string(), + message: "There was an with the server-side time clock.".to_string(), + path: None, + } + ], + }); + } + }; + + if !valid { + debug!("current: {}", totpmachine.generate_current().unwrap()); + error!("user send invalid totp code"); + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_UNAUTHORIZED".to_string(), + message: "Unauthorized".to_string(), + path: None, + } + ], + }) + } + + let model: auth_token::Model = auth_token::Model { + id: random_token("auth"), + user: session_token.user.id, + expires_on: expires_in_seconds(CONFIG.tokens.mfa_tokens_expiry_time_seconds) as i64, + }; + let token = model.id.clone(); + let active_model = model.into_active_model(); + match active_model.insert(&db.conn).await { + Ok(_) => (), + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error issuing the authentication token.".to_string(), + path: None, + } + ], + }); + } + } + + HttpResponse::Ok().json(TotpResponse { + data: TotpResponseData { auth_token: token }, + metadata: TotpResponseMetadata {}, + }) +} \ No newline at end of file diff --git a/trifid-api/src/routes/v1/auth/verify_magic_link.rs b/trifid-api/src/routes/v1/auth/verify_magic_link.rs index cbd8240..d5eb770 100644 --- a/trifid-api/src/routes/v1/auth/verify_magic_link.rs +++ b/trifid-api/src/routes/v1/auth/verify_magic_link.rs @@ -66,7 +66,7 @@ pub async fn verify_magic_link_request(db: Data, req: Json r, Err(e) => { error!("database error: {}", e); diff --git a/trifid-api/trifid_api_entities/src/entity/mod.rs b/trifid-api/trifid_api_entities/src/entity/mod.rs index 82f32b1..194bbb3 100644 --- a/trifid-api/trifid_api_entities/src/entity/mod.rs +++ b/trifid-api/trifid_api_entities/src/entity/mod.rs @@ -9,5 +9,6 @@ pub mod magic_link; pub mod network; pub mod organization; pub mod session_token; +pub mod signing_ca; pub mod totp_authenticator; pub mod user; diff --git a/trifid-api/trifid_api_entities/src/entity/prelude.rs b/trifid-api/trifid_api_entities/src/entity/prelude.rs index d201e1a..2fdea32 100644 --- a/trifid-api/trifid_api_entities/src/entity/prelude.rs +++ b/trifid-api/trifid_api_entities/src/entity/prelude.rs @@ -7,5 +7,6 @@ pub use super::magic_link::Entity as MagicLink; pub use super::network::Entity as Network; pub use super::organization::Entity as Organization; pub use super::session_token::Entity as SessionToken; +pub use super::signing_ca::Entity as SigningCa; pub use super::totp_authenticator::Entity as TotpAuthenticator; pub use super::user::Entity as User; diff --git a/trifid-api/trifid_api_entities/src/entity/signing_ca.rs b/trifid-api/trifid_api_entities/src/entity/signing_ca.rs new file mode 100644 index 0000000..2caba85 --- /dev/null +++ b/trifid-api/trifid_api_entities/src/entity/signing_ca.rs @@ -0,0 +1,22 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "signing_ca")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + pub organization: String, + pub cert: String, + #[sea_orm(unique)] + pub key: String, + pub expires: i64, + #[sea_orm(unique)] + pub nonce: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/trifid-api/trifid_api_migration/src/lib.rs b/trifid-api/trifid_api_migration/src/lib.rs index 68d4c03..3768bfc 100644 --- a/trifid-api/trifid_api_migration/src/lib.rs +++ b/trifid-api/trifid_api_migration/src/lib.rs @@ -11,6 +11,7 @@ pub mod m20230402_233043_create_table_api_keys; pub mod m20230402_233047_create_table_api_keys_scopes; mod m20230402_234025_create_table_totp_authenticators; mod m20230403_002256_create_table_auth_tokens; +mod m20230403_142517_create_table_signing_cas; #[async_trait::async_trait] impl MigratorTrait for Migrator { @@ -25,6 +26,7 @@ impl MigratorTrait for Migrator { Box::new(m20230402_233047_create_table_api_keys_scopes::Migration), Box::new(m20230402_234025_create_table_totp_authenticators::Migration), Box::new(m20230403_002256_create_table_auth_tokens::Migration), + Box::new(m20230403_142517_create_table_signing_cas::Migration), ] } } diff --git a/trifid-api/trifid_api_migration/src/m20230403_142517_create_table_signing_cas.rs b/trifid-api/trifid_api_migration/src/m20230403_142517_create_table_signing_cas.rs new file mode 100644 index 0000000..084dcd0 --- /dev/null +++ b/trifid-api/trifid_api_migration/src/m20230403_142517_create_table_signing_cas.rs @@ -0,0 +1,37 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager.create_table( + Table::create() + .table(SigningCA::Table) + .col(ColumnDef::new(SigningCA::Id).string().not_null().primary_key()) + .col(ColumnDef::new(SigningCA::Organization).string().not_null()) + .col(ColumnDef::new(SigningCA::Cert).string().not_null()) + .col(ColumnDef::new(SigningCA::Key).string().not_null().unique_key()) + .col(ColumnDef::new(SigningCA::Expires).big_integer().not_null()) + .col(ColumnDef::new(SigningCA::Nonce).string().not_null().unique_key()) + .to_owned() + ).await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager.drop_table(Table::drop().table(SigningCA::Table).to_owned()).await + } +} + +/// Learn more at https://docs.rs/sea-query#iden +#[derive(Iden)] +pub enum SigningCA { + Table, + Id, + Organization, + Cert, + Key, + Expires, + Nonce +}