mega/types/
folder_key.rs

1use base64::Engine;
2use base64::engine::general_purpose::URL_SAFE_NO_PAD;
3
4const KEY_SIZE: usize = 16;
5pub(crate) const BASE64_LEN: usize = 22;
6const BASE64_DECODE_BUFFER_LEN: usize = (BASE64_LEN * 2).div_ceil(4) * 3;
7
8/// An error that may occur while parsing a FolderKey.
9#[derive(Debug, thiserror::Error)]
10pub enum ParseError {
11    /// The base64 string is the wrong size
12    #[error("invalid base64 length \"{length}\", expected length of \"{BASE64_LEN}\"")]
13    InvalidBase64Length { length: usize },
14
15    /// An error occured while decoding base64
16    #[error("base64 decode error")]
17    Base64Decode(#[from] base64::DecodeSliceError),
18
19    /// The key is the wrong size
20    #[error("invalid key length \"{length}\", expected length of \"{KEY_SIZE}\"")]
21    InvalidLength { length: usize },
22}
23
24/// The encryption key for a folder.
25///
26/// This is a 128 bit AES key.
27#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
28#[serde(into = "String", try_from = "String")]
29pub struct FolderKey(pub u128);
30
31impl std::str::FromStr for FolderKey {
32    type Err = ParseError;
33
34    fn from_str(input: &str) -> Result<Self, Self::Err> {
35        let length = input.len();
36        if length < BASE64_LEN {
37            return Err(ParseError::InvalidBase64Length { length });
38        }
39
40        let mut base64_decode_buffer = [0; BASE64_DECODE_BUFFER_LEN];
41        let decoded_len = URL_SAFE_NO_PAD.decode_slice(input, &mut base64_decode_buffer)?;
42        let input = &base64_decode_buffer[..decoded_len];
43
44        let length = input.len();
45        if length != KEY_SIZE {
46            return Err(ParseError::InvalidLength { length });
47        }
48
49        // Length check is done earlier
50        let key = u128::from_be_bytes(input.try_into().unwrap());
51
52        Ok(Self(key))
53    }
54}
55
56impl std::fmt::Display for FolderKey {
57    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
58        let mut buffer = [0; BASE64_LEN];
59        let encoded_len = URL_SAFE_NO_PAD
60            .encode_slice(self.0.to_be_bytes(), &mut buffer)
61            .expect("output buffer should never be too small");
62        let value = std::str::from_utf8(&buffer[..encoded_len]).expect("output should be utf8");
63
64        f.write_str(value)
65    }
66}
67
68impl From<FolderKey> for String {
69    fn from(key: FolderKey) -> Self {
70        key.to_string()
71    }
72}
73
74impl TryFrom<String> for FolderKey {
75    type Error = <Self as std::str::FromStr>::Err;
76
77    fn try_from(value: String) -> Result<Self, Self::Error> {
78        value.parse()
79    }
80}
81
82#[cfg(test)]
83mod test {
84    use super::*;
85    use crate::test::*;
86
87    #[test]
88    fn round() {
89        let folder_key = FolderKey(TEST_FOLDER_KEY_DECODED);
90        let folder_key_string = folder_key.to_string();
91        let new_folder_key: FolderKey = folder_key_string.parse().expect("failed to parse");
92        assert!(folder_key.0 == new_folder_key.0);
93    }
94}