bc_components/encapsulation/
encapsulation_private_key.rs

1use anyhow::{Result, bail};
2use dcbor::prelude::*;
3
4use crate::{
5    Decrypter, EncapsulationCiphertext, EncapsulationScheme, MLKEMPrivateKey,
6    SymmetricKey, X25519PrivateKey, tags,
7};
8
9/// A private key used for key encapsulation mechanisms (KEM).
10///
11/// `EncapsulationPrivateKey` is an enum representing different types of private
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 private keys are used to decrypt (decapsulate) shared secrets that
19/// have been encapsulated with the corresponding public keys.
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
21pub enum EncapsulationPrivateKey {
22    /// An X25519 private key
23    X25519(X25519PrivateKey),
24    /// An ML-KEM private key (post-quantum)
25    MLKEM(MLKEMPrivateKey),
26}
27
28impl EncapsulationPrivateKey {
29    /// Returns the encapsulation scheme associated with this private key.
30    ///
31    /// # Returns
32    ///
33    /// The encapsulation scheme (X25519, MLKEM512, MLKEM768, or MLKEM1024)
34    /// that corresponds to this private key.
35    ///
36    /// # Example
37    ///
38    /// ```
39    /// use bc_components::{
40    ///     EncapsulationPrivateKey, EncapsulationScheme, X25519PrivateKey,
41    /// };
42    ///
43    /// let x25519_private_key = X25519PrivateKey::new();
44    /// let encapsulation_private_key =
45    ///     EncapsulationPrivateKey::X25519(x25519_private_key);
46    /// assert_eq!(
47    ///     encapsulation_private_key.encapsulation_scheme(),
48    ///     EncapsulationScheme::X25519
49    /// );
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    /// Decapsulates a shared secret from a ciphertext using this private key.
63    ///
64    /// This method performs the decapsulation operation for key exchange. It
65    /// takes an `EncapsulationCiphertext` and extracts the shared secret
66    /// that was encapsulated using the corresponding public key.
67    ///
68    /// # Parameters
69    ///
70    /// * `ciphertext` - The encapsulation ciphertext containing the
71    ///   encapsulated shared secret
72    ///
73    /// # Returns
74    ///
75    /// A `Result` containing the decapsulated `SymmetricKey` if successful,
76    /// or an error if the decapsulation fails or if the ciphertext type doesn't
77    /// match the private key type.
78    ///
79    /// # Errors
80    ///
81    /// Returns an error if:
82    /// - The ciphertext type doesn't match the private key type
83    /// - The decapsulation operation fails
84    ///
85    /// # Example
86    ///
87    /// ```
88    /// use bc_components::EncapsulationScheme;
89    ///
90    /// // Generate a key pair
91    /// let (private_key, public_key) = EncapsulationScheme::default().keypair();
92    ///
93    /// // Encapsulate a new shared secret using the public key
94    /// let (secret1, ciphertext) = public_key.encapsulate_new_shared_secret();
95    ///
96    /// // Decapsulate the shared secret using the private key
97    /// let secret2 = private_key.decapsulate_shared_secret(&ciphertext).unwrap();
98    ///
99    /// // The original and decapsulated secrets should match
100    /// assert_eq!(secret1, secret2);
101    /// ```
102    pub fn decapsulate_shared_secret(
103        &self,
104        ciphertext: &EncapsulationCiphertext,
105    ) -> Result<SymmetricKey> {
106        match (self, ciphertext) {
107            (
108                EncapsulationPrivateKey::X25519(private_key),
109                EncapsulationCiphertext::X25519(public_key),
110            ) => Ok(private_key.shared_key_with(public_key)),
111            (
112                EncapsulationPrivateKey::MLKEM(private_key),
113                EncapsulationCiphertext::MLKEM(ciphertext),
114            ) => private_key.decapsulate_shared_secret(ciphertext),
115            _ => bail!(
116                "Mismatched key encapsulation types. private key: {:?}, ciphertext: {:?}",
117                self.encapsulation_scheme(),
118                ciphertext.encapsulation_scheme()
119            ),
120        }
121    }
122}
123
124/// Implementation of the `Decrypter` trait for `EncapsulationPrivateKey`.
125///
126/// This allows `EncapsulationPrivateKey` to be used with the generic decryption
127/// interface defined by the `Decrypter` trait.
128impl Decrypter for EncapsulationPrivateKey {
129    fn encapsulation_private_key(&self) -> EncapsulationPrivateKey {
130        self.clone()
131    }
132
133    fn decapsulate_shared_secret(
134        &self,
135        ciphertext: &EncapsulationCiphertext,
136    ) -> Result<SymmetricKey> {
137        self.decapsulate_shared_secret(ciphertext)
138    }
139}
140
141/// Conversion from `EncapsulationPrivateKey` to CBOR for serialization.
142impl From<EncapsulationPrivateKey> for CBOR {
143    fn from(private_key: EncapsulationPrivateKey) -> Self {
144        match private_key {
145            EncapsulationPrivateKey::X25519(private_key) => private_key.into(),
146            EncapsulationPrivateKey::MLKEM(private_key) => private_key.into(),
147        }
148    }
149}
150
151/// Conversion from CBOR to `EncapsulationPrivateKey` for deserialization.
152impl TryFrom<CBOR> for EncapsulationPrivateKey {
153    type Error = anyhow::Error;
154
155    fn try_from(cbor: CBOR) -> Result<Self> {
156        match cbor.as_case() {
157            CBORCase::Tagged(tag, _) => match tag.value() {
158                tags::TAG_X25519_PRIVATE_KEY => {
159                    Ok(EncapsulationPrivateKey::X25519(
160                        X25519PrivateKey::try_from(cbor)?,
161                    ))
162                }
163                tags::TAG_MLKEM_PRIVATE_KEY => {
164                    Ok(EncapsulationPrivateKey::MLKEM(
165                        MLKEMPrivateKey::try_from(cbor)?,
166                    ))
167                }
168                _ => bail!("Invalid encapsulation private key"),
169            },
170            _ => bail!("Invalid encapsulation private key"),
171        }
172    }
173}