bc_components/encapsulation/encapsulation_private_key.rs
1use bc_ur::prelude::*;
2
3use crate::EncapsulationPublicKey;
4#[cfg(feature = "pqcrypto")]
5use crate::MLKEMPrivateKey;
6#[cfg_attr(not(feature = "pqcrypto"), allow(unused_imports))]
7use crate::{
8 Decrypter, Digest, EncapsulationCiphertext, EncapsulationScheme, Error,
9 Reference, ReferenceProvider, Result, SymmetricKey, X25519PrivateKey, tags,
10};
11
12/// A private key used for key encapsulation mechanisms (KEM).
13///
14/// `EncapsulationPrivateKey` is an enum representing different types of private
15/// keys that can be used for key encapsulation, including:
16///
17/// - X25519: Curve25519-based key exchange
18/// - ML-KEM: Module Lattice-based Key Encapsulation Mechanism at various
19/// security levels
20///
21/// These private keys are used to decrypt (decapsulate) shared secrets that
22/// have been encapsulated with the corresponding public keys.
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub enum EncapsulationPrivateKey {
25 /// An X25519 private key
26 X25519(X25519PrivateKey),
27 /// An ML-KEM private key (post-quantum)
28 #[cfg(feature = "pqcrypto")]
29 MLKEM(MLKEMPrivateKey),
30}
31
32impl EncapsulationPrivateKey {
33 /// Returns the encapsulation scheme associated with this private key.
34 ///
35 /// # Returns
36 ///
37 /// The encapsulation scheme (X25519, MLKEM512, MLKEM768, or MLKEM1024)
38 /// that corresponds to this private key.
39 ///
40 /// # Example
41 ///
42 /// ```
43 /// use bc_components::{
44 /// EncapsulationPrivateKey, EncapsulationScheme, X25519PrivateKey,
45 /// };
46 ///
47 /// let x25519_private_key = X25519PrivateKey::new();
48 /// let encapsulation_private_key =
49 /// EncapsulationPrivateKey::X25519(x25519_private_key);
50 /// assert_eq!(
51 /// encapsulation_private_key.encapsulation_scheme(),
52 /// EncapsulationScheme::X25519
53 /// );
54 /// ```
55 pub fn encapsulation_scheme(&self) -> EncapsulationScheme {
56 match self {
57 Self::X25519(_) => EncapsulationScheme::X25519,
58 #[cfg(feature = "pqcrypto")]
59 Self::MLKEM(pk) => match pk.level() {
60 crate::MLKEM::MLKEM512 => EncapsulationScheme::MLKEM512,
61 crate::MLKEM::MLKEM768 => EncapsulationScheme::MLKEM768,
62 crate::MLKEM::MLKEM1024 => EncapsulationScheme::MLKEM1024,
63 },
64 }
65 }
66
67 /// Decapsulates a shared secret from a ciphertext using this private key.
68 ///
69 /// This method performs the decapsulation operation for key exchange. It
70 /// takes an `EncapsulationCiphertext` and extracts the shared secret
71 /// that was encapsulated using the corresponding public key.
72 ///
73 /// # Parameters
74 ///
75 /// * `ciphertext` - The encapsulation ciphertext containing the
76 /// encapsulated shared secret
77 ///
78 /// # Returns
79 ///
80 /// A `Result` containing the decapsulated `SymmetricKey` if successful,
81 /// or an error if the decapsulation fails or if the ciphertext type doesn't
82 /// match the private key type.
83 ///
84 /// # Errors
85 ///
86 /// Returns an error if:
87 /// - The ciphertext type doesn't match the private key type
88 /// - The decapsulation operation fails
89 ///
90 /// # Example
91 ///
92 /// ```
93 /// use bc_components::EncapsulationScheme;
94 ///
95 /// // Generate a key pair
96 /// let (private_key, public_key) = EncapsulationScheme::default().keypair();
97 ///
98 /// // Encapsulate a new shared secret using the public key
99 /// let (secret1, ciphertext) = public_key.encapsulate_new_shared_secret();
100 ///
101 /// // Decapsulate the shared secret using the private key
102 /// let secret2 = private_key.decapsulate_shared_secret(&ciphertext).unwrap();
103 ///
104 /// // The original and decapsulated secrets should match
105 /// assert_eq!(secret1, secret2);
106 /// ```
107 pub fn decapsulate_shared_secret(
108 &self,
109 ciphertext: &EncapsulationCiphertext,
110 ) -> Result<SymmetricKey> {
111 match (self, ciphertext) {
112 (
113 EncapsulationPrivateKey::X25519(private_key),
114 EncapsulationCiphertext::X25519(public_key),
115 ) => Ok(private_key.shared_key_with(public_key)),
116 #[cfg(feature = "pqcrypto")]
117 (
118 EncapsulationPrivateKey::MLKEM(private_key),
119 EncapsulationCiphertext::MLKEM(ciphertext),
120 ) => private_key.decapsulate_shared_secret(ciphertext),
121 #[cfg(feature = "pqcrypto")]
122 _ => Err(Error::crypto(format!(
123 "Mismatched key encapsulation types. private key: {:?}, ciphertext: {:?}",
124 self.encapsulation_scheme(),
125 ciphertext.encapsulation_scheme()
126 ))),
127 }
128 }
129
130 pub fn public_key(&self) -> Result<EncapsulationPublicKey> {
131 match self {
132 Self::X25519(private_key) => {
133 Ok(EncapsulationPublicKey::X25519(private_key.public_key()))
134 }
135 #[cfg(feature = "pqcrypto")]
136 Self::MLKEM(_) => {
137 Err(Error::crypto("Deriving ML-KEM public key not supported"))
138 }
139 }
140 }
141}
142
143/// Implementation of the `Decrypter` trait for `EncapsulationPrivateKey`.
144///
145/// This allows `EncapsulationPrivateKey` to be used with the generic decryption
146/// interface defined by the `Decrypter` trait.
147impl Decrypter for EncapsulationPrivateKey {
148 fn encapsulation_private_key(&self) -> EncapsulationPrivateKey {
149 self.clone()
150 }
151
152 fn decapsulate_shared_secret(
153 &self,
154 ciphertext: &EncapsulationCiphertext,
155 ) -> Result<SymmetricKey> {
156 self.decapsulate_shared_secret(ciphertext)
157 }
158}
159
160/// Conversion from `EncapsulationPrivateKey` to CBOR for serialization.
161impl From<EncapsulationPrivateKey> for CBOR {
162 fn from(private_key: EncapsulationPrivateKey) -> Self {
163 match private_key {
164 EncapsulationPrivateKey::X25519(private_key) => private_key.into(),
165 #[cfg(feature = "pqcrypto")]
166 EncapsulationPrivateKey::MLKEM(private_key) => private_key.into(),
167 }
168 }
169}
170
171/// Conversion from CBOR to `EncapsulationPrivateKey` for deserialization.
172impl TryFrom<CBOR> for EncapsulationPrivateKey {
173 type Error = dcbor::Error;
174
175 fn try_from(cbor: CBOR) -> std::result::Result<Self, dcbor::Error> {
176 match cbor.as_case() {
177 CBORCase::Tagged(tag, _) => match tag.value() {
178 tags::TAG_X25519_PRIVATE_KEY => {
179 Ok(EncapsulationPrivateKey::X25519(
180 X25519PrivateKey::try_from(cbor)?,
181 ))
182 }
183 #[cfg(feature = "pqcrypto")]
184 tags::TAG_MLKEM_PRIVATE_KEY => {
185 Ok(EncapsulationPrivateKey::MLKEM(
186 MLKEMPrivateKey::try_from(cbor)?,
187 ))
188 }
189 _ => {
190 Err(dcbor::Error::msg("Invalid encapsulation private key"))
191 }
192 },
193 _ => Err(dcbor::Error::msg("Invalid encapsulation private key")),
194 }
195 }
196}
197
198impl ReferenceProvider for EncapsulationPrivateKey {
199 fn reference(&self) -> Reference {
200 Reference::from_digest(Digest::from_image(self.to_cbor_data()))
201 }
202}
203
204impl std::fmt::Display for EncapsulationPrivateKey {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 let display_key = match self {
207 EncapsulationPrivateKey::X25519(key) => key.to_string(),
208 #[cfg(feature = "pqcrypto")]
209 EncapsulationPrivateKey::MLKEM(key) => key.to_string(),
210 };
211 write!(
212 f,
213 "EncapsulationPrivateKey({}, {})",
214 self.ref_hex_short(),
215 display_key
216 )
217 }
218}