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}