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