miden_crypto/ies/
keys.rs

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