miden_objects/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::AddressError;
9use crate::address::AddressInterface;
10use crate::crypto::dsa::{ecdsa_k256_keccak, eddsa_25519};
11use crate::crypto::ies::SealingKey;
12use crate::errors::Bech32Error;
13use crate::note::NoteTag;
14use crate::utils::serde::{
15    ByteReader,
16    ByteWriter,
17    Deserializable,
18    DeserializationError,
19    Serializable,
20};
21use crate::utils::sync::LazyLock;
22
23/// The HRP used for encoding routing parameters.
24///
25/// This HRP is only used internally, but needs to be well-defined for other routing parameter
26/// encode/decode implementations.
27///
28/// `mrp` stands for Miden Routing Parameters.
29static ROUTING_PARAMETERS_HRP: LazyLock<Hrp> =
30    LazyLock::new(|| Hrp::parse("mrp").expect("hrp should be valid"));
31
32/// The separator character used in bech32.
33const BECH32_SEPARATOR: &str = "1";
34
35/// The value to encode the absence of a note tag routing parameter (i.e. `None`).
36///
37/// The note tag length occupies 5 bits (values 0..=31). Valid tag lengths are 0..=30,
38/// so we reserve the maximum 5-bit value (31) to represent `None`.
39///
40/// If the note tag length is absent from routing parameters, the note tag length for the address
41/// will be set to the default default tag length of the address' ID component.
42const ABSENT_NOTE_TAG_LEN: u8 = (1 << 5) - 1; // 31
43
44/// The routing parameter key for the receiver profile.
45const RECEIVER_PROFILE_PARAM_KEY: u8 = 0;
46
47/// The routing parameter key for the encryption key.
48const ENCRYPTION_KEY_PARAM_KEY: u8 = 1;
49
50/// The expected length of Ed25519/X25519 public keys in bytes.
51const X25519_PUBLIC_KEY_LENGTH: usize = 32;
52
53/// The expected length of K256 (secp256k1) public keys in bytes (compressed format).
54const K256_PUBLIC_KEY_LENGTH: usize = 33;
55
56/// Discriminants for encryption key variants.
57const ENCRYPTION_KEY_X25519_XCHACHA20POLY1305: u8 = 0;
58const ENCRYPTION_KEY_K256_XCHACHA20POLY1305: u8 = 1;
59const ENCRYPTION_KEY_X25519_AEAD_RPO: u8 = 2;
60const ENCRYPTION_KEY_K256_AEAD_RPO: u8 = 3;
61
62/// Parameters that define how a sender should route a note to the [`AddressId`](super::AddressId)
63/// in an [`Address`](super::Address).
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct RoutingParameters {
66    interface: AddressInterface,
67    note_tag_len: Option<u8>,
68    encryption_key: Option<SealingKey>,
69}
70
71impl RoutingParameters {
72    // CONSTRUCTORS
73    // --------------------------------------------------------------------------------------------
74
75    /// Creates new [`RoutingParameters`] from an [`AddressInterface`] and all other parameters
76    /// initialized to `None`.
77    pub fn new(interface: AddressInterface) -> Self {
78        Self {
79            interface,
80            note_tag_len: None,
81            encryption_key: None,
82        }
83    }
84
85    /// Sets the note tag length routing parameter.
86    ///
87    /// The tag length determines how many bits of the address ID are encoded into [`NoteTag`]s of
88    /// notes targeted to this address. This lets the receiver choose their level of privacy. A
89    /// higher tag length makes the address ID more uniquely identifiable and reduces privacy,
90    /// while a shorter length increases privacy at the cost of matching more notes
91    /// published onchain.
92    ///
93    /// # Errors
94    ///
95    /// Returns an error if:
96    /// - The tag length exceeds the maximum of [`NoteTag::MAX_LOCAL_TAG_LENGTH`] and
97    ///   [`NoteTag::DEFAULT_NETWORK_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_LOCAL_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..=30` (e.g. the maximum of
113    /// [`NoteTag::MAX_LOCAL_TAG_LENGTH`] and [`NoteTag::DEFAULT_NETWORK_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> = source.read_many(num_bytes as usize)?;
258
259        Self::decode_from_bytes(bytes.into_iter())
260            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
261    }
262}
263
264// ENCODING / DECODING HELPERS
265// ================================================================================================
266
267/// Returns receiver profile bytes constructed from the provided interface and note tag length.
268fn encode_receiver_profile(interface: AddressInterface, note_tag_len: Option<u8>) -> [u8; 2] {
269    let note_tag_len = note_tag_len.unwrap_or(ABSENT_NOTE_TAG_LEN);
270
271    let interface = interface as u16;
272    debug_assert_eq!(interface >> 11, 0, "address interface should have its upper 5 bits unset");
273
274    // The interface takes up 11 bits and the tag length 5 bits, so we can merge them
275    // together.
276    let tag_len = (note_tag_len as u16) << 11;
277    let receiver_profile: u16 = tag_len | interface;
278    receiver_profile.to_be_bytes()
279}
280
281/// Reads the receiver profile from the provided bytes.
282fn decode_receiver_profile(
283    byte_iter: &mut impl ExactSizeIterator<Item = u8>,
284) -> Result<(AddressInterface, Option<u8>), AddressError> {
285    if byte_iter.len() < 2 {
286        return Err(AddressError::decode_error("expected two bytes to decode receiver profile"));
287    };
288
289    let byte0 = byte_iter.next().expect("byte0 should exist");
290    let byte1 = byte_iter.next().expect("byte1 should exist");
291    let receiver_profile = u16::from_be_bytes([byte0, byte1]);
292
293    let tag_len = (receiver_profile >> 11) as u8;
294    let note_tag_len = if tag_len == ABSENT_NOTE_TAG_LEN {
295        None
296    } else {
297        Some(tag_len)
298    };
299
300    let addr_interface = receiver_profile & 0b0000_0111_1111_1111;
301    let addr_interface = AddressInterface::try_from(addr_interface).map_err(|err| {
302        AddressError::decode_error_with_source("failed to decode address interface", err)
303    })?;
304
305    Ok((addr_interface, note_tag_len))
306}
307
308/// Append encryption key variant discriminant and key to the provided vector of bytes.
309fn encode_encryption_key(key: &SealingKey, encoded: &mut Vec<u8>) {
310    match key {
311        SealingKey::X25519XChaCha20Poly1305(pk) => {
312            encoded.push(ENCRYPTION_KEY_X25519_XCHACHA20POLY1305);
313            encoded.extend(&pk.to_bytes());
314        },
315        SealingKey::K256XChaCha20Poly1305(pk) => {
316            encoded.push(ENCRYPTION_KEY_K256_XCHACHA20POLY1305);
317            encoded.extend(&pk.to_bytes());
318        },
319        SealingKey::X25519AeadRpo(pk) => {
320            encoded.push(ENCRYPTION_KEY_X25519_AEAD_RPO);
321            encoded.extend(&pk.to_bytes());
322        },
323        SealingKey::K256AeadRpo(pk) => {
324            encoded.push(ENCRYPTION_KEY_K256_AEAD_RPO);
325            encoded.extend(&pk.to_bytes());
326        },
327    }
328}
329
330/// Reads the encryption key from the provided bytes.
331fn decode_encryption_key(
332    byte_iter: &mut impl ExactSizeIterator<Item = u8>,
333) -> Result<SealingKey, AddressError> {
334    // Read variant discriminant
335    let Some(variant) = byte_iter.next() else {
336        return Err(AddressError::decode_error(
337            "expected at least 1 byte for encryption key variant",
338        ));
339    };
340
341    // Reconstruct the appropriate PublicEncryptionKey variant
342    let public_encryption_key = match variant {
343        ENCRYPTION_KEY_X25519_XCHACHA20POLY1305 => {
344            SealingKey::X25519XChaCha20Poly1305(read_x25519_pub_key(byte_iter)?)
345        },
346        ENCRYPTION_KEY_K256_XCHACHA20POLY1305 => {
347            SealingKey::K256XChaCha20Poly1305(read_k256_pub_key(byte_iter)?)
348        },
349        ENCRYPTION_KEY_X25519_AEAD_RPO => {
350            SealingKey::X25519AeadRpo(read_x25519_pub_key(byte_iter)?)
351        },
352        ENCRYPTION_KEY_K256_AEAD_RPO => SealingKey::K256AeadRpo(read_k256_pub_key(byte_iter)?),
353        other => {
354            return Err(AddressError::decode_error(format!(
355                "unknown encryption key variant: {}",
356                other
357            )));
358        },
359    };
360
361    Ok(public_encryption_key)
362}
363
364fn read_x25519_pub_key(
365    byte_iter: &mut impl ExactSizeIterator<Item = u8>,
366) -> Result<eddsa_25519::PublicKey, AddressError> {
367    if byte_iter.len() < X25519_PUBLIC_KEY_LENGTH {
368        return Err(AddressError::decode_error(format!(
369            "expected {} bytes to decode X25519 public key",
370            X25519_PUBLIC_KEY_LENGTH
371        )));
372    }
373    let key_bytes: [u8; X25519_PUBLIC_KEY_LENGTH] = read_byte_array(byte_iter);
374    eddsa_25519::PublicKey::read_from_bytes(&key_bytes).map_err(|err| {
375        AddressError::decode_error_with_source("failed to decode X25519 public key", err)
376    })
377}
378
379fn read_k256_pub_key(
380    byte_iter: &mut impl ExactSizeIterator<Item = u8>,
381) -> Result<ecdsa_k256_keccak::PublicKey, AddressError> {
382    if byte_iter.len() < K256_PUBLIC_KEY_LENGTH {
383        return Err(AddressError::decode_error(format!(
384            "expected {} bytes to decode K256 public key",
385            K256_PUBLIC_KEY_LENGTH
386        )));
387    }
388    let key_bytes: [u8; K256_PUBLIC_KEY_LENGTH] = read_byte_array(byte_iter);
389    ecdsa_k256_keccak::PublicKey::read_from_bytes(&key_bytes).map_err(|err| {
390        AddressError::decode_error_with_source("failed to decode K256 public key", err)
391    })
392}
393
394/// Reads bytes from the provided iterator into an array of length N and returns this array.
395///
396/// Assumes that there are at least N bytes in the iterator.
397fn read_byte_array<const N: usize>(byte_iter: &mut impl ExactSizeIterator<Item = u8>) -> [u8; N] {
398    let mut array = [0u8; N];
399    for byte in array.iter_mut() {
400        *byte = byte_iter.next().expect("iterator should have enough bytes");
401    }
402    array
403}
404
405// TESTS
406// ================================================================================================
407
408#[cfg(test)]
409mod tests {
410    use bech32::{Bech32m, Checksum, Hrp};
411
412    use super::*;
413
414    /// Checks the assumptions about the total length allowed in bech32 encoding.
415    ///
416    /// The assumption is that encoding should error if the total length of the hrp + data (encoded
417    /// in GF(32)) + the separator + the checksum exceeds Bech32m::CODE_LENGTH.
418    #[test]
419    fn bech32_code_length_assertions() -> anyhow::Result<()> {
420        let hrp = Hrp::parse("mrp").unwrap();
421        let separator_len = BECH32_SEPARATOR.len();
422        // The fixed number of characters included in a bech32 string.
423        let fixed_num_bytes = hrp.as_str().len() + separator_len + Bech32m::CHECKSUM_LENGTH;
424        let num_allowed_chars = Bech32m::CODE_LENGTH - fixed_num_bytes;
425        // Multiply by the 5 bits per base32 character and divide by 8 bits per byte.
426        let num_allowed_bytes = num_allowed_chars * 5 / 8;
427
428        // The number of bytes that routing parameters effectively have available.
429        assert_eq!(num_allowed_bytes, 633);
430
431        // This amount of data is the max that should be okay to encode.
432        let data_ok = vec![5; num_allowed_bytes];
433        // One more byte than the max allowed amount should result in an error.
434        let data_too_long = vec![5; num_allowed_bytes + 1];
435
436        assert!(bech32::encode::<Bech32m>(hrp, &data_ok).is_ok());
437        assert!(bech32::encode::<Bech32m>(hrp, &data_too_long).is_err());
438
439        Ok(())
440    }
441
442    /// Tests bech32 encoding and decoding roundtrip with various tag lengths.
443    #[test]
444    fn routing_parameters_bech32_encode_decode_roundtrip() -> anyhow::Result<()> {
445        // Test case 1: No explicit tag length
446        let params_no_tag = RoutingParameters::new(AddressInterface::BasicWallet);
447        let encoded = params_no_tag.encode_to_string();
448        let decoded = RoutingParameters::decode(encoded)?;
449        assert_eq!(params_no_tag, decoded);
450        assert_eq!(decoded.note_tag_len(), None);
451
452        // Test case 2: Explicit tag length 0
453        let params_tag_0 =
454            RoutingParameters::new(AddressInterface::BasicWallet).with_note_tag_len(0)?;
455        let encoded = params_tag_0.encode_to_string();
456        let decoded = RoutingParameters::decode(encoded)?;
457        assert_eq!(params_tag_0, decoded);
458        assert_eq!(decoded.note_tag_len(), Some(0));
459
460        // Test case 3: Explicit tag length 6
461        let params_tag_6 =
462            RoutingParameters::new(AddressInterface::BasicWallet).with_note_tag_len(6)?;
463        let encoded = params_tag_6.encode_to_string();
464        let decoded = RoutingParameters::decode(encoded)?;
465        assert_eq!(params_tag_6, decoded);
466        assert_eq!(decoded.note_tag_len(), Some(6));
467
468        // Test case 4: Explicit tag length set to max
469        let params_tag_max = RoutingParameters::new(AddressInterface::BasicWallet)
470            .with_note_tag_len(NoteTag::MAX_LOCAL_TAG_LENGTH)?;
471        let encoded = params_tag_max.encode_to_string();
472        let decoded = RoutingParameters::decode(encoded)?;
473        assert_eq!(params_tag_max, decoded);
474        assert_eq!(decoded.note_tag_len(), Some(NoteTag::MAX_LOCAL_TAG_LENGTH));
475
476        Ok(())
477    }
478
479    /// Tests serialization and deserialization roundtrip with various tag lengths.
480    #[test]
481    fn routing_parameters_serialization() -> anyhow::Result<()> {
482        // Test case 1: No explicit tag length
483        let params_no_tag = RoutingParameters::new(AddressInterface::BasicWallet);
484        let serialized = params_no_tag.to_bytes();
485        let deserialized = RoutingParameters::read_from_bytes(&serialized)?;
486        assert_eq!(params_no_tag, deserialized);
487        assert_eq!(deserialized.note_tag_len(), None);
488
489        // Test case 2: Explicit tag length 0
490        let params_tag_0 =
491            RoutingParameters::new(AddressInterface::BasicWallet).with_note_tag_len(0)?;
492        let serialized = params_tag_0.to_bytes();
493        let deserialized = RoutingParameters::read_from_bytes(&serialized)?;
494        assert_eq!(params_tag_0, deserialized);
495        assert_eq!(deserialized.note_tag_len(), Some(0));
496
497        // Test case 3: Explicit tag length 6
498        let params_tag_6 =
499            RoutingParameters::new(AddressInterface::BasicWallet).with_note_tag_len(6)?;
500        let serialized = params_tag_6.to_bytes();
501        let deserialized = RoutingParameters::read_from_bytes(&serialized)?;
502        assert_eq!(params_tag_6, deserialized);
503        assert_eq!(deserialized.note_tag_len(), Some(6));
504
505        // Test case 4: Explicit tag length set to max
506        let params_tag_max = RoutingParameters::new(AddressInterface::BasicWallet)
507            .with_note_tag_len(NoteTag::MAX_LOCAL_TAG_LENGTH)?;
508        let serialized = params_tag_max.to_bytes();
509        let deserialized = RoutingParameters::read_from_bytes(&serialized)?;
510        assert_eq!(params_tag_max, deserialized);
511        assert_eq!(deserialized.note_tag_len(), Some(NoteTag::MAX_LOCAL_TAG_LENGTH));
512
513        Ok(())
514    }
515
516    /// Tests encoding/decoding and serialization for all encryption key variants.
517    #[test]
518    fn routing_parameters_all_encryption_key_variants() -> anyhow::Result<()> {
519        // Helper function to test both encoding/decoding and serialization
520        fn test_encryption_key_roundtrip(encryption_key: SealingKey) -> anyhow::Result<()> {
521            let routing_params = RoutingParameters::new(AddressInterface::BasicWallet)
522                .with_encryption_key(encryption_key.clone());
523
524            // Test bech32 encoding/decoding
525            let encoded = routing_params.encode_to_string();
526            let decoded = RoutingParameters::decode(encoded)?;
527            assert_eq!(routing_params, decoded);
528            assert_eq!(decoded.encryption_key(), Some(&encryption_key));
529
530            // Test serialization/deserialization
531            let serialized = routing_params.to_bytes();
532            let deserialized = RoutingParameters::read_from_bytes(&serialized)?;
533            assert_eq!(routing_params, deserialized);
534            assert_eq!(deserialized.encryption_key(), Some(&encryption_key));
535
536            Ok(())
537        }
538
539        // Test X25519XChaCha20Poly1305
540        {
541            use crate::crypto::dsa::eddsa_25519::SecretKey;
542            let secret_key = SecretKey::with_rng(&mut rand::rng());
543            let public_key = secret_key.public_key();
544            let encryption_key = SealingKey::X25519XChaCha20Poly1305(public_key);
545            test_encryption_key_roundtrip(encryption_key)?;
546        }
547
548        // Test K256XChaCha20Poly1305
549        {
550            use crate::crypto::dsa::ecdsa_k256_keccak::SecretKey;
551            let secret_key = SecretKey::with_rng(&mut rand::rng());
552            let public_key = secret_key.public_key();
553            let encryption_key = SealingKey::K256XChaCha20Poly1305(public_key);
554            test_encryption_key_roundtrip(encryption_key)?;
555        }
556
557        // Test X25519AeadRpo
558        {
559            use crate::crypto::dsa::eddsa_25519::SecretKey;
560            let secret_key = SecretKey::with_rng(&mut rand::rng());
561            let public_key = secret_key.public_key();
562            let encryption_key = SealingKey::X25519AeadRpo(public_key);
563            test_encryption_key_roundtrip(encryption_key)?;
564        }
565
566        // Test K256AeadRpo
567        {
568            use crate::crypto::dsa::ecdsa_k256_keccak::SecretKey;
569            let secret_key = SecretKey::with_rng(&mut rand::rng());
570            let public_key = secret_key.public_key();
571            let encryption_key = SealingKey::K256AeadRpo(public_key);
572            test_encryption_key_roundtrip(encryption_key)?;
573        }
574
575        Ok(())
576    }
577}