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}