bc_components/encapsulation/
encapsulation_public_key.rs

1use bc_ur::prelude::*;
2
3#[cfg(feature = "pqcrypto")]
4use crate::MLKEMPublicKey;
5use crate::{
6    Digest, EncapsulationCiphertext, EncapsulationScheme, Encrypter,
7    PrivateKeyBase, Reference, ReferenceProvider, SymmetricKey,
8    X25519PublicKey, tags,
9};
10
11/// A public key used for key encapsulation mechanisms (KEM).
12///
13/// `EncapsulationPublicKey` is an enum representing different types of public
14/// keys that can be used for key encapsulation, including:
15///
16/// - X25519: Curve25519-based key exchange
17/// - ML-KEM: Module Lattice-based Key Encapsulation Mechanism at various
18///   security levels
19///
20/// These public keys are used to encrypt (encapsulate) shared secrets that can
21/// only be decrypted (decapsulated) by the corresponding private key holder.
22#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23pub enum EncapsulationPublicKey {
24    /// An X25519 public key
25    X25519(X25519PublicKey),
26    /// An ML-KEM public key (post-quantum)
27    #[cfg(feature = "pqcrypto")]
28    MLKEM(MLKEMPublicKey),
29}
30
31impl EncapsulationPublicKey {
32    /// Returns the encapsulation scheme associated with this public key.
33    ///
34    /// # Returns
35    ///
36    /// The encapsulation scheme (X25519, MLKEM512, MLKEM768, or MLKEM1024)
37    /// that corresponds to this public key.
38    ///
39    /// # Example
40    ///
41    /// ```
42    /// use bc_components::{EncapsulationScheme, X25519PrivateKey};
43    ///
44    /// // Generate a keypair
45    /// let private_key = X25519PrivateKey::new();
46    /// let public_key = private_key.public_key();
47    ///
48    /// // Convert to encapsulation public key
49    /// let encapsulation_public_key =
50    ///     bc_components::EncapsulationPublicKey::X25519(public_key);
51    ///
52    /// // Check the scheme
53    /// assert_eq!(
54    ///     encapsulation_public_key.encapsulation_scheme(),
55    ///     EncapsulationScheme::X25519
56    /// );
57    /// ```
58    pub fn encapsulation_scheme(&self) -> EncapsulationScheme {
59        match self {
60            Self::X25519(_) => EncapsulationScheme::X25519,
61            #[cfg(feature = "pqcrypto")]
62            Self::MLKEM(pk) => match pk.level() {
63                crate::MLKEM::MLKEM512 => EncapsulationScheme::MLKEM512,
64                crate::MLKEM::MLKEM768 => EncapsulationScheme::MLKEM768,
65                crate::MLKEM::MLKEM1024 => EncapsulationScheme::MLKEM1024,
66            },
67        }
68    }
69
70    /// Encapsulates a new shared secret using this public key.
71    ///
72    /// This method performs the encapsulation operation for key exchange. It
73    /// generates a new shared secret and encapsulates it using this public
74    /// key.
75    ///
76    /// The encapsulation process differs based on the key type:
77    /// - For X25519: Generates an ephemeral private/public key pair, derives a
78    ///   shared secret using Diffie-Hellman, and returns the shared secret
79    ///   along with the ephemeral public key
80    /// - For ML-KEM: Uses the KEM encapsulation algorithm to generate and
81    ///   encapsulate a random shared secret
82    ///
83    /// # Returns
84    ///
85    /// A tuple containing:
86    /// - The generated shared secret as a `SymmetricKey`
87    /// - The encapsulation ciphertext that can be sent to the private key
88    ///   holder
89    ///
90    /// # Example
91    ///
92    /// ```
93    /// use bc_components::EncapsulationScheme;
94    ///
95    /// // Generate a key pair using the default scheme (X25519)
96    /// let (private_key, public_key) = EncapsulationScheme::default().keypair();
97    ///
98    /// // Encapsulate a new shared secret
99    /// let (shared_secret, ciphertext) =
100    ///     public_key.encapsulate_new_shared_secret();
101    ///
102    /// // The private key holder can recover the same shared secret
103    /// let recovered_secret =
104    ///     private_key.decapsulate_shared_secret(&ciphertext).unwrap();
105    /// assert_eq!(shared_secret, recovered_secret);
106    /// ```
107    pub fn encapsulate_new_shared_secret(
108        &self,
109    ) -> (SymmetricKey, EncapsulationCiphertext) {
110        match self {
111            EncapsulationPublicKey::X25519(public_key) => {
112                let emphemeral_sender = PrivateKeyBase::new();
113                let ephemeral_private_key =
114                    emphemeral_sender.x25519_private_key();
115                let ephemeral_public_key = ephemeral_private_key.public_key();
116                let shared_key =
117                    ephemeral_private_key.shared_key_with(public_key);
118                (
119                    shared_key,
120                    EncapsulationCiphertext::X25519(ephemeral_public_key),
121                )
122            }
123            #[cfg(feature = "pqcrypto")]
124            EncapsulationPublicKey::MLKEM(public_key) => {
125                let (shared_key, ciphertext) =
126                    public_key.encapsulate_new_shared_secret();
127                (shared_key, EncapsulationCiphertext::MLKEM(ciphertext))
128            }
129        }
130    }
131}
132
133/// Implementation of the `Encrypter` trait for `EncapsulationPublicKey`.
134///
135/// This allows `EncapsulationPublicKey` to be used with the generic encryption
136/// interface defined by the `Encrypter` trait.
137impl Encrypter for EncapsulationPublicKey {
138    fn encapsulation_public_key(&self) -> EncapsulationPublicKey {
139        self.clone()
140    }
141
142    fn encapsulate_new_shared_secret(
143        &self,
144    ) -> (SymmetricKey, EncapsulationCiphertext) {
145        self.encapsulate_new_shared_secret()
146    }
147}
148
149/// Conversion from `EncapsulationPublicKey` to CBOR for serialization.
150impl From<EncapsulationPublicKey> for CBOR {
151    fn from(public_key: EncapsulationPublicKey) -> Self {
152        match public_key {
153            EncapsulationPublicKey::X25519(public_key) => public_key.into(),
154            #[cfg(feature = "pqcrypto")]
155            EncapsulationPublicKey::MLKEM(public_key) => public_key.into(),
156        }
157    }
158}
159
160/// Conversion from CBOR to `EncapsulationPublicKey` for deserialization.
161impl TryFrom<CBOR> for EncapsulationPublicKey {
162    type Error = dcbor::Error;
163
164    fn try_from(cbor: CBOR) -> std::result::Result<Self, dcbor::Error> {
165        match cbor.as_case() {
166            CBORCase::Tagged(tag, _) => match tag.value() {
167                tags::TAG_X25519_PUBLIC_KEY => {
168                    Ok(EncapsulationPublicKey::X25519(
169                        X25519PublicKey::try_from(cbor)?,
170                    ))
171                }
172                #[cfg(feature = "pqcrypto")]
173                tags::TAG_MLKEM_PUBLIC_KEY => {
174                    Ok(EncapsulationPublicKey::MLKEM(MLKEMPublicKey::try_from(
175                        cbor,
176                    )?))
177                }
178                _ => Err(dcbor::Error::msg("Invalid encapsulation public key")),
179            },
180            _ => Err(dcbor::Error::msg("Invalid encapsulation public key")),
181        }
182    }
183}
184
185impl ReferenceProvider for EncapsulationPublicKey {
186    fn reference(&self) -> Reference {
187        Reference::from_digest(Digest::from_image(self.to_cbor_data()))
188    }
189}
190
191impl std::fmt::Display for EncapsulationPublicKey {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        let display_key = match self {
194            EncapsulationPublicKey::X25519(key) => key.to_string(),
195            #[cfg(feature = "pqcrypto")]
196            EncapsulationPublicKey::MLKEM(key) => key.to_string(),
197        };
198        write!(
199            f,
200            "EncapsulationPublicKey({}, {})",
201            self.ref_hex_short(),
202            display_key
203        )
204    }
205}