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}