bitwarden_crypto/enc_string/
mod.rs

1//! Encrypted string types
2//!
3//! [EncString] and [UnsignedSharedKey] are Bitwarden specific primitive that represents a
4//! encrypted string. They are are used together with the [KeyDecryptable][crate::KeyDecryptable]
5//! and [KeyEncryptable][crate::KeyEncryptable] traits to encrypt and decrypt data using
6//! [SymmetricCryptoKey][crate::SymmetricCryptoKey] and
7//! [AsymmetricCryptoKey][crate::AsymmetricCryptoKey]s.
8
9mod asymmetric;
10mod symmetric;
11
12pub use asymmetric::UnsignedSharedKey;
13use base64::{engine::general_purpose::STANDARD, Engine};
14pub use symmetric::EncString;
15
16use crate::error::{EncStringParseError, Result};
17
18fn check_length(buf: &[u8], expected: usize) -> Result<()> {
19    if buf.len() < expected {
20        return Err(EncStringParseError::InvalidLength {
21            expected,
22            got: buf.len(),
23        }
24        .into());
25    }
26    Ok(())
27}
28
29fn from_b64_vec(s: &str) -> Result<Vec<u8>> {
30    Ok(STANDARD
31        .decode(s)
32        .map_err(EncStringParseError::InvalidBase64)?)
33}
34
35fn from_b64<const N: usize>(s: &str) -> Result<[u8; N]> {
36    Ok(from_b64_vec(s)?
37        .try_into()
38        .map_err(|e: Vec<_>| EncStringParseError::InvalidLength {
39            expected: N,
40            got: e.len(),
41        })?)
42}
43
44fn split_enc_string(s: &str) -> (&str, Vec<&str>) {
45    let header_parts: Vec<_> = s.split('.').collect();
46
47    if header_parts.len() == 2 {
48        (header_parts[0], header_parts[1].split('|').collect())
49    } else {
50        // Support legacy format with no header
51        let parts: Vec<_> = s.split('|').collect();
52        if parts.len() == 3 {
53            ("1", parts) // Aes128Cbc_HmacSha256_B64
54        } else {
55            ("0", parts) // Aes256Cbc_B64
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_check_length_less_than_expected() {
66        let buf = [1, 2, 3];
67        let expected = 5;
68        let result = check_length(&buf, expected);
69        assert!(result.is_err());
70    }
71
72    #[test]
73    fn test_check_length_equal_to_expected() {
74        let buf = [1, 2, 3, 4, 5];
75        let expected = 5;
76        let result = check_length(&buf, expected);
77        assert!(result.is_ok());
78    }
79
80    #[test]
81    fn test_check_length_greater_than_expected() {
82        let buf = [1, 2, 3, 4, 5, 6];
83        let expected = 5;
84        let result = check_length(&buf, expected);
85        assert!(result.is_ok());
86    }
87
88    #[test]
89    fn test_split_enc_string_new_format() {
90        let s = "2.abc|def|ghi";
91        let (header, parts) = split_enc_string(s);
92        assert_eq!(header, "2");
93        assert_eq!(parts, vec!["abc", "def", "ghi"]);
94    }
95
96    #[test]
97    fn test_split_enc_string_old_format_three_parts() {
98        let s = "abc|def|ghi";
99        let (header, parts) = split_enc_string(s);
100        assert_eq!(header, "1");
101        assert_eq!(parts, vec!["abc", "def", "ghi"]);
102    }
103
104    #[test]
105    fn test_split_enc_string_old_format_fewer_parts() {
106        let s = "abc|def";
107        let (header, parts) = split_enc_string(s);
108        assert_eq!(header, "0");
109        assert_eq!(parts, vec!["abc", "def"]);
110    }
111}