Skip to main content

bitwarden_crypto_cipher_suite/
cipher_suite_client.rs

1use bitwarden_core::{Client, FromClient, key_management::KeySlotIds};
2use bitwarden_crypto::{Kdf, KeyStore};
3#[cfg(feature = "wasm")]
4use wasm_bindgen::prelude::wasm_bindgen;
5
6/// Tells you which cryptographic algorithms to use for the current account and environment.
7///
8/// Some environments are restricted in which algorithms they may use — for example, government
9/// (FedRAMP) deployments must use FIPS-approved algorithms. Ask this client which algorithm to use
10/// instead of picking one yourself, so the choice stays correct as those rules change.
11#[cfg_attr(feature = "wasm", wasm_bindgen)]
12#[derive(FromClient)]
13pub struct CryptoCipherSuiteClient {
14    pub(crate) key_store: KeyStore<KeySlotIds>,
15}
16
17#[cfg_attr(feature = "wasm", wasm_bindgen)]
18impl CryptoCipherSuiteClient {
19    /// Returns the KDF a new account should use in the current environment.
20    ///
21    /// In a FIPS (gov) environment the FIPS-approved PBKDF2 is used; otherwise the modern Argon2id
22    /// default.
23    pub fn default_kdf_for_new_account(&self) -> Kdf {
24        self.key_store
25            .context()
26            .cipher_suite()
27            .default_kdf_for_new_account()
28    }
29
30    /// Returns whether the given KDF is allowed in the current environment.
31    ///
32    /// Intended for surfaces that let a user pick a KDF (e.g. the Change KDF settings screen): the
33    /// caller can validate or filter the options it offers. In a FIPS (gov) environment only PBKDF2
34    /// is allowed; otherwise every supported KDF is allowed.
35    pub fn is_kdf_compliant(&self, kdf: Kdf) -> bool {
36        self.key_store
37            .context()
38            .cipher_suite()
39            .is_kdf_compliant(&kdf)
40    }
41}
42
43/// Extension trait that exposes [`CryptoCipherSuiteClient`] on [`Client`].
44pub trait CryptoCipherSuiteClientExt {
45    /// Returns a [`CryptoCipherSuiteClient`] for the current environment.
46    fn crypto_cipher_suite(&self) -> CryptoCipherSuiteClient;
47}
48
49impl CryptoCipherSuiteClientExt for Client {
50    fn crypto_cipher_suite(&self) -> CryptoCipherSuiteClient {
51        CryptoCipherSuiteClient::from_client(self)
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use bitwarden_core::{Client, ClientSettings};
58    use bitwarden_crypto::CipherSuite;
59
60    use super::*;
61
62    fn client_with_suite(cipher_suite: CipherSuite) -> Client {
63        let client = Client::new(Some(ClientSettings::default()));
64        client
65            .internal
66            .get_key_store()
67            .set_cipher_suite(cipher_suite);
68        client
69    }
70
71    #[test]
72    fn default_kdf_for_new_account_is_argon2_under_standard() {
73        let kdf = client_with_suite(CipherSuite::Standard)
74            .crypto_cipher_suite()
75            .default_kdf_for_new_account();
76        assert!(matches!(kdf, Kdf::Argon2id { .. }));
77    }
78
79    #[test]
80    fn default_kdf_for_new_account_is_pbkdf2_under_fips() {
81        let kdf = client_with_suite(CipherSuite::Fips)
82            .crypto_cipher_suite()
83            .default_kdf_for_new_account();
84        assert!(matches!(kdf, Kdf::PBKDF2 { .. }));
85    }
86
87    #[test]
88    fn every_kdf_is_compliant_under_standard() {
89        let client = client_with_suite(CipherSuite::Standard).crypto_cipher_suite();
90        assert!(client.is_kdf_compliant(Kdf::default_argon2()));
91        assert!(client.is_kdf_compliant(Kdf::default_pbkdf2()));
92    }
93
94    #[test]
95    fn only_pbkdf2_is_compliant_under_fips() {
96        let client = client_with_suite(CipherSuite::Fips).crypto_cipher_suite();
97        assert!(client.is_kdf_compliant(Kdf::default_pbkdf2()));
98        assert!(!client.is_kdf_compliant(Kdf::default_argon2()));
99    }
100}