miden_crypto/ies/
keys.rs

1use alloc::vec::Vec;
2
3use rand::{CryptoRng, RngCore};
4
5use super::{IesError, IesScheme, crypto_box::CryptoBox, message::SealedMessage};
6use crate::{
7    Felt,
8    aead::{aead_rpo::AeadRpo, xchacha::XChaCha},
9    ecdh::{KeyAgreementScheme, k256::K256, x25519::X25519},
10    utils::{Deserializable, Serializable},
11};
12
13// TYPE ALIASES
14// ================================================================================================
15
16/// Instantiation of sealed box using K256 + XChaCha20Poly1305
17type K256XChaCha20Poly1305 = CryptoBox<K256, XChaCha>;
18/// Instantiation of sealed box using X25519 + XChaCha20Poly1305
19type X25519XChaCha20Poly1305 = CryptoBox<X25519, XChaCha>;
20/// Instantiation of sealed box using K256 + AeadRPO
21type K256AeadRpo = CryptoBox<K256, AeadRpo>;
22/// Instantiation of sealed box using X25519 + AeadRPO
23type X25519AeadRpo = CryptoBox<X25519, AeadRpo>;
24
25// HELPER MACROS
26// ================================================================================================
27
28/// Generates seal_bytes_with_associated_data method implementation
29macro_rules! impl_seal_bytes_with_associated_data {
30    ($($variant:path => $crypto_box:ty, $ephemeral_variant:path;)*) => {
31        /// Seals the provided plaintext (represented as bytes) and associated data with this
32        /// sealing key.
33        ///
34        /// The returned message can be unsealed with the [UnsealingKey] associated with this
35        /// sealing key.
36        pub fn seal_bytes_with_associated_data<R: CryptoRng + RngCore>(
37            &self,
38            rng: &mut R,
39            plaintext: &[u8],
40            associated_data: &[u8],
41        ) -> Result<SealedMessage, IesError> {
42            match self {
43                $(
44                    $variant(key) => {
45                        let (ciphertext, ephemeral) = <$crypto_box>::seal_bytes_with_associated_data(
46                            rng,
47                            key,
48                            plaintext,
49                            associated_data,
50                        )?;
51
52                        Ok(SealedMessage {
53                            ephemeral_key: $ephemeral_variant(ephemeral),
54                            ciphertext,
55                        })
56                    }
57                )*
58            }
59        }
60    };
61}
62
63/// Generates seal_elements_with_associated_data method implementation
64macro_rules! impl_seal_elements_with_associated_data {
65    ($($variant:path => $crypto_box:ty, $ephemeral_variant:path;)*) => {
66        /// Seals the provided plaintext (represented as filed elements) and associated data with
67        /// this sealing key.
68        ///
69        /// The returned message can be unsealed with the [UnsealingKey] associated with this
70        /// sealing key.
71        pub fn seal_elements_with_associated_data<R: CryptoRng + RngCore>(
72            &self,
73            rng: &mut R,
74            plaintext: &[Felt],
75            associated_data: &[Felt],
76        ) -> Result<SealedMessage, IesError> {
77            match self {
78                $(
79                    $variant(key) => {
80                        let (ciphertext, ephemeral) = <$crypto_box>::seal_elements_with_associated_data(
81                            rng,
82                            key,
83                            plaintext,
84                            associated_data,
85                        )?;
86
87                        Ok(SealedMessage {
88                            ephemeral_key: $ephemeral_variant(ephemeral),
89                            ciphertext,
90                        })
91                    }
92                )*
93            }
94        }
95    };
96}
97
98/// Generates unseal_bytes_with_associated_data method implementation
99macro_rules! impl_unseal_bytes_with_associated_data {
100    ($($variant:path => $crypto_box:ty, $ephemeral_variant:path;)*) => {
101        /// Unseals the provided message using this unsealing key and returns the plaintext as bytes.
102        ///
103        /// # Errors
104        /// Returns an error if:
105        /// - The message was not sealed as bytes (i.e., if it was sealed using `seal_elements()`
106        ///   or `seal_elements_with_associated_data()`)
107        /// - The scheme used to seal the message does not match this unsealing key's scheme
108        /// - Decryption or authentication fails
109        pub fn unseal_bytes_with_associated_data(
110            &self,
111            sealed_message: SealedMessage,
112            associated_data: &[u8],
113        ) -> Result<Vec<u8>, IesError> {
114            // Check scheme compatibility using constant-time comparison
115            let self_algo = self.scheme() as u8;
116            let msg_algo = sealed_message.ephemeral_key.scheme() as u8;
117
118            let compatible = self_algo == msg_algo;
119            if !compatible {
120                return Err(IesError::SchemeMismatch);
121            }
122
123            let SealedMessage { ephemeral_key, ciphertext } = sealed_message;
124
125            match (self, ephemeral_key) {
126                $(
127                    ($variant(key), $ephemeral_variant(ephemeral)) => {
128                        <$crypto_box>::unseal_bytes_with_associated_data(key, &ephemeral, &ciphertext, associated_data)
129                    }
130                )*
131                _ => Err(IesError::SchemeMismatch),
132            }
133        }
134    };
135}
136
137/// Generates unseal_elements_with_associated_data method implementation
138macro_rules! impl_unseal_elements_with_associated_data {
139    ($($variant:path => $crypto_box:ty, $ephemeral_variant:path;)*) => {
140        /// Unseals the provided message using this unsealing key and returns the plaintext as field elements.
141        ///
142        /// # Errors
143        /// Returns an error if:
144        /// - The message was not sealed as elements (i.e., if it was sealed using `seal_bytes()`
145        ///   or `seal_bytes_with_associated_data()`)
146        /// - The scheme used to seal the message does not match this unsealing key's scheme
147        /// - Decryption or authentication fails
148        pub fn unseal_elements_with_associated_data(
149            &self,
150            sealed_message: SealedMessage,
151            associated_data: &[Felt],
152        ) -> Result<Vec<Felt>, IesError> {
153            // Check scheme compatibility
154            let self_algo = self.scheme() as u8;
155            let msg_algo = sealed_message.ephemeral_key.scheme() as u8;
156
157            let compatible = self_algo == msg_algo;
158            if !compatible {
159                return Err(IesError::SchemeMismatch);
160            }
161
162            let SealedMessage { ephemeral_key, ciphertext } = sealed_message;
163
164            match (self, ephemeral_key) {
165                $(
166                    ($variant(key), $ephemeral_variant(ephemeral)) => {
167                        <$crypto_box>::unseal_elements_with_associated_data(key, &ephemeral, &ciphertext, associated_data)
168                    }
169                )*
170                _ => Err(IesError::SchemeMismatch),
171            }
172        }
173    };
174}
175
176// SEALING KEY
177// ================================================================================================
178
179/// Public key for sealing messages to a recipient.
180#[derive(Debug, Clone, PartialEq, Eq)]
181pub enum SealingKey {
182    K256XChaCha20Poly1305(crate::dsa::ecdsa_k256_keccak::PublicKey),
183    X25519XChaCha20Poly1305(crate::dsa::eddsa_25519::PublicKey),
184    K256AeadRpo(crate::dsa::ecdsa_k256_keccak::PublicKey),
185    X25519AeadRpo(crate::dsa::eddsa_25519::PublicKey),
186}
187
188impl SealingKey {
189    /// Seals the provided plaintext (represented as bytes) with this sealing key.
190    ///
191    /// The returned message can be unsealed with the [UnsealingKey] associated with this sealing
192    /// key.
193    pub fn seal_bytes<R: CryptoRng + RngCore>(
194        &self,
195        rng: &mut R,
196        plaintext: &[u8],
197    ) -> Result<SealedMessage, IesError> {
198        self.seal_bytes_with_associated_data(rng, plaintext, &[])
199    }
200
201    impl_seal_bytes_with_associated_data! {
202        SealingKey::K256XChaCha20Poly1305 => K256XChaCha20Poly1305, EphemeralPublicKey::K256XChaCha20Poly1305;
203        SealingKey::X25519XChaCha20Poly1305 => X25519XChaCha20Poly1305, EphemeralPublicKey::X25519XChaCha20Poly1305;
204        SealingKey::K256AeadRpo => K256AeadRpo, EphemeralPublicKey::K256AeadRpo;
205        SealingKey::X25519AeadRpo => X25519AeadRpo, EphemeralPublicKey::X25519AeadRpo;
206    }
207
208    /// Seals the provided plaintext (represented as filed elements) with this sealing key.
209    ///
210    /// The returned message can be unsealed with the [UnsealingKey] associated with this sealing
211    /// key.
212    pub fn seal_elements<R: CryptoRng + RngCore>(
213        &self,
214        rng: &mut R,
215        plaintext: &[Felt],
216    ) -> Result<SealedMessage, IesError> {
217        self.seal_elements_with_associated_data(rng, plaintext, &[])
218    }
219
220    impl_seal_elements_with_associated_data! {
221        SealingKey::K256XChaCha20Poly1305 => K256XChaCha20Poly1305, EphemeralPublicKey::K256XChaCha20Poly1305;
222        SealingKey::X25519XChaCha20Poly1305 => X25519XChaCha20Poly1305, EphemeralPublicKey::X25519XChaCha20Poly1305;
223        SealingKey::K256AeadRpo => K256AeadRpo, EphemeralPublicKey::K256AeadRpo;
224        SealingKey::X25519AeadRpo => X25519AeadRpo, EphemeralPublicKey::X25519AeadRpo;
225    }
226}
227
228// UNSEALING KEY
229// ================================================================================================
230
231/// Secret key for unsealing messages.
232pub enum UnsealingKey {
233    K256XChaCha20Poly1305(crate::dsa::ecdsa_k256_keccak::SecretKey),
234    X25519XChaCha20Poly1305(crate::dsa::eddsa_25519::SecretKey),
235    K256AeadRpo(crate::dsa::ecdsa_k256_keccak::SecretKey),
236    X25519AeadRpo(crate::dsa::eddsa_25519::SecretKey),
237}
238
239impl UnsealingKey {
240    /// Returns scheme identifier for this unsealing key.
241    fn scheme(&self) -> IesScheme {
242        match self {
243            UnsealingKey::K256XChaCha20Poly1305(_) => IesScheme::K256XChaCha20Poly1305,
244            UnsealingKey::X25519XChaCha20Poly1305(_) => IesScheme::X25519XChaCha20Poly1305,
245            UnsealingKey::K256AeadRpo(_) => IesScheme::K256AeadRpo,
246            UnsealingKey::X25519AeadRpo(_) => IesScheme::X25519AeadRpo,
247        }
248    }
249
250    /// Returns scheme name for this unsealing key.
251    pub fn scheme_name(&self) -> &'static str {
252        self.scheme().name()
253    }
254
255    /// Unseals the provided message using this unsealing key.
256    ///
257    /// The message must have been sealed as bytes (i.e., using `seal_bytes()` or
258    /// `seal_bytes_with_associated_data()` method), otherwise an error will be returned.
259    pub fn unseal_bytes(&self, sealed_message: SealedMessage) -> Result<Vec<u8>, IesError> {
260        self.unseal_bytes_with_associated_data(sealed_message, &[])
261    }
262
263    impl_unseal_bytes_with_associated_data! {
264        UnsealingKey::K256XChaCha20Poly1305 => K256XChaCha20Poly1305, EphemeralPublicKey::K256XChaCha20Poly1305;
265        UnsealingKey::X25519XChaCha20Poly1305 => X25519XChaCha20Poly1305, EphemeralPublicKey::X25519XChaCha20Poly1305;
266        UnsealingKey::K256AeadRpo => K256AeadRpo, EphemeralPublicKey::K256AeadRpo;
267        UnsealingKey::X25519AeadRpo => X25519AeadRpo, EphemeralPublicKey::X25519AeadRpo;
268    }
269
270    /// Unseals the provided message using this unsealing key.
271    ///
272    /// The message must have been sealed as elements (i.e., using `seal_elements()` or
273    /// `seal_elements_with_associated_data()` method), otherwise an error will be returned.
274    pub fn unseal_elements(&self, sealed_message: SealedMessage) -> Result<Vec<Felt>, IesError> {
275        self.unseal_elements_with_associated_data(sealed_message, &[])
276    }
277
278    impl_unseal_elements_with_associated_data! {
279        UnsealingKey::K256XChaCha20Poly1305 => K256XChaCha20Poly1305, EphemeralPublicKey::K256XChaCha20Poly1305;
280        UnsealingKey::X25519XChaCha20Poly1305 => X25519XChaCha20Poly1305, EphemeralPublicKey::X25519XChaCha20Poly1305;
281        UnsealingKey::K256AeadRpo => K256AeadRpo, EphemeralPublicKey::K256AeadRpo;
282        UnsealingKey::X25519AeadRpo => X25519AeadRpo, EphemeralPublicKey::X25519AeadRpo;
283    }
284}
285
286// EPHEMERAL PUBLIC KEY
287// ================================================================================================
288
289/// Ephemeral public key, part of sealed messages
290#[derive(Debug, Clone, PartialEq, Eq)]
291pub(super) enum EphemeralPublicKey {
292    K256XChaCha20Poly1305(crate::ecdh::k256::EphemeralPublicKey),
293    X25519XChaCha20Poly1305(crate::ecdh::x25519::EphemeralPublicKey),
294    K256AeadRpo(crate::ecdh::k256::EphemeralPublicKey),
295    X25519AeadRpo(crate::ecdh::x25519::EphemeralPublicKey),
296}
297
298impl EphemeralPublicKey {
299    /// Get scheme identifier for this ephemeral key
300    pub fn scheme(&self) -> IesScheme {
301        match self {
302            EphemeralPublicKey::K256XChaCha20Poly1305(_) => IesScheme::K256XChaCha20Poly1305,
303            EphemeralPublicKey::X25519XChaCha20Poly1305(_) => IesScheme::X25519XChaCha20Poly1305,
304            EphemeralPublicKey::K256AeadRpo(_) => IesScheme::K256AeadRpo,
305            EphemeralPublicKey::X25519AeadRpo(_) => IesScheme::X25519AeadRpo,
306        }
307    }
308
309    /// Serialize to bytes
310    pub fn to_bytes(&self) -> Vec<u8> {
311        match self {
312            EphemeralPublicKey::K256XChaCha20Poly1305(key) => key.to_bytes(),
313            EphemeralPublicKey::X25519XChaCha20Poly1305(key) => key.to_bytes(),
314            EphemeralPublicKey::K256AeadRpo(key) => key.to_bytes(),
315            EphemeralPublicKey::X25519AeadRpo(key) => key.to_bytes(),
316        }
317    }
318
319    /// Deserialize from bytes with explicit scheme
320    pub fn from_bytes(scheme: IesScheme, bytes: &[u8]) -> Result<Self, IesError> {
321        match scheme {
322            IesScheme::K256XChaCha20Poly1305 | IesScheme::K256AeadRpo => {
323                let key = <K256 as KeyAgreementScheme>::EphemeralPublicKey::read_from_bytes(bytes)
324                    .map_err(|_| IesError::EphemeralPublicKeyDeserializationFailed)?;
325                Ok(EphemeralPublicKey::K256XChaCha20Poly1305(key))
326            },
327            IesScheme::X25519XChaCha20Poly1305 | IesScheme::X25519AeadRpo => {
328                let key =
329                    <X25519 as KeyAgreementScheme>::EphemeralPublicKey::read_from_bytes(bytes)
330                        .map_err(|_| IesError::EphemeralPublicKeyDeserializationFailed)?;
331                Ok(EphemeralPublicKey::X25519XChaCha20Poly1305(key))
332            },
333        }
334    }
335}