Spiegel von
https://github.com/dani-garcia/vaultwarden.git
synchronisiert 2024-07-04 13:14:42 +02:00
fix u2f migration
Dieser Commit ist enthalten in:
Ursprung
257829c6ab
Commit
035a843c71
1
Cargo.lock
generiert
1
Cargo.lock
generiert
|
@ -4143,6 +4143,7 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"webauthn-rs",
|
"webauthn-rs",
|
||||||
|
"webauthn-rs-core",
|
||||||
"which",
|
"which",
|
||||||
"yubico",
|
"yubico",
|
||||||
]
|
]
|
||||||
|
|
|
@ -109,7 +109,8 @@ totp-lite = "2.0.1"
|
||||||
yubico = { version = "0.11.0", features = ["online-tokio"], default-features = false }
|
yubico = { version = "0.11.0", features = ["online-tokio"], default-features = false }
|
||||||
|
|
||||||
# WebAuthn libraries
|
# WebAuthn libraries
|
||||||
webauthn-rs = { version = "0.4.8", features = ["danger-allow-state-serialisation"] }
|
webauthn-rs = { version = "0.4.8", features = ["danger-allow-state-serialisation", "danger-credential-internals", "resident-key-support"] }
|
||||||
|
webauthn-rs-core = { version = "0.4.9" }
|
||||||
|
|
||||||
# Handling of URL's for WebAuthn and favicons
|
# Handling of URL's for WebAuthn and favicons
|
||||||
url = "2.5.0"
|
url = "2.5.0"
|
||||||
|
|
|
@ -22,6 +22,47 @@ pub fn routes() -> Vec<Route> {
|
||||||
routes![get_webauthn, generate_webauthn_challenge, activate_webauthn, activate_webauthn_put, delete_webauthn,]
|
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: CredentialID,
|
||||||
|
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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WebauthnRegistration {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub migrated: bool,
|
||||||
|
|
||||||
|
pub security_key: SecurityKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebauthnRegistration {
|
||||||
|
fn to_json(&self) -> Value {
|
||||||
|
json!({
|
||||||
|
"Id": self.id,
|
||||||
|
"Name": self.name,
|
||||||
|
"migrated": self.migrated,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/two-factor/get-webauthn", data = "<data>")]
|
#[post("/two-factor/get-webauthn", data = "<data>")]
|
||||||
async fn get_webauthn(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
async fn get_webauthn(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||||
if !CONFIG.domain_set() {
|
if !CONFIG.domain_set() {
|
||||||
|
@ -168,11 +209,27 @@ async fn delete_webauthn(data: JsonUpcase<DeleteU2FData>, headers: Headers, mut
|
||||||
None => err!("Webauthn entry not found"),
|
None => err!("Webauthn entry not found"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let _removed_item = data.remove(item_pos);
|
let removed_item = data.remove(item_pos);
|
||||||
tf.data = serde_json::to_string(&data)?;
|
tf.data = serde_json::to_string(&data)?;
|
||||||
tf.save(&mut conn).await?;
|
tf.save(&mut conn).await?;
|
||||||
drop(tf);
|
drop(tf);
|
||||||
|
|
||||||
|
// 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, &mut conn).await
|
||||||
|
{
|
||||||
|
let mut data: Vec<U2FRegistration> = match serde_json::from_str(&u2f.data) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(_) => err!("Error parsing U2F data"),
|
||||||
|
};
|
||||||
|
|
||||||
|
data.retain(|r| r.reg.key_handle != *removed_item.security_key.cred_id());
|
||||||
|
let new_data_str = serde_json::to_string(&data)?;
|
||||||
|
|
||||||
|
u2f.data = new_data_str;
|
||||||
|
u2f.save(&mut conn).await?;
|
||||||
|
}
|
||||||
|
|
||||||
let keys_json: Vec<Value> = data.iter().map(WebauthnRegistration::to_json).collect();
|
let keys_json: Vec<Value> = data.iter().map(WebauthnRegistration::to_json).collect();
|
||||||
|
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
|
@ -182,26 +239,7 @@ async fn delete_webauthn(data: JsonUpcase<DeleteU2FData>, headers: Headers, mut
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
pub async fn get_webauthn_registrations(
|
||||||
struct WebauthnRegistration {
|
|
||||||
pub id: i32,
|
|
||||||
pub name: String,
|
|
||||||
pub migrated: bool,
|
|
||||||
|
|
||||||
pub security_key: SecurityKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WebauthnRegistration {
|
|
||||||
fn to_json(&self) -> Value {
|
|
||||||
json!({
|
|
||||||
"Id": self.id,
|
|
||||||
"Name": self.name,
|
|
||||||
"migrated": self.migrated,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_webauthn_registrations(
|
|
||||||
user_uuid: &str,
|
user_uuid: &str,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) -> Result<(bool, Vec<WebauthnRegistration>), Error> {
|
) -> Result<(bool, Vec<WebauthnRegistration>), Error> {
|
||||||
|
|
|
@ -159,7 +159,7 @@ impl TwoFactor {
|
||||||
|
|
||||||
use crate::api::core::two_factor::webauthn::U2FRegistration;
|
use crate::api::core::two_factor::webauthn::U2FRegistration;
|
||||||
use crate::api::core::two_factor::webauthn::{get_webauthn_registrations, WebauthnRegistration};
|
use crate::api::core::two_factor::webauthn::{get_webauthn_registrations, WebauthnRegistration};
|
||||||
use webauthn_rs::proto::*;
|
use webauthn_rs_core::proto::*;
|
||||||
|
|
||||||
for mut u2f in u2f_factors {
|
for mut u2f in u2f_factors {
|
||||||
let mut regs: Vec<U2FRegistration> = serde_json::from_str(&u2f.data)?;
|
let mut regs: Vec<U2FRegistration> = serde_json::from_str(&u2f.data)?;
|
||||||
|
@ -183,22 +183,23 @@ impl TwoFactor {
|
||||||
type_: COSEAlgorithm::ES256,
|
type_: COSEAlgorithm::ES256,
|
||||||
key: COSEKeyType::EC_EC2(COSEEC2Key {
|
key: COSEKeyType::EC_EC2(COSEEC2Key {
|
||||||
curve: ECDSACurve::SECP256R1,
|
curve: ECDSACurve::SECP256R1,
|
||||||
x,
|
x: x.to_vec().into(),
|
||||||
y,
|
y: y.to_vec().into(),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let credential = CredentialV3 {
|
||||||
|
counter: reg.counter,
|
||||||
|
verified: false,
|
||||||
|
cred: key,
|
||||||
|
cred_id: reg.reg.key_handle.clone().into(),
|
||||||
|
registration_policy: UserVerificationPolicy::Preferred,
|
||||||
|
};
|
||||||
let new_reg = WebauthnRegistration {
|
let new_reg = WebauthnRegistration {
|
||||||
id: reg.id,
|
id: reg.id,
|
||||||
migrated: true,
|
migrated: true,
|
||||||
name: reg.name.clone(),
|
name: reg.name.clone(),
|
||||||
credential: Credential {
|
security_key: (Credential::from(credential).into()),
|
||||||
counter: reg.counter,
|
|
||||||
verified: false,
|
|
||||||
cred: key,
|
|
||||||
cred_id: reg.reg.key_handle.clone(),
|
|
||||||
registration_policy: UserVerificationPolicy::Discouraged,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
webauthn_regs.push(new_reg);
|
webauthn_regs.push(new_reg);
|
||||||
|
|
Laden …
In neuem Issue referenzieren