Skip to content

Commit

Permalink
[PM-6761] Add fido credentials to data model (#657)
Browse files Browse the repository at this point in the history
Add `Fido2Credentials` to the `Login` struct to support storing
passkeys. Currently we don't decrypt passkeys but rather pass them along
encrypted to ensure we can re-encrypt the login without data loss.
  • Loading branch information
Hinton authored Mar 14, 2024
1 parent 07a4ceb commit aaa6466
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 0 deletions.
1 change: 1 addition & 0 deletions crates/bitwarden/src/tool/exporters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ mod tests {
uris: None,
totp: None,
autofill_on_page_load: None,
fido2_credentials: None,
}),
id: "fd411a1a-fec8-4070-985d-0e6560860e69".parse().ok(),
organization_id: None,
Expand Down
1 change: 1 addition & 0 deletions crates/bitwarden/src/vault/cipher/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ mod tests {
uris: None,
totp: None,
autofill_on_page_load: None,
fido2_credentials: None,
}),
id: "fd411a1a-fec8-4070-985d-0e6560860e69".parse().ok(),
organization_id: None,
Expand Down
56 changes: 56 additions & 0 deletions crates/bitwarden/src/vault/cipher/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ pub struct LoginUriView {
pub r#match: Option<UriMatchType>,
}

#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "mobile", derive(uniffi::Record))]
pub struct Fido2Credential {
pub credential_id: EncString,
pub key_type: EncString,
pub key_algorithm: EncString,
pub key_curve: EncString,
pub key_value: EncString,
pub rp_id: EncString,
pub user_handle: Option<EncString>,
pub user_name: Option<EncString>,
pub counter: EncString,
pub rp_name: Option<EncString>,
pub user_display_name: Option<EncString>,
pub discoverable: EncString,
pub creation_date: DateTime<Utc>,
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "mobile", derive(uniffi::Record))]
Expand All @@ -49,6 +68,8 @@ pub struct Login {
pub uris: Option<Vec<LoginUri>>,
pub totp: Option<EncString>,
pub autofill_on_page_load: Option<bool>,

pub fido2_credentials: Option<Vec<Fido2Credential>>,
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
Expand All @@ -62,6 +83,9 @@ pub struct LoginView {
pub uris: Option<Vec<LoginUriView>>,
pub totp: Option<String>,
pub autofill_on_page_load: Option<bool>,

// TODO: Remove this once the SDK supports state
pub fido2_credentials: Option<Vec<Fido2Credential>>,
}

impl KeyEncryptable<SymmetricCryptoKey, LoginUri> for LoginUriView {
Expand All @@ -82,6 +106,7 @@ impl KeyEncryptable<SymmetricCryptoKey, Login> for LoginView {
uris: self.uris.encrypt_with_key(key)?,
totp: self.totp.encrypt_with_key(key)?,
autofill_on_page_load: self.autofill_on_page_load,
fido2_credentials: self.fido2_credentials,
})
}
}
Expand All @@ -104,6 +129,7 @@ impl KeyDecryptable<SymmetricCryptoKey, LoginView> for Login {
uris: self.uris.decrypt_with_key(key).ok().flatten(),
totp: self.totp.decrypt_with_key(key).ok().flatten(),
autofill_on_page_load: self.autofill_on_page_load,
fido2_credentials: self.fido2_credentials.clone(),
})
}
}
Expand All @@ -125,6 +151,10 @@ impl TryFrom<CipherLoginModel> for Login {
.transpose()?,
totp: EncString::try_from_optional(login.totp)?,
autofill_on_page_load: login.autofill_on_page_load,
fido2_credentials: login
.fido2_credentials
.map(|v| v.into_iter().map(|c| c.try_into()).collect())
.transpose()?,
})
}
}
Expand Down Expand Up @@ -152,3 +182,29 @@ impl From<bitwarden_api_api::models::UriMatchType> for UriMatchType {
}
}
}

impl TryFrom<bitwarden_api_api::models::CipherFido2CredentialModel> for Fido2Credential {
type Error = Error;

fn try_from(value: bitwarden_api_api::models::CipherFido2CredentialModel) -> Result<Self> {
Ok(Self {
credential_id: value.credential_id.ok_or(Error::MissingFields)?.parse()?,
key_type: value.key_type.ok_or(Error::MissingFields)?.parse()?,
key_algorithm: value.key_algorithm.ok_or(Error::MissingFields)?.parse()?,
key_curve: value.key_curve.ok_or(Error::MissingFields)?.parse()?,
key_value: value.key_value.ok_or(Error::MissingFields)?.parse()?,
rp_id: value.rp_id.ok_or(Error::MissingFields)?.parse()?,
user_handle: EncString::try_from_optional(value.user_handle)
.ok()
.flatten(),
user_name: EncString::try_from_optional(value.user_name).ok().flatten(),
counter: value.counter.ok_or(Error::MissingFields)?.parse()?,
rp_name: EncString::try_from_optional(value.rp_name).ok().flatten(),
user_display_name: EncString::try_from_optional(value.user_display_name)
.ok()
.flatten(),
discoverable: value.discoverable.ok_or(Error::MissingFields)?.parse()?,
creation_date: value.creation_date.parse().unwrap(),
})
}
}

0 comments on commit aaa6466

Please sign in to comment.