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}