bc_components/encapsulation/
encapsulation_public_key.rs

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