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
12use std::str::FromStr;
13
14pub use asymmetric::UnsignedSharedKey;
15use base64::{engine::general_purpose::STANDARD, Engine};
16pub use symmetric::EncString;
17
18use crate::error::{EncStringParseError, Result};
19
20fn check_length(buf: &[u8], expected: usize) -> Result<()> {
21    if buf.len() < expected {
22        return Err(EncStringParseError::InvalidLength {
23            expected,
24            got: buf.len(),
25        }
26        .into());
27    }
28    Ok(())
29}
30
31fn from_b64_vec(s: &str) -> Result<Vec<u8>> {
32    Ok(STANDARD
33        .decode(s)
34        .map_err(EncStringParseError::InvalidBase64)?)
35}
36
37fn from_b64<const N: usize>(s: &str) -> Result<[u8; N]> {
38    Ok(from_b64_vec(s)?
39        .try_into()
40        .map_err(|e: Vec<_>| EncStringParseError::InvalidLength {
41            expected: N,
42            got: e.len(),
43        })?)
44}
45
46fn split_enc_string(s: &str) -> (&str, Vec<&str>) {
47    let header_parts: Vec<_> = s.split('.').collect();
48
49    if header_parts.len() == 2 {
50        (header_parts[0], header_parts[1].split('|').collect())
51    } else {
52        // Support legacy format with no header
53        let parts: Vec<_> = s.split('|').collect();
54        if parts.len() == 3 {
55            ("1", parts) // Aes128Cbc_HmacSha256_B64
56        } else {
57            ("0", parts) // Aes256Cbc_B64
58        }
59    }
60}
61
62struct FromStrVisitor<T>(std::marker::PhantomData<T>);
63impl<T> FromStrVisitor<T> {
64    fn new() -> Self {
65        Self(Default::default())
66    }
67}
68impl<T: FromStr> serde::de::Visitor<'_> for FromStrVisitor<T>
69where
70    T::Err: std::fmt::Debug,
71{
72    type Value = T;
73
74    fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
75        write!(f, "a valid string")
76    }
77
78    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
79    where
80        E: serde::de::Error,
81    {
82        T::from_str(v).map_err(|e| E::custom(format!("{:?}", e)))
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_check_length_less_than_expected() {
92        let buf = [1, 2, 3];
93        let expected = 5;
94        let result = check_length(&buf, expected);
95        assert!(result.is_err());
96    }
97
98    #[test]
99    fn test_check_length_equal_to_expected() {
100        let buf = [1, 2, 3, 4, 5];
101        let expected = 5;
102        let result = check_length(&buf, expected);
103        assert!(result.is_ok());
104    }
105
106    #[test]
107    fn test_check_length_greater_than_expected() {
108        let buf = [1, 2, 3, 4, 5, 6];
109        let expected = 5;
110        let result = check_length(&buf, expected);
111        assert!(result.is_ok());
112    }
113
114    #[test]
115    fn test_split_enc_string_new_format() {
116        let s = "2.abc|def|ghi";
117        let (header, parts) = split_enc_string(s);
118        assert_eq!(header, "2");
119        assert_eq!(parts, vec!["abc", "def", "ghi"]);
120    }
121
122    #[test]
123    fn test_split_enc_string_old_format_three_parts() {
124        let s = "abc|def|ghi";
125        let (header, parts) = split_enc_string(s);
126        assert_eq!(header, "1");
127        assert_eq!(parts, vec!["abc", "def", "ghi"]);
128    }
129
130    #[test]
131    fn test_split_enc_string_old_format_fewer_parts() {
132        let s = "abc|def";
133        let (header, parts) = split_enc_string(s);
134        assert_eq!(header, "0");
135        assert_eq!(parts, vec!["abc", "def"]);
136    }
137}