bc_components/encapsulation/
encapsulation_public_key.rs

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