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}