Skip to main content

sendcipher_core/crypto/
key_wrapper.rs

1/* Created on 2025.11.06 */
2/* Copyright (c) 2025-2026 Youcef Lemsafer */
3/* SPDX-License-Identifier: MIT */
4
5use serde::{Deserialize, Serialize};
6
7use crate::crypto::{
8    Aes256GcmParams, Argon2IdKeyProducer, Argon2idParams, KeyEnvelope, KeyEnvelopeType,
9    blob_header::KdfAlgorithm, crypto, decrypt_in_place,
10};
11
12pub trait KeyWrapper: Send {
13    /// Envelope type identifier
14    fn envelope_type(&self) -> KeyEnvelopeType;
15    /// Serialization
16    fn to_bytes(&self) -> Result<Vec<u8>, crate::error::Error>;
17}
18
19pub trait KdfBasedKeyWrapper: KeyWrapper {
20    fn kdf_algorithm(&self) -> KdfAlgorithm;
21    fn update_salt(&mut self, salt: Vec<u8>) -> Result<(), crate::error::Error>;
22    fn unwrap_key(&self, password: &str) -> Result<Vec<u8>, crate::error::Error>;
23    fn impl_to_bytes(&self) -> Result<Vec<u8>, crate::error::Error>;
24    fn kdf_wrapper_to_bytes(&self) -> Result<Vec<u8>, crate::error::Error> {
25        let mut buffer = Vec::<u8>::with_capacity(32);
26        buffer.extend(self.kdf_algorithm().to_bytes());
27        buffer.extend(self.impl_to_bytes()?);
28        Ok(buffer)
29    }
30}
31
32#[derive(Clone, Serialize, Deserialize)]
33pub enum AnyKeyWrapper {
34    Argon2id(Argon2idKeyWrapper),
35    Pgp(PgpKeyWrapper),
36    Age(AgeKeyWrapper),
37}
38
39impl KeyWrapper for AnyKeyWrapper {
40    fn envelope_type(&self) -> KeyEnvelopeType {
41        match self {
42            AnyKeyWrapper::Argon2id(_) => KeyEnvelopeType::Kdf,
43            AnyKeyWrapper::Pgp(_) => KeyEnvelopeType::Pgp,
44            AnyKeyWrapper::Age(_) => KeyEnvelopeType::Age,
45        }
46    }
47
48    fn to_bytes(&self) -> Result<Vec<u8>, crate::error::Error> {
49        match self {
50            AnyKeyWrapper::Argon2id(kw) => kw.to_bytes(),
51            AnyKeyWrapper::Pgp(kw) => kw.to_bytes(),
52            AnyKeyWrapper::Age(kw) => kw.to_bytes(),
53        }
54    }
55}
56
57impl AnyKeyWrapper {
58    pub fn as_kdf_based(&self) -> Option<&dyn KdfBasedKeyWrapper> {
59        match self {
60            AnyKeyWrapper::Argon2id(kw) => Some(kw),
61            _ => None,
62        }
63    }
64    pub fn expect_kdf_based(&self) -> Result<&dyn KdfBasedKeyWrapper, crate::error::Error> {
65        let opt = self.as_kdf_based();
66        match opt {
67            Some(x) => Ok(x),
68            None => Err(crate::error::Error::LogicError(
69                "Expecting a KDF based key wrapper".to_string(),
70            )),
71        }
72    }
73}
74
75#[derive(Clone, Serialize, Deserialize)]
76pub struct Argon2idKeyWrapper {
77    version: u8,
78    parameters: Argon2idParams,
79    wrapped_key: Vec<u8>,
80    authentication_data: Vec<u8>,
81}
82
83impl KdfBasedKeyWrapper for Argon2idKeyWrapper {
84    fn kdf_algorithm(&self) -> KdfAlgorithm {
85        KdfAlgorithm::Argon2id
86    }
87
88    fn update_salt(&mut self, salt: Vec<u8>) -> Result<(), crate::error::Error> {
89        use crate::crypto::random;
90        self.parameters.salt = salt;
91        // OPSEC: garble the wrapped key and the authentication data!
92        self.wrapped_key = random::get_rand_bytes(self.wrapped_key.len())?;
93        self.authentication_data = random::get_rand_bytes(self.authentication_data.len())?;
94        Ok(())
95    }
96
97    fn unwrap_key(&self, password: &str) -> Result<Vec<u8>, crate::error::Error> {
98        let mut dek = self.wrapped_key.clone();
99        let kek = Argon2IdKeyProducer::new(password, &self.parameters)
100            .get_key()
101            .clone();
102        decrypt_in_place(
103            &mut dek,
104            &<[u8; 32]>::try_from(kek)
105                .map_err(|e| crate::error::Error::DecryptionError("".to_string()))?,
106            &Aes256GcmParams {
107                nonce: vec![0u8; 12],
108            },
109            &self.authentication_data,
110        )?;
111        Ok(dek)
112    }
113
114    fn impl_to_bytes(&self) -> Result<Vec<u8>, crate::error::Error> {
115        Ok(bincode::serialize(self)
116            .map_err(|e| crate::error::Error::SerializationError(e.to_string()))?)
117    }
118}
119
120impl KeyWrapper for Argon2idKeyWrapper {
121    fn envelope_type(&self) -> KeyEnvelopeType {
122        KeyEnvelopeType::Kdf
123    }
124
125    fn to_bytes(&self) -> Result<Vec<u8>, crate::error::Error> {
126        self.kdf_wrapper_to_bytes()
127    }
128}
129
130impl Argon2idKeyWrapper {
131    const CURRENT_VERSION: u8 = 1;
132    pub fn new(
133        password: &str,
134        parameters: &Argon2idParams,
135        dek: &Vec<u8>,
136    ) -> Result<Self, crate::error::Error> {
137        let key_prod = Argon2IdKeyProducer::new(password, &parameters);
138        Self::construct_inst(&key_prod, dek)
139    }
140    pub fn with_default_parameters(
141        password: &str,
142        dek: &Vec<u8>,
143    ) -> Result<Self, crate::error::Error> {
144        let key_prod = Argon2IdKeyProducer::with_default_parameters(password);
145        Self::construct_inst(&key_prod, dek)
146    }
147    fn construct_inst(
148        key_prod: &Argon2IdKeyProducer,
149        dek: &Vec<u8>,
150    ) -> Result<Self, crate::error::Error> {
151        let mut inst = Self {
152            version: Self::CURRENT_VERSION,
153            parameters: key_prod.get_parameters().clone(),
154            wrapped_key: dek.clone(),
155            authentication_data: vec![],
156        };
157        let aes256gcm_params = Aes256GcmParams {
158            nonce: vec![0u8; 12],
159        };
160        inst.authentication_data = crypto::encrypt_in_place(
161            &mut inst.wrapped_key,
162            &key_prod.get_key().clone().try_into().unwrap(),
163            &aes256gcm_params,
164        )?;
165        Ok(inst)
166    }
167    pub fn from_bytes(data: &[u8]) -> Result<Self, crate::error::Error> {
168        bincode::deserialize(data)
169            .map_err(|e| crate::error::Error::DeserializationError(e.to_string()))
170    }
171}
172
173#[derive(Clone, Serialize, Deserialize)]
174pub(crate) struct PgpKeyWrapper {}
175
176impl KeyWrapper for PgpKeyWrapper {
177    fn envelope_type(&self) -> KeyEnvelopeType {
178        KeyEnvelopeType::Pgp
179    }
180
181    fn to_bytes(&self) -> Result<Vec<u8>, crate::error::Error> {
182        todo!("Not implemented yet: serialization of PgpKeyWrapper")
183    }
184}
185impl PgpKeyWrapper {
186    pub fn from_bytes(data: &[u8]) -> Result<Self, crate::error::Error> {
187        todo!()
188    }
189}
190
191#[derive(Clone, Serialize, Deserialize)]
192pub(crate) struct AgeKeyWrapper {}
193
194impl KeyWrapper for AgeKeyWrapper {
195    fn envelope_type(&self) -> KeyEnvelopeType {
196        KeyEnvelopeType::Age
197    }
198
199    fn to_bytes(&self) -> Result<Vec<u8>, crate::error::Error> {
200        todo!("Not implemented yet: serialization of AgeKeyWrapper")
201    }
202}
203impl AgeKeyWrapper {
204    pub fn from_bytes(data: &[u8]) -> Result<Self, crate::error::Error> {
205        todo!()
206    }
207}
208
209pub(crate) fn from_key_envelope(
210    key_envelope: &KeyEnvelope,
211) -> Result<AnyKeyWrapper, crate::error::Error> {
212    match key_envelope.envelope_type {
213        KeyEnvelopeType::Invalid => Err(crate::error::Error::DeserializationError(
214            "Invalid key envelop type tag".to_string(),
215        )),
216        KeyEnvelopeType::Kdf => {
217            let data = key_envelope.envelope_data();
218            let (kdf_algo, pos_after_kdf_algo) = KdfAlgorithm::from_bytes(data)?;
219            match kdf_algo {
220                KdfAlgorithm::Invalid => {
221                    return Err(crate::error::Error::DeserializationError(
222                        "Invalid KDF algorithm tag".to_string(),
223                    ));
224                }
225                KdfAlgorithm::Argon2id => Ok(AnyKeyWrapper::Argon2id(
226                    Argon2idKeyWrapper::from_bytes(&data[pos_after_kdf_algo..])?,
227                )),
228            }
229        }
230        KeyEnvelopeType::Pgp => Ok(AnyKeyWrapper::Pgp(PgpKeyWrapper::from_bytes(
231            &key_envelope.envelope_data(),
232        )?)),
233        KeyEnvelopeType::Age => Ok(AnyKeyWrapper::Age(AgeKeyWrapper::from_bytes(
234            &key_envelope.envelope_data(),
235        )?)),
236    }
237}