bc_components/encapsulation/
encapsulation_private_key.rs

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