Skip to main content

miden_protocol/address/
routing_parameters.rs

1use alloc::borrow::ToOwned;
2use alloc::string::{String, ToString};
3use alloc::vec::Vec;
4
5use bech32::primitives::decode::CheckedHrpstring;
6use bech32::{Bech32m, Hrp};
7
8use crate::address::AddressInterface;
9use crate::crypto::dsa::{ecdsa_k256_keccak, eddsa_25519_sha512};
10use crate::crypto::ies::SealingKey;
11use crate::errors::{AddressError, Bech32Error};
12use crate::note::NoteTag;
13use crate::utils::serde::{
14    ByteReader,
15    ByteWriter,
16    Deserializable,
17    DeserializationError,
18    Serializable,
19};
20use crate::utils::sync::LazyLock;
21
22/// The HRP used for encoding routing parameters.
23///
24/// This HRP is only used internally, but needs to be well-defined for other routing parameter
25/// encode/decode implementations.
26///
27/// `mrp` stands for Miden Routing Parameters.
28static ROUTING_PARAMETERS_HRP: LazyLock<Hrp> =
29    LazyLock::new(|| Hrp::parse("mrp").expect("hrp should be valid"));
30
31/// The separator character used in bech32.
32const BECH32_SEPARATOR: &str = "1";
33
34/// The value to encode the absence of a note tag routing parameter (i.e. `None`).
35///
36/// The note tag length occupies 6 bits (values 0..=63). Valid tag lengths are 0..=32,
37/// so we reserve the maximum 6-bit value (63) to represent `None`.
38///
39/// If the note tag length is absent from routing parameters, the note tag length for the address
40/// will be set to the default default tag length of the address' ID component.
41const ABSENT_NOTE_TAG_LEN: u8 = 63;
42
43/// The routing parameter key for the receiver profile.
44const RECEIVER_PROFILE_PARAM_KEY: u8 = 0;
45
46/// The routing parameter key for the encryption key.
47const ENCRYPTION_KEY_PARAM_KEY: u8 = 1;
48
49/// The expected length of Ed25519/X25519 public keys in bytes.
50const X25519_PUBLIC_KEY_LENGTH: usize = 32;
51
52/// The expected length of K256 (secp256k1) public keys in bytes (compressed format).
53const K256_PUBLIC_KEY_LENGTH: usize = 33;
54
55/// Discriminants for encryption key variants.
56const ENCRYPTION_KEY_X25519_XCHACHA20POLY1305: u8 = 0;
57const ENCRYPTION_KEY_K256_XCHACHA20POLY1305: u8 = 1;
58const ENCRYPTION_KEY_X25519_AEAD_POSEIDON2: u8 = 2;
59const ENCRYPTION_KEY_K256_AEAD_POSEIDON2: u8 = 3;
60const ENCRYPTION_KEY_X25519_AEAD_EIDOS: u8 = 4;
61const ENCRYPTION_KEY_K256_AEAD_EIDOS: u8 = 5;
62
63/// Parameters that define how a sender should route a note to the [`AddressId`](super::AddressId)
64/// in an [`Address`](super::Address).
65#[derive(Debug, Clone, PartialEq, Eq)]
66pub struct RoutingParameters {
67    interface: AddressInterface,
68    note_tag_len: Option<u8>,
69    encryption_key: Option<SealingKey>,
70}
71
72impl RoutingParameters {
73    // CONSTRUCTORS
74    // --------------------------------------------------------------------------------------------
75
76    /// Creates new [`RoutingParameters`] from an [`AddressInterface`] and all other parameters
77    /// initialized to `None`.
78    pub fn new(interface: AddressInterface) -> Self {
79        Self {
80            interface,
81            note_tag_len: None,
82            encryption_key: None,
83        }
84    }
85
86    /// Sets the note tag length routing parameter.
87    ///
88    /// The tag length determines how many bits of the address ID are encoded into [`NoteTag`]s of
89    /// notes targeted to this address. This lets the receiver choose their level of privacy. A
90    /// higher tag length makes the address ID more uniquely identifiable and reduces privacy,
91    /// while a shorter length increases privacy at the cost of matching more notes published
92    /// onchain.
93    ///
94    /// # Errors
95    ///
96    /// Returns an error if:
97    /// - The tag length exceeds the maximum of [`NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH `].
98    pub fn with_note_tag_len(mut self, note_tag_len: u8) -> Result<Self, AddressError> {
99        if note_tag_len > NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH {
100            return Err(AddressError::TagLengthTooLarge(note_tag_len));
101        }
102
103        self.note_tag_len = Some(note_tag_len);
104        Ok(self)
105    }
106
107    // ACCESSORS
108    // --------------------------------------------------------------------------------------------
109
110    /// Returns the note tag length preference.
111    ///
112    /// This is guaranteed to be in range `0..=32` (i.e. at most
113    /// [`NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH `]).
114    pub fn note_tag_len(&self) -> Option<u8> {
115        self.note_tag_len
116    }
117
118    /// Returns the [`AddressInterface`] of the account to which the address points.
119    pub fn interface(&self) -> AddressInterface {
120        self.interface
121    }
122
123    /// Returns the public encryption key.
124    pub fn encryption_key(&self) -> Option<&SealingKey> {
125        self.encryption_key.as_ref()
126    }
127
128    /// Sets the encryption key routing parameter.
129    ///
130    /// This allows senders to encrypt note payloads using sealed box encryption
131    /// for the recipient of this address.
132    pub fn with_encryption_key(mut self, key: SealingKey) -> Self {
133        self.encryption_key = Some(key);
134        self
135    }
136
137    // HELPERS
138    // --------------------------------------------------------------------------------------------
139
140    /// Encodes [`RoutingParameters`] to a byte vector.
141    pub(crate) fn encode_to_bytes(&self) -> Vec<u8> {
142        let mut encoded = Vec::new();
143
144        // Append the receiver profile key and the encoded value to the vector.
145        encoded.push(RECEIVER_PROFILE_PARAM_KEY);
146        encoded.extend(encode_receiver_profile(self.interface, self.note_tag_len));
147
148        // Append the encryption key if present.
149        if let Some(encryption_key) = &self.encryption_key {
150            encoded.push(ENCRYPTION_KEY_PARAM_KEY);
151            encode_encryption_key(encryption_key, &mut encoded);
152        }
153
154        encoded
155    }
156
157    /// Encodes [`RoutingParameters`] to a bech32 string _without_ the leading hrp and separator.
158    pub(crate) fn encode_to_string(&self) -> String {
159        let encoded = self.encode_to_bytes();
160
161        let bech32_str =
162            bech32::encode::<Bech32m>(*ROUTING_PARAMETERS_HRP, &encoded).expect("TODO");
163        let encoded_str = bech32_str
164            .strip_prefix(ROUTING_PARAMETERS_HRP.as_str())
165            .expect("bech32 str should start with the hrp");
166        let encoded_str = encoded_str
167            .strip_prefix(BECH32_SEPARATOR)
168            .expect("encoded str should start with bech32 separator `1`");
169        encoded_str.to_owned()
170    }
171
172    /// Decodes [`RoutingParameters`] from a bech32 string _without_ the leading hrp and separator.
173    pub(crate) fn decode(mut bech32_string: String) -> Result<Self, AddressError> {
174        // ------ Decode bech32 string into bytes ------
175
176        // Reinsert the expected HRP into the string that is stripped during encoding.
177        bech32_string.insert_str(0, BECH32_SEPARATOR);
178        bech32_string.insert_str(0, ROUTING_PARAMETERS_HRP.as_str());
179
180        // We use CheckedHrpString with an explicit checksum algorithm so we don't allow the
181        // `Bech32` or `NoChecksum` algorithms.
182        let checked_string =
183            CheckedHrpstring::new::<Bech32m>(&bech32_string).map_err(|source| {
184                // The CheckedHrpStringError does not implement core::error::Error, only
185                // std::error::Error, so for now we convert it to a String. Even if it will
186                // implement the trait in the future, we should include it as an opaque
187                // error since the crate does not have a stable release yet.
188                AddressError::decode_error_with_source(
189                    "failed to decode routing parameters bech32 string",
190                    Bech32Error::DecodeError(source.to_string().into()),
191                )
192            })?;
193
194        Self::decode_from_bytes(checked_string.byte_iter())
195    }
196
197    /// Decodes [`RoutingParameters`] from a byte iterator.
198    pub(crate) fn decode_from_bytes(
199        mut byte_iter: impl ExactSizeIterator<Item = u8>,
200    ) -> Result<Self, AddressError> {
201        let mut interface = None;
202        let mut note_tag_len = None;
203        let mut encryption_key = None;
204
205        while let Some(key) = byte_iter.next() {
206            match key {
207                RECEIVER_PROFILE_PARAM_KEY => {
208                    if interface.is_some() {
209                        return Err(AddressError::decode_error(
210                            "duplicate receiver profile routing parameter",
211                        ));
212                    }
213                    let receiver_profile = decode_receiver_profile(&mut byte_iter)?;
214                    interface = Some(receiver_profile.0);
215                    note_tag_len = receiver_profile.1;
216                },
217                ENCRYPTION_KEY_PARAM_KEY => {
218                    if encryption_key.is_some() {
219                        return Err(AddressError::decode_error(
220                            "duplicate encryption key routing parameter",
221                        ));
222                    }
223                    encryption_key = Some(decode_encryption_key(&mut byte_iter)?);
224                },
225                other => {
226                    return Err(AddressError::UnknownRoutingParameterKey(other));
227                },
228            }
229        }
230
231        let interface = interface.ok_or_else(|| {
232            AddressError::decode_error("interface must be present in routing parameters")
233        })?;
234
235        let mut routing_parameters = RoutingParameters::new(interface);
236        routing_parameters.note_tag_len = note_tag_len;
237        routing_parameters.encryption_key = encryption_key;
238
239        Ok(routing_parameters)
240    }
241}
242
243impl Serializable for RoutingParameters {
244    fn write_into<W: ByteWriter>(&self, target: &mut W) {
245        let bytes = self.encode_to_bytes();
246        // Due to the bech32 constraint of max 633 bytes, a u16 is sufficient.
247        let num_bytes = bytes.len() as u16;
248
249        target.write_u16(num_bytes);
250        target.write_many(bytes);
251    }
252}
253
254impl Deserializable for RoutingParameters {
255    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
256        let num_bytes = source.read_u16()?;
257        let bytes: Vec<u8> =
258            source.read_many_iter(num_bytes as usize)?.collect::<Result<_, _>>()?;
259
260        Self::decode_from_bytes(bytes.into_iter())
261            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
262    }
263}
264
265// ENCODING / DECODING HELPERS
266// ================================================================================================
267
268/// Returns receiver profile bytes constructed from the provided interface and note tag length.
269fn encode_receiver_profile(interface: AddressInterface, note_tag_len: Option<u8>) -> [u8; 2] {
270    let note_tag_len = note_tag_len.unwrap_or(ABSENT_NOTE_TAG_LEN);
271
272    let interface = interface as u16;
273    debug_assert_eq!(interface >> 10, 0, "address interface should fit into 10 bits");
274
275    // The interface takes up 10 bits and the tag length 6 bits, so we can merge them
276    // together.
277    let tag_len = (note_tag_len as u16) << 10;
278    let receiver_profile: u16 = tag_len | interface;
279    receiver_profile.to_be_bytes()
280}
281
282/// Reads the receiver profile from the provided bytes.
283fn decode_receiver_profile(
284    byte_iter: &mut impl ExactSizeIterator<Item = u8>,
285) -> Result<(AddressInterface, Option<u8>), AddressError> {
286    if byte_iter.len() < 2 {
287        return Err(AddressError::decode_error("expected two bytes to decode receiver profile"));
288    };
289
290    let byte0 = byte_iter.next().expect("byte0 should exist");
291    let byte1 = byte_iter.next().expect("byte1 should exist");
292    let receiver_profile = u16::from_be_bytes([byte0, byte1]);
293
294    let tag_len = (receiver_profile >> 10) as u8;
295    let note_tag_len = match tag_len {
296        ABSENT_NOTE_TAG_LEN => None,
297        0..=32 => Some(tag_len),
298        _ => {
299            return Err(AddressError::decode_error(format!("invalid note tag length {}", tag_len)));
300        },
301    };
302
303    let addr_interface = receiver_profile & 0b0000_0011_1111_1111;
304    let addr_interface = AddressInterface::try_from(addr_interface).map_err(|err| {
305        AddressError::decode_error_with_source("failed to decode address interface", err)
306    })?;
307
308    Ok((addr_interface, note_tag_len))
309}
310
311/// Append encryption key variant discriminant and key to the provided vector of bytes.
312fn encode_encryption_key(key: &SealingKey, encoded: &mut Vec<u8>) {
313    match key {
314        SealingKey::X25519XChaCha20Poly1305(pk) => {
315            encoded.push(ENCRYPTION_KEY_X25519_XCHACHA20POLY1305);
316            encoded.extend(&pk.to_bytes());
317        },
318        SealingKey::K256XChaCha20Poly1305(pk) => {
319            encoded.push(ENCRYPTION_KEY_K256_XCHACHA20POLY1305);
320            encoded.extend(&pk.to_bytes());
321        },
322        SealingKey::X25519AeadPoseidon2(pk) => {
323            encoded.push(ENCRYPTION_KEY_X25519_AEAD_POSEIDON2);
324            encoded.extend(&pk.to_bytes());
325        },
326        SealingKey::K256AeadPoseidon2(pk) => {
327            encoded.push(ENCRYPTION_KEY_K256_AEAD_POSEIDON2);
328            encoded.extend(&pk.to_bytes());
329        },
330        SealingKey::X25519AeadEidos(pk) => {
331            encoded.push(ENCRYPTION_KEY_X25519_AEAD_EIDOS);
332            encoded.extend(&pk.to_bytes());
333        },
334        SealingKey::K256AeadEidos(pk) => {
335            encoded.push(ENCRYPTION_KEY_K256_AEAD_EIDOS);
336            encoded.extend(&pk.to_bytes());
337        },
338    }
339}
340
341/// Reads the encryption key from the provided bytes.
342fn decode_encryption_key(
343    byte_iter: &mut impl ExactSizeIterator<Item = u8>,
344) -> Result<SealingKey, AddressError> {
345    // Read variant discriminant
346    let Some(variant) = byte_iter.next() else {
347        return Err(AddressError::decode_error(
348            "expected at least 1 byte for encryption key variant",
349        ));
350    };
351
352    // Reconstruct the appropriate PublicEncryptionKey variant
353    let public_encryption_key = match variant {
354        ENCRYPTION_KEY_X25519_XCHACHA20POLY1305 => {
355            SealingKey::X25519XChaCha20Poly1305(read_x25519_pub_key(byte_iter)?)
356        },
357        ENCRYPTION_KEY_K256_XCHACHA20POLY1305 => {
358            SealingKey::K256XChaCha20Poly1305(read_k256_pub_key(byte_iter)?)
359        },
360        ENCRYPTION_KEY_X25519_AEAD_POSEIDON2 => {
361            SealingKey::X25519AeadPoseidon2(read_x25519_pub_key(byte_iter)?)
362        },
363        ENCRYPTION_KEY_K256_AEAD_POSEIDON2 => {
364            SealingKey::K256AeadPoseidon2(read_k256_pub_key(byte_iter)?)
365        },
366        ENCRYPTION_KEY_X25519_AEAD_EIDOS => {
367            SealingKey::X25519AeadEidos(read_x25519_pub_key(byte_iter)?)
368        },
369        ENCRYPTION_KEY_K256_AEAD_EIDOS => SealingKey::K256AeadEidos(read_k256_pub_key(byte_iter)?),
370        other => {
371            return Err(AddressError::decode_error(format!(
372                "unknown encryption key variant: {}",
373                other
374            )));
375        },
376    };
377
378    Ok(public_encryption_key)
379}
380
381fn read_x25519_pub_key(
382    byte_iter: &mut impl ExactSizeIterator<Item = u8>,
383) -> Result<eddsa_25519_sha512::PublicKey, AddressError> {
384    if byte_iter.len() < X25519_PUBLIC_KEY_LENGTH {
385        return Err(AddressError::decode_error(format!(
386            "expected {} bytes to decode X25519 public key",
387            X25519_PUBLIC_KEY_LENGTH
388        )));
389    }
390    let key_bytes: [u8; X25519_PUBLIC_KEY_LENGTH] = read_byte_array(byte_iter);
391    eddsa_25519_sha512::PublicKey::read_from_bytes(&key_bytes).map_err(|err| {
392        AddressError::decode_error_with_source("failed to decode X25519 public key", err)
393    })
394}
395
396fn read_k256_pub_key(
397    byte_iter: &mut impl ExactSizeIterator<Item = u8>,
398) -> Result<ecdsa_k256_keccak::PublicKey, AddressError> {
399    if byte_iter.len() < K256_PUBLIC_KEY_LENGTH {
400        return Err(AddressError::decode_error(format!(
401            "expected {} bytes to decode K256 public key",
402            K256_PUBLIC_KEY_LENGTH
403        )));
404    }
405    let key_bytes: [u8; K256_PUBLIC_KEY_LENGTH] = read_byte_array(byte_iter);
406    ecdsa_k256_keccak::PublicKey::read_from_bytes(&key_bytes).map_err(|err| {
407        AddressError::decode_error_with_source("failed to decode K256 public key", err)
408    })
409}
410
411/// Reads bytes from the provided iterator into an array of length N and returns this array.
412///
413/// Assumes that there are at least N bytes in the iterator.
414fn read_byte_array<const N: usize>(byte_iter: &mut impl ExactSizeIterator<Item = u8>) -> [u8; N] {
415    let mut array = [0u8; N];
416    for byte in array.iter_mut() {
417        *byte = byte_iter.next().expect("iterator should have enough bytes");
418    }
419    array
420}
421
422// TESTS
423// ================================================================================================
424
425#[cfg(test)]
426mod tests {
427    use bech32::{Bech32m, Checksum, Hrp};
428
429    use super::*;
430
431    /// Checks the assumptions about the total length allowed in bech32 encoding.
432    ///
433    /// The assumption is that encoding should error if the total length of the hrp + data (encoded
434    /// in GF(32)) + the separator + the checksum exceeds Bech32m::CODE_LENGTH.
435    #[test]
436    fn bech32_code_length_assertions() -> anyhow::Result<()> {
437        let hrp = Hrp::parse("mrp").unwrap();
438        let separator_len = BECH32_SEPARATOR.len();
439        // The fixed number of characters included in a bech32 string.
440        let fixed_num_bytes = hrp.as_str().len() + separator_len + Bech32m::CHECKSUM_LENGTH;
441        let num_allowed_chars = Bech32m::CODE_LENGTH - fixed_num_bytes;
442        // Multiply by the 5 bits per base32 character and divide by 8 bits per byte.
443        let num_allowed_bytes = num_allowed_chars * 5 / 8;
444
445        // The number of bytes that routing parameters effectively have available.
446        assert_eq!(num_allowed_bytes, 633);
447
448        // This amount of data is the max that should be okay to encode.
449        let data_ok = vec![5; num_allowed_bytes];
450        // One more byte than the max allowed amount should result in an error.
451        let data_too_long = vec![5; num_allowed_bytes + 1];
452
453        assert!(bech32::encode::<Bech32m>(hrp, &data_ok).is_ok());
454        assert!(bech32::encode::<Bech32m>(hrp, &data_too_long).is_err());
455
456        Ok(())
457    }
458
459    /// Tests bech32 encoding and decoding roundtrip with various tag lengths.
460    #[test]
461    fn routing_parameters_bech32_encode_decode_roundtrip() -> anyhow::Result<()> {
462        // Test case 1: No explicit tag length
463        let params_no_tag = RoutingParameters::new(AddressInterface::BasicWallet);
464        let encoded = params_no_tag.encode_to_string();
465        let decoded = RoutingParameters::decode(encoded)?;
466        assert_eq!(params_no_tag, decoded);
467        assert_eq!(decoded.note_tag_len(), None);
468
469        // Test case 2: Explicit tag length 0
470        let params_tag_0 =
471            RoutingParameters::new(AddressInterface::BasicWallet).with_note_tag_len(0)?;
472        let encoded = params_tag_0.encode_to_string();
473        let decoded = RoutingParameters::decode(encoded)?;
474        assert_eq!(params_tag_0, decoded);
475        assert_eq!(decoded.note_tag_len(), Some(0));
476
477        // Test case 3: Explicit tag length 6
478        let params_tag_6 =
479            RoutingParameters::new(AddressInterface::BasicWallet).with_note_tag_len(6)?;
480        let encoded = params_tag_6.encode_to_string();
481        let decoded = RoutingParameters::decode(encoded)?;
482        assert_eq!(params_tag_6, decoded);
483        assert_eq!(decoded.note_tag_len(), Some(6));
484
485        // Test case 4: Explicit tag length set to max
486        let params_tag_max = RoutingParameters::new(AddressInterface::BasicWallet)
487            .with_note_tag_len(NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH)?;
488        let encoded = params_tag_max.encode_to_string();
489        let decoded = RoutingParameters::decode(encoded)?;
490        assert_eq!(params_tag_max, decoded);
491        assert_eq!(decoded.note_tag_len(), Some(NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH));
492
493        Ok(())
494    }
495
496    /// Tests serialization and deserialization roundtrip with various tag lengths.
497    #[test]
498    fn routing_parameters_serialization() -> anyhow::Result<()> {
499        // Test case 1: No explicit tag length
500        let params_no_tag = RoutingParameters::new(AddressInterface::BasicWallet);
501        let serialized = params_no_tag.to_bytes();
502        let deserialized = RoutingParameters::read_from_bytes(&serialized)?;
503        assert_eq!(params_no_tag, deserialized);
504        assert_eq!(deserialized.note_tag_len(), None);
505
506        // Test case 2: Explicit tag length 0
507        let params_tag_0 =
508            RoutingParameters::new(AddressInterface::BasicWallet).with_note_tag_len(0)?;
509        let serialized = params_tag_0.to_bytes();
510        let deserialized = RoutingParameters::read_from_bytes(&serialized)?;
511        assert_eq!(params_tag_0, deserialized);
512        assert_eq!(deserialized.note_tag_len(), Some(0));
513
514        // Test case 3: Explicit tag length 6
515        let params_tag_6 =
516            RoutingParameters::new(AddressInterface::BasicWallet).with_note_tag_len(6)?;
517        let serialized = params_tag_6.to_bytes();
518        let deserialized = RoutingParameters::read_from_bytes(&serialized)?;
519        assert_eq!(params_tag_6, deserialized);
520        assert_eq!(deserialized.note_tag_len(), Some(6));
521
522        // Test case 4: Explicit tag length set to max
523        let params_tag_max = RoutingParameters::new(AddressInterface::BasicWallet)
524            .with_note_tag_len(NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH)?;
525        let serialized = params_tag_max.to_bytes();
526        let deserialized = RoutingParameters::read_from_bytes(&serialized)?;
527        assert_eq!(params_tag_max, deserialized);
528        assert_eq!(deserialized.note_tag_len(), Some(NoteTag::MAX_ACCOUNT_TARGET_TAG_LENGTH));
529
530        Ok(())
531    }
532
533    /// Tests encoding/decoding and serialization for all encryption key variants.
534    #[test]
535    fn routing_parameters_all_encryption_key_variants() -> anyhow::Result<()> {
536        // Helper function to test both encoding/decoding and serialization
537        fn test_encryption_key_roundtrip(encryption_key: SealingKey) -> anyhow::Result<()> {
538            let routing_params = RoutingParameters::new(AddressInterface::BasicWallet)
539                .with_encryption_key(encryption_key.clone());
540
541            // Test bech32 encoding/decoding
542            let encoded = routing_params.encode_to_string();
543            let decoded = RoutingParameters::decode(encoded)?;
544            assert_eq!(routing_params, decoded);
545            assert_eq!(decoded.encryption_key(), Some(&encryption_key));
546
547            // Test serialization/deserialization
548            let serialized = routing_params.to_bytes();
549            let deserialized = RoutingParameters::read_from_bytes(&serialized)?;
550            assert_eq!(routing_params, deserialized);
551            assert_eq!(deserialized.encryption_key(), Some(&encryption_key));
552
553            Ok(())
554        }
555
556        // Test X25519XChaCha20Poly1305
557        {
558            use crate::crypto::dsa::eddsa_25519_sha512::KeyExchangeKey;
559            let secret_key = KeyExchangeKey::with_rng(&mut rand::rng());
560            let public_key = secret_key.public_key();
561            let encryption_key = SealingKey::X25519XChaCha20Poly1305(public_key);
562            test_encryption_key_roundtrip(encryption_key)?;
563        }
564
565        // Test K256XChaCha20Poly1305
566        {
567            use crate::crypto::dsa::ecdsa_k256_keccak::KeyExchangeKey;
568            let secret_key = KeyExchangeKey::with_rng(&mut rand::rng());
569            let public_key = secret_key.public_key();
570            let encryption_key = SealingKey::K256XChaCha20Poly1305(public_key);
571            test_encryption_key_roundtrip(encryption_key)?;
572        }
573
574        // Test X25519AeadPoseidon2
575        {
576            use crate::crypto::dsa::eddsa_25519_sha512::KeyExchangeKey;
577            let secret_key = KeyExchangeKey::with_rng(&mut rand::rng());
578            let public_key = secret_key.public_key();
579            let encryption_key = SealingKey::X25519AeadPoseidon2(public_key);
580            test_encryption_key_roundtrip(encryption_key)?;
581        }
582
583        // Test K256AeadPoseidon2
584        {
585            use crate::crypto::dsa::ecdsa_k256_keccak::KeyExchangeKey;
586            let secret_key = KeyExchangeKey::with_rng(&mut rand::rng());
587            let public_key = secret_key.public_key();
588            let encryption_key = SealingKey::K256AeadPoseidon2(public_key);
589            test_encryption_key_roundtrip(encryption_key)?;
590        }
591
592        // Test X25519AeadEidos
593        {
594            use crate::crypto::dsa::eddsa_25519_sha512::KeyExchangeKey;
595            let secret_key = KeyExchangeKey::with_rng(&mut rand::rng());
596            let public_key = secret_key.public_key();
597            let encryption_key = SealingKey::X25519AeadEidos(public_key);
598            test_encryption_key_roundtrip(encryption_key)?;
599        }
600
601        // Test K256AeadEidos
602        {
603            use crate::crypto::dsa::ecdsa_k256_keccak::KeyExchangeKey;
604            let secret_key = KeyExchangeKey::with_rng(&mut rand::rng());
605            let public_key = secret_key.public_key();
606            let encryption_key = SealingKey::K256AeadEidos(public_key);
607            test_encryption_key_roundtrip(encryption_key)?;
608        }
609
610        Ok(())
611    }
612}