mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-31 13:51:14 +00:00 
			
		
		
		
	Remove u2f implementation
For a while now WebAuthn has replaced u2f. And since web-vault v2.27.0 the connector files for u2f have been removed. Also, on the official bitwarden server the endpoint to `/two-factor/get-u2f` results in a 404. - Removed all u2f code except the migration code from u2f to WebAuthn
This commit is contained in:
		
					parent
					
						
							
								6643e83b61
							
						
					
				
			
			
				commit
				
					
						fb0c23b71f
					
				
			
		
					 8 changed files with 32 additions and 414 deletions
				
			
		
							
								
								
									
										40
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										40
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -184,12 +184,6 @@ version = "0.2.8" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "base64" | ||||
| version = "0.11.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "base64" | ||||
| version = "0.13.0" | ||||
|  | @ -435,7 +429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" | ||||
| dependencies = [ | ||||
|  "aes-gcm", | ||||
|  "base64 0.13.0", | ||||
|  "base64", | ||||
|  "hkdf", | ||||
|  "hmac 0.12.1", | ||||
|  "percent-encoding 2.1.0", | ||||
|  | @ -1404,7 +1398,7 @@ version = "8.0.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "012bb02250fdd38faa5feee63235f7a459974440b9b57593822414c31f92839e" | ||||
| dependencies = [ | ||||
|  "base64 0.13.0", | ||||
|  "base64", | ||||
|  "pem", | ||||
|  "ring", | ||||
|  "serde", | ||||
|  | @ -1440,7 +1434,7 @@ version = "0.10.0-rc.4" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "71d8da8f34d086b081c9cc3b57d3bb3b51d16fc06b5c848a188e2f14d58ac2a5" | ||||
| dependencies = [ | ||||
|  "base64 0.13.0", | ||||
|  "base64", | ||||
|  "fastrand", | ||||
|  "hostname", | ||||
|  "httpdate", | ||||
|  | @ -2087,7 +2081,7 @@ version = "1.0.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e9a3b09a20e374558580a4914d3b7d89bd61b954a5a5e1dcbea98753addb1947" | ||||
| dependencies = [ | ||||
|  "base64 0.13.0", | ||||
|  "base64", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -2543,7 +2537,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" | ||||
| dependencies = [ | ||||
|  "async-compression", | ||||
|  "base64 0.13.0", | ||||
|  "base64", | ||||
|  "bytes 1.1.0", | ||||
|  "cookie 0.15.1", | ||||
|  "cookie_store", | ||||
|  | @ -2738,7 +2732,7 @@ version = "0.3.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" | ||||
| dependencies = [ | ||||
|  "base64 0.13.0", | ||||
|  "base64", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -3549,23 +3543,6 @@ version = "1.15.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "u2f" | ||||
| version = "0.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e2f285392366190c4d46823458f4543ac0f35174759c78e80c5baa39e1f7aa4f" | ||||
| dependencies = [ | ||||
|  "base64 0.11.0", | ||||
|  "byteorder", | ||||
|  "bytes 0.4.12", | ||||
|  "chrono", | ||||
|  "openssl", | ||||
|  "serde", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
|  "time 0.1.43", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "ubyte" | ||||
| version = "0.10.1" | ||||
|  | @ -3718,7 +3695,6 @@ dependencies = [ | |||
|  "tokio", | ||||
|  "totp-lite", | ||||
|  "tracing", | ||||
|  "u2f", | ||||
|  "url 2.2.2", | ||||
|  "uuid", | ||||
|  "webauthn-rs", | ||||
|  | @ -3858,7 +3834,7 @@ version = "0.3.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "90b266eccb4b32595876f5c73ea443b0516da0b1df72ca07bc08ed9ba7f96ec1" | ||||
| dependencies = [ | ||||
|  "base64 0.13.0", | ||||
|  "base64", | ||||
|  "nom 7.1.1", | ||||
|  "openssl", | ||||
|  "rand 0.8.5", | ||||
|  | @ -4013,7 +3989,7 @@ version = "0.10.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4d3c3f584739059f479ca4de114cbfe032315752abb3be60afb30db40a802169" | ||||
| dependencies = [ | ||||
|  "base64 0.13.0", | ||||
|  "base64", | ||||
|  "crypto-mac 0.10.1", | ||||
|  "futures", | ||||
|  "hmac 0.10.1", | ||||
|  |  | |||
|  | @ -100,8 +100,7 @@ totp-lite = "1.0.3" | |||
| # Yubico Library | ||||
| yubico = { version = "0.10.0", features = ["online-tokio"], default-features = false } | ||||
| 
 | ||||
| # U2F libraries | ||||
| u2f = "0.2.0" | ||||
| # WebAuthn libraries | ||||
| webauthn-rs = "0.3.2" | ||||
| 
 | ||||
| # Handling of URL's for WebAuthn | ||||
|  |  | |||
|  | @ -15,7 +15,6 @@ use crate::{ | |||
| pub mod authenticator; | ||||
| pub mod duo; | ||||
| pub mod email; | ||||
| pub mod u2f; | ||||
| pub mod webauthn; | ||||
| pub mod yubikey; | ||||
| 
 | ||||
|  | @ -25,7 +24,6 @@ pub fn routes() -> Vec<Route> { | |||
|     routes.append(&mut authenticator::routes()); | ||||
|     routes.append(&mut duo::routes()); | ||||
|     routes.append(&mut email::routes()); | ||||
|     routes.append(&mut u2f::routes()); | ||||
|     routes.append(&mut webauthn::routes()); | ||||
|     routes.append(&mut yubikey::routes()); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,353 +0,0 @@ | |||
| use once_cell::sync::Lazy; | ||||
| use rocket::serde::json::Json; | ||||
| use rocket::Route; | ||||
| use serde_json::Value; | ||||
| use u2f::{ | ||||
|     messages::{RegisterResponse, SignResponse, U2fSignRequest}, | ||||
|     protocol::{Challenge, U2f}, | ||||
|     register::Registration, | ||||
| }; | ||||
| 
 | ||||
| use crate::{ | ||||
|     api::{ | ||||
|         core::two_factor::_generate_recover_code, ApiResult, EmptyResult, JsonResult, JsonUpcase, NumberOrString, | ||||
|         PasswordData, | ||||
|     }, | ||||
|     auth::Headers, | ||||
|     db::{ | ||||
|         models::{TwoFactor, TwoFactorType}, | ||||
|         DbConn, | ||||
|     }, | ||||
|     error::Error, | ||||
|     CONFIG, | ||||
| }; | ||||
| 
 | ||||
| const U2F_VERSION: &str = "U2F_V2"; | ||||
| 
 | ||||
| static APP_ID: Lazy<String> = Lazy::new(|| format!("{}/app-id.json", &CONFIG.domain())); | ||||
| static U2F: Lazy<U2f> = Lazy::new(|| U2f::new(APP_ID.clone())); | ||||
| 
 | ||||
| pub fn routes() -> Vec<Route> { | ||||
|     routes![generate_u2f, generate_u2f_challenge, activate_u2f, activate_u2f_put, delete_u2f,] | ||||
| } | ||||
| 
 | ||||
| #[post("/two-factor/get-u2f", data = "<data>")] | ||||
| async fn generate_u2f(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     if !CONFIG.domain_set() { | ||||
|         err!("`DOMAIN` environment variable is not set. U2F disabled") | ||||
|     } | ||||
|     let data: PasswordData = data.into_inner().data; | ||||
| 
 | ||||
|     if !headers.user.check_valid_password(&data.MasterPasswordHash) { | ||||
|         err!("Invalid password"); | ||||
|     } | ||||
| 
 | ||||
|     let (enabled, keys) = get_u2f_registrations(&headers.user.uuid, &conn).await?; | ||||
|     let keys_json: Vec<Value> = keys.iter().map(U2FRegistration::to_json).collect(); | ||||
| 
 | ||||
|     Ok(Json(json!({ | ||||
|         "Enabled": enabled, | ||||
|         "Keys": keys_json, | ||||
|         "Object": "twoFactorU2f" | ||||
|     }))) | ||||
| } | ||||
| 
 | ||||
| #[post("/two-factor/get-u2f-challenge", data = "<data>")] | ||||
| async fn generate_u2f_challenge(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     let data: PasswordData = data.into_inner().data; | ||||
| 
 | ||||
|     if !headers.user.check_valid_password(&data.MasterPasswordHash) { | ||||
|         err!("Invalid password"); | ||||
|     } | ||||
| 
 | ||||
|     let _type = TwoFactorType::U2fRegisterChallenge; | ||||
|     let challenge = _create_u2f_challenge(&headers.user.uuid, _type, &conn).await.challenge; | ||||
| 
 | ||||
|     Ok(Json(json!({ | ||||
|         "UserId": headers.user.uuid, | ||||
|         "AppId": APP_ID.to_string(), | ||||
|         "Challenge": challenge, | ||||
|         "Version": U2F_VERSION, | ||||
|     }))) | ||||
| } | ||||
| 
 | ||||
| #[derive(Deserialize, Debug)] | ||||
| #[allow(non_snake_case)] | ||||
| struct EnableU2FData { | ||||
|     Id: NumberOrString, | ||||
|     // 1..5
 | ||||
|     Name: String, | ||||
|     MasterPasswordHash: String, | ||||
|     DeviceResponse: String, | ||||
| } | ||||
| 
 | ||||
| // This struct is referenced from the U2F lib
 | ||||
| // because it doesn't implement Deserialize
 | ||||
| #[derive(Serialize, Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| #[serde(remote = "Registration")] | ||||
| struct RegistrationDef { | ||||
|     key_handle: Vec<u8>, | ||||
|     pub_key: Vec<u8>, | ||||
|     attestation_cert: Option<Vec<u8>>, | ||||
|     device_name: Option<String>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize)] | ||||
| pub struct U2FRegistration { | ||||
|     pub id: i32, | ||||
|     pub name: String, | ||||
|     #[serde(with = "RegistrationDef")] | ||||
|     pub reg: Registration, | ||||
|     pub counter: u32, | ||||
|     compromised: bool, | ||||
|     pub migrated: Option<bool>, | ||||
| } | ||||
| 
 | ||||
| impl U2FRegistration { | ||||
|     fn to_json(&self) -> Value { | ||||
|         json!({ | ||||
|             "Id": self.id, | ||||
|             "Name": self.name, | ||||
|             "Compromised": self.compromised, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // This struct is copied from the U2F lib
 | ||||
| // to add an optional error code
 | ||||
| #[derive(Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| struct RegisterResponseCopy { | ||||
|     pub registration_data: String, | ||||
|     pub version: String, | ||||
|     pub client_data: String, | ||||
| 
 | ||||
|     pub error_code: Option<NumberOrString>, | ||||
| } | ||||
| 
 | ||||
| impl From<RegisterResponseCopy> for RegisterResponse { | ||||
|     fn from(r: RegisterResponseCopy) -> RegisterResponse { | ||||
|         RegisterResponse { | ||||
|             registration_data: r.registration_data, | ||||
|             version: r.version, | ||||
|             client_data: r.client_data, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[post("/two-factor/u2f", data = "<data>")] | ||||
| async fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     let data: EnableU2FData = data.into_inner().data; | ||||
|     let mut user = headers.user; | ||||
| 
 | ||||
|     if !user.check_valid_password(&data.MasterPasswordHash) { | ||||
|         err!("Invalid password"); | ||||
|     } | ||||
| 
 | ||||
|     let tf_type = TwoFactorType::U2fRegisterChallenge as i32; | ||||
|     let tf_challenge = match TwoFactor::find_by_user_and_type(&user.uuid, tf_type, &conn).await { | ||||
|         Some(c) => c, | ||||
|         None => err!("Can't recover challenge"), | ||||
|     }; | ||||
| 
 | ||||
|     let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?; | ||||
|     tf_challenge.delete(&conn).await?; | ||||
| 
 | ||||
|     let response: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?; | ||||
| 
 | ||||
|     let error_code = response.error_code.clone().map_or("0".into(), NumberOrString::into_string); | ||||
| 
 | ||||
|     if error_code != "0" { | ||||
|         err!("Error registering U2F token") | ||||
|     } | ||||
| 
 | ||||
|     let registration = U2F.register_response(challenge, response.into())?; | ||||
|     let full_registration = U2FRegistration { | ||||
|         id: data.Id.into_i32()?, | ||||
|         name: data.Name, | ||||
|         reg: registration, | ||||
|         compromised: false, | ||||
|         counter: 0, | ||||
|         migrated: None, | ||||
|     }; | ||||
| 
 | ||||
|     let mut regs = get_u2f_registrations(&user.uuid, &conn).await?.1; | ||||
| 
 | ||||
|     // TODO: Check that there is no repeat Id
 | ||||
|     regs.push(full_registration); | ||||
|     save_u2f_registrations(&user.uuid, ®s, &conn).await?; | ||||
| 
 | ||||
|     _generate_recover_code(&mut user, &conn).await; | ||||
| 
 | ||||
|     let keys_json: Vec<Value> = regs.iter().map(U2FRegistration::to_json).collect(); | ||||
|     Ok(Json(json!({ | ||||
|         "Enabled": true, | ||||
|         "Keys": keys_json, | ||||
|         "Object": "twoFactorU2f" | ||||
|     }))) | ||||
| } | ||||
| 
 | ||||
| #[put("/two-factor/u2f", data = "<data>")] | ||||
| async fn activate_u2f_put(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     activate_u2f(data, headers, conn).await | ||||
| } | ||||
| 
 | ||||
| #[derive(Deserialize, Debug)] | ||||
| #[allow(non_snake_case)] | ||||
| struct DeleteU2FData { | ||||
|     Id: NumberOrString, | ||||
|     MasterPasswordHash: String, | ||||
| } | ||||
| 
 | ||||
| #[delete("/two-factor/u2f", data = "<data>")] | ||||
| async fn delete_u2f(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     let data: DeleteU2FData = data.into_inner().data; | ||||
| 
 | ||||
|     let id = data.Id.into_i32()?; | ||||
| 
 | ||||
|     if !headers.user.check_valid_password(&data.MasterPasswordHash) { | ||||
|         err!("Invalid password"); | ||||
|     } | ||||
| 
 | ||||
|     let type_ = TwoFactorType::U2f as i32; | ||||
|     let mut tf = match TwoFactor::find_by_user_and_type(&headers.user.uuid, type_, &conn).await { | ||||
|         Some(tf) => tf, | ||||
|         None => err!("U2F data not found!"), | ||||
|     }; | ||||
| 
 | ||||
|     let mut data: Vec<U2FRegistration> = match serde_json::from_str(&tf.data) { | ||||
|         Ok(d) => d, | ||||
|         Err(_) => err!("Error parsing U2F data"), | ||||
|     }; | ||||
| 
 | ||||
|     data.retain(|r| r.id != id); | ||||
| 
 | ||||
|     let new_data_str = serde_json::to_string(&data)?; | ||||
| 
 | ||||
|     tf.data = new_data_str; | ||||
|     tf.save(&conn).await?; | ||||
| 
 | ||||
|     let keys_json: Vec<Value> = data.iter().map(U2FRegistration::to_json).collect(); | ||||
| 
 | ||||
|     Ok(Json(json!({ | ||||
|         "Enabled": true, | ||||
|         "Keys": keys_json, | ||||
|         "Object": "twoFactorU2f" | ||||
|     }))) | ||||
| } | ||||
| 
 | ||||
| async fn _create_u2f_challenge(user_uuid: &str, type_: TwoFactorType, conn: &DbConn) -> Challenge { | ||||
|     let challenge = U2F.generate_challenge().unwrap(); | ||||
| 
 | ||||
|     TwoFactor::new(user_uuid.into(), type_, serde_json::to_string(&challenge).unwrap()) | ||||
|         .save(conn) | ||||
|         .await | ||||
|         .expect("Error saving challenge"); | ||||
| 
 | ||||
|     challenge | ||||
| } | ||||
| 
 | ||||
| async fn save_u2f_registrations(user_uuid: &str, regs: &[U2FRegistration], conn: &DbConn) -> EmptyResult { | ||||
|     TwoFactor::new(user_uuid.into(), TwoFactorType::U2f, serde_json::to_string(regs)?).save(conn).await | ||||
| } | ||||
| 
 | ||||
| async fn get_u2f_registrations(user_uuid: &str, conn: &DbConn) -> Result<(bool, Vec<U2FRegistration>), Error> { | ||||
|     let type_ = TwoFactorType::U2f as i32; | ||||
|     let (enabled, regs) = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await { | ||||
|         Some(tf) => (tf.enabled, tf.data), | ||||
|         None => return Ok((false, Vec::new())), // If no data, return empty list
 | ||||
|     }; | ||||
| 
 | ||||
|     let data = match serde_json::from_str(®s) { | ||||
|         Ok(d) => d, | ||||
|         Err(_) => { | ||||
|             // If error, try old format
 | ||||
|             let mut old_regs = _old_parse_registrations(®s); | ||||
| 
 | ||||
|             if old_regs.len() != 1 { | ||||
|                 err!("The old U2F format only allows one device") | ||||
|             } | ||||
| 
 | ||||
|             // Convert to new format
 | ||||
|             let new_regs = vec![U2FRegistration { | ||||
|                 id: 1, | ||||
|                 name: "Unnamed U2F key".into(), | ||||
|                 reg: old_regs.remove(0), | ||||
|                 compromised: false, | ||||
|                 counter: 0, | ||||
|                 migrated: None, | ||||
|             }]; | ||||
| 
 | ||||
|             // Save new format
 | ||||
|             save_u2f_registrations(user_uuid, &new_regs, conn).await?; | ||||
| 
 | ||||
|             new_regs | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     Ok((enabled, data)) | ||||
| } | ||||
| 
 | ||||
| fn _old_parse_registrations(registations: &str) -> Vec<Registration> { | ||||
|     #[derive(Deserialize)] | ||||
|     struct Helper(#[serde(with = "RegistrationDef")] Registration); | ||||
| 
 | ||||
|     let regs: Vec<Value> = serde_json::from_str(registations).expect("Can't parse Registration data"); | ||||
| 
 | ||||
|     regs.into_iter().map(|r| serde_json::from_value(r).unwrap()).map(|Helper(r)| r).collect() | ||||
| } | ||||
| 
 | ||||
| pub async fn generate_u2f_login(user_uuid: &str, conn: &DbConn) -> ApiResult<U2fSignRequest> { | ||||
|     let challenge = _create_u2f_challenge(user_uuid, TwoFactorType::U2fLoginChallenge, conn).await; | ||||
| 
 | ||||
|     let registrations: Vec<_> = get_u2f_registrations(user_uuid, conn).await?.1.into_iter().map(|r| r.reg).collect(); | ||||
| 
 | ||||
|     if registrations.is_empty() { | ||||
|         err!("No U2F devices registered") | ||||
|     } | ||||
| 
 | ||||
|     Ok(U2F.sign_request(challenge, registrations)) | ||||
| } | ||||
| 
 | ||||
| pub async fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult { | ||||
|     let challenge_type = TwoFactorType::U2fLoginChallenge as i32; | ||||
|     let tf_challenge = TwoFactor::find_by_user_and_type(user_uuid, challenge_type, conn).await; | ||||
| 
 | ||||
|     let challenge = match tf_challenge { | ||||
|         Some(tf_challenge) => { | ||||
|             let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?; | ||||
|             tf_challenge.delete(conn).await?; | ||||
|             challenge | ||||
|         } | ||||
|         None => err!("Can't recover login challenge"), | ||||
|     }; | ||||
|     let response: SignResponse = serde_json::from_str(response)?; | ||||
|     let mut registrations = get_u2f_registrations(user_uuid, conn).await?.1; | ||||
|     if registrations.is_empty() { | ||||
|         err!("No U2F devices registered") | ||||
|     } | ||||
| 
 | ||||
|     for reg in &mut registrations { | ||||
|         let response = U2F.sign_response(challenge.clone(), reg.reg.clone(), response.clone(), reg.counter); | ||||
|         match response { | ||||
|             Ok(new_counter) => { | ||||
|                 reg.counter = new_counter; | ||||
|                 save_u2f_registrations(user_uuid, ®istrations, conn).await?; | ||||
| 
 | ||||
|                 return Ok(()); | ||||
|             } | ||||
|             Err(u2f::u2ferror::U2fError::CounterTooLow) => { | ||||
|                 reg.compromised = true; | ||||
|                 save_u2f_registrations(user_uuid, ®istrations, conn).await?; | ||||
| 
 | ||||
|                 err!("This device might be compromised!"); | ||||
|             } | ||||
|             Err(e) => { | ||||
|                 warn!("E {:#}", e); | ||||
|                 // break;
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     err!("error verifying response") | ||||
| } | ||||
|  | @ -21,6 +21,28 @@ pub fn routes() -> Vec<Route> { | |||
|     routes![get_webauthn, generate_webauthn_challenge, activate_webauthn, activate_webauthn_put, delete_webauthn,] | ||||
| } | ||||
| 
 | ||||
| // Some old u2f structs still needed for migrating from u2f to WebAuthn
 | ||||
| // Both `struct Registration` and `struct U2FRegistration` can be removed if we remove the u2f to WebAuthn migration
 | ||||
| #[derive(Serialize, Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct Registration { | ||||
|     pub key_handle: Vec<u8>, | ||||
|     pub pub_key: Vec<u8>, | ||||
|     pub attestation_cert: Option<Vec<u8>>, | ||||
|     pub device_name: Option<String>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize)] | ||||
| pub struct U2FRegistration { | ||||
|     pub id: i32, | ||||
|     pub name: String, | ||||
|     #[serde(with = "Registration")] | ||||
|     pub reg: Registration, | ||||
|     pub counter: u32, | ||||
|     compromised: bool, | ||||
|     pub migrated: Option<bool>, | ||||
| } | ||||
| 
 | ||||
| struct WebauthnConfig { | ||||
|     url: String, | ||||
|     origin: Url, | ||||
|  | @ -306,7 +328,6 @@ async fn delete_webauthn(data: JsonUpcase<DeleteU2FData>, headers: Headers, conn | |||
|     // If entry is migrated from u2f, delete the u2f entry as well
 | ||||
|     if let Some(mut u2f) = TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::U2f as i32, &conn).await | ||||
|     { | ||||
|         use crate::api::core::two_factor::u2f::U2FRegistration; | ||||
|         let mut data: Vec<U2FRegistration> = match serde_json::from_str(&u2f.data) { | ||||
|             Ok(d) => d, | ||||
|             Err(_) => err!("Error parsing U2F data"), | ||||
|  |  | |||
|  | @ -314,7 +314,6 @@ async fn twofactor_auth( | |||
|         Some(TwoFactorType::Authenticator) => { | ||||
|             _tf::authenticator::validate_totp_code_str(user_uuid, twofactor_code, &selected_data?, ip, conn).await? | ||||
|         } | ||||
|         Some(TwoFactorType::U2f) => _tf::u2f::validate_u2f_login(user_uuid, twofactor_code, conn).await?, | ||||
|         Some(TwoFactorType::Webauthn) => { | ||||
|             _tf::webauthn::validate_webauthn_login(user_uuid, twofactor_code, conn).await? | ||||
|         } | ||||
|  | @ -372,26 +371,6 @@ async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) | |||
|         match TwoFactorType::from_i32(*provider) { | ||||
|             Some(TwoFactorType::Authenticator) => { /* Nothing to do for TOTP */ } | ||||
| 
 | ||||
|             Some(TwoFactorType::U2f) if CONFIG.domain_set() => { | ||||
|                 let request = two_factor::u2f::generate_u2f_login(user_uuid, conn).await?; | ||||
|                 let mut challenge_list = Vec::new(); | ||||
| 
 | ||||
|                 for key in request.registered_keys { | ||||
|                     challenge_list.push(json!({ | ||||
|                         "appId": request.app_id, | ||||
|                         "challenge": request.challenge, | ||||
|                         "version": key.version, | ||||
|                         "keyHandle": key.key_handle, | ||||
|                     })); | ||||
|                 } | ||||
| 
 | ||||
|                 let challenge_list_str = serde_json::to_string(&challenge_list).unwrap(); | ||||
| 
 | ||||
|                 result["TwoFactorProviders2"][provider.to_string()] = json!({ | ||||
|                     "Challenges": challenge_list_str, | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             Some(TwoFactorType::Webauthn) if CONFIG.domain_set() => { | ||||
|                 let request = two_factor::webauthn::generate_webauthn_login(user_uuid, conn).await?; | ||||
|                 result["TwoFactorProviders2"][provider.to_string()] = request.0; | ||||
|  |  | |||
|  | @ -157,7 +157,7 @@ impl TwoFactor { | |||
|                 .from_db() | ||||
|         }}; | ||||
| 
 | ||||
|         use crate::api::core::two_factor::u2f::U2FRegistration; | ||||
|         use crate::api::core::two_factor::webauthn::U2FRegistration; | ||||
|         use crate::api::core::two_factor::webauthn::{get_webauthn_registrations, WebauthnRegistration}; | ||||
|         use webauthn_rs::proto::*; | ||||
| 
 | ||||
|  |  | |||
|  | @ -49,7 +49,6 @@ use rocket::error::Error as RocketErr; | |||
| use serde_json::{Error as SerdeErr, Value}; | ||||
| use std::io::Error as IoErr; | ||||
| use std::time::SystemTimeError as TimeErr; | ||||
| use u2f::u2ferror::U2fError as U2fErr; | ||||
| use webauthn_rs::error::WebauthnError as WebauthnErr; | ||||
| use yubico::yubicoerror::YubicoError as YubiErr; | ||||
| 
 | ||||
|  | @ -70,7 +69,6 @@ make_error! { | |||
|     Json(Value):     _no_source,  _serialize, | ||||
|     Db(DieselErr):   _has_source, _api_error, | ||||
|     R2d2(R2d2Err):   _has_source, _api_error, | ||||
|     U2f(U2fErr):     _has_source, _api_error, | ||||
|     Serde(SerdeErr): _has_source, _api_error, | ||||
|     JWt(JwtErr):     _has_source, _api_error, | ||||
|     Handlebars(HbErr): _has_source, _api_error, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue