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