Skip to main content

hashtree_core/
encryption.rs

1//! Explicit encryption suites with legacy CHK key compatibility.
2
3use crate::crypto::{self, CryptoError, EncryptionKey};
4
5/// Writer selection. New writes use versioned CHK keys by default.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum EncryptionMode {
8    LegacyChk,
9    #[default]
10    ChkV1,
11    RndV1,
12}
13
14/// A validated legacy 32-byte key or a 33-byte suite-prefixed key.
15#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub struct ObjectKey {
17    bytes: [u8; 33],
18    length: u8,
19}
20
21impl std::fmt::Debug for ObjectKey {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        f.debug_struct("ObjectKey")
24            .field("mode", &self.mode())
25            .finish_non_exhaustive()
26    }
27}
28
29impl ObjectKey {
30    pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
31        match bytes.len() {
32            32 => {}
33            33 if matches!(bytes[0], 1 | 2) => {}
34            33 => return Err(CryptoError::UnsupportedSuite(bytes[0])),
35            _ => return Err(CryptoError::InvalidKeyLength),
36        }
37        let mut stored = [0; 33];
38        stored[..bytes.len()].copy_from_slice(bytes);
39        Ok(Self {
40            bytes: stored,
41            length: bytes.len() as u8,
42        })
43    }
44
45    pub fn from_hex(value: &str) -> Result<Self, CryptoError> {
46        let bytes = hex::decode(value).map_err(|_| CryptoError::InvalidKeyLength)?;
47        Self::from_bytes(&bytes)
48    }
49
50    pub fn as_bytes(&self) -> &[u8] {
51        &self.bytes[..self.length as usize]
52    }
53
54    pub fn mode(&self) -> EncryptionMode {
55        if self.length == 32 {
56            EncryptionMode::LegacyChk
57        } else if self.bytes[0] == 1 {
58            EncryptionMode::ChkV1
59        } else {
60            EncryptionMode::RndV1
61        }
62    }
63
64    pub fn raw_key(&self) -> EncryptionKey {
65        self.as_bytes()[self.length as usize - 32..]
66            .try_into()
67            .expect("validated key")
68    }
69
70    pub fn to_hex(&self) -> String {
71        hex::encode(self.as_bytes())
72    }
73
74    fn versioned(suite: u8, key: EncryptionKey) -> Self {
75        let mut bytes = [0; 33];
76        bytes[0] = suite;
77        bytes[1..].copy_from_slice(&key);
78        Self { bytes, length: 33 }
79    }
80}
81
82impl From<EncryptionKey> for ObjectKey {
83    fn from(key: EncryptionKey) -> Self {
84        let mut bytes = [0; 33];
85        bytes[..32].copy_from_slice(&key);
86        Self { bytes, length: 32 }
87    }
88}
89
90impl AsRef<[u8]> for ObjectKey {
91    fn as_ref(&self) -> &[u8] {
92        self.as_bytes()
93    }
94}
95
96pub fn encrypt_object(
97    plaintext: &[u8],
98    mode: EncryptionMode,
99) -> Result<(Vec<u8>, ObjectKey), CryptoError> {
100    match mode {
101        EncryptionMode::LegacyChk | EncryptionMode::ChkV1 => {
102            let (bytes, key) = crypto::encrypt_chk(plaintext)?;
103            let key = if mode == EncryptionMode::LegacyChk {
104                key.into()
105            } else {
106                ObjectKey::versioned(1, key)
107            };
108            Ok((bytes, key))
109        }
110        EncryptionMode::RndV1 => {
111            let key = crypto::generate_key();
112            Ok((
113                crypto::encrypt(plaintext, &key)?,
114                ObjectKey::versioned(2, key),
115            ))
116        }
117    }
118}
119
120pub fn decrypt_object(bytes: &[u8], key: &ObjectKey) -> Result<Vec<u8>, CryptoError> {
121    match key.mode() {
122        EncryptionMode::LegacyChk | EncryptionMode::ChkV1 => {
123            crypto::decrypt_chk(bytes, &key.raw_key())
124        }
125        EncryptionMode::RndV1 => crypto::decrypt(bytes, &key.raw_key()),
126    }
127}
128
129impl std::ops::Deref for ObjectKey {
130    type Target = [u8];
131    fn deref(&self) -> &[u8] {
132        self.as_bytes()
133    }
134}
135
136impl serde::Serialize for ObjectKey {
137    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
138        self.as_bytes().serialize(serializer)
139    }
140}
141
142impl<'de> serde::Deserialize<'de> for ObjectKey {
143    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
144        let bytes = Vec::<u8>::deserialize(deserializer)?;
145        Self::from_bytes(&bytes).map_err(serde::de::Error::custom)
146    }
147}