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}