libcrux/
kem.rs

1//! # Key Encapsulation Mechanism
2//!
3//! A KEM interface.
4
5use rand::{CryptoRng, Rng};
6
7use crate::ecdh;
8use crate::ecdh::p256;
9use crate::ecdh::p256_derive;
10use crate::ecdh::x25519;
11
12/// KEM Algorithms
13///
14/// This includes named elliptic curves or dedicated KEM algorithms like Kyber.
15#[derive(Clone, Copy, PartialEq, Debug)]
16pub enum Algorithm {
17    X25519,
18    X448,
19    Secp256r1,
20    Secp384r1,
21    Secp521r1,
22    Kyber512,
23    Kyber768,
24    Kyber768X25519,
25    Kyber1024,
26}
27
28#[derive(Debug, PartialEq, Eq)]
29pub enum Error {
30    EcDhError(ecdh::Error),
31    KeyGen,
32    Encapsulate,
33    Decapsulate,
34    UnsupportedAlgorithm,
35    InvalidPrivateKey,
36    InvalidPublicKey,
37    InvalidCiphertext,
38}
39
40impl TryFrom<Algorithm> for ecdh::Algorithm {
41    type Error = &'static str;
42
43    fn try_from(value: Algorithm) -> Result<Self, Self::Error> {
44        match value {
45            Algorithm::X25519 => Ok(ecdh::Algorithm::X25519),
46            Algorithm::X448 => Ok(ecdh::Algorithm::X448),
47            Algorithm::Secp256r1 => Ok(ecdh::Algorithm::P256),
48            Algorithm::Secp384r1 => Ok(ecdh::Algorithm::P384),
49            Algorithm::Secp521r1 => Ok(ecdh::Algorithm::P521),
50            Algorithm::Kyber768X25519 => Ok(ecdh::Algorithm::X25519),
51            _ => Err("provided algorithm is not an ECDH algorithm"),
52        }
53    }
54}
55
56impl From<ecdh::Error> for Error {
57    fn from(value: ecdh::Error) -> Self {
58        Error::EcDhError(value)
59    }
60}
61
62/// A KEM private key.
63pub enum PrivateKey {
64    X25519(x25519::PrivateKey),
65    P256(p256::PrivateKey),
66}
67
68/// A KEM public key.
69pub enum PublicKey {
70    X25519(x25519::PublicKey),
71    P256(p256::PublicKey),
72}
73
74/// A KEM ciphertext
75pub enum Ct {
76    X25519(x25519::PublicKey),
77    P256(p256::PublicKey),
78}
79
80/// A KEM shared secret
81pub enum Ss {
82    X25519(x25519::PublicKey),
83    P256(p256::PublicKey),
84}
85
86impl PrivateKey {
87    /// Encode a private key.
88    pub fn encode(&self) -> Vec<u8> {
89        match self {
90            PrivateKey::X25519(k) => k.0.to_vec(),
91            PrivateKey::P256(k) => k.0.to_vec(),
92        }
93    }
94
95    /// Decode a private key.
96    pub fn decode(alg: Algorithm, bytes: &[u8]) -> Result<Self, Error> {
97        match alg {
98            Algorithm::X25519 => bytes
99                .try_into()
100                .map_err(|_| Error::InvalidPrivateKey)
101                .map(|k| Self::X25519(k)),
102            Algorithm::Secp256r1 => bytes
103                .try_into()
104                .map_err(|_| Error::InvalidPrivateKey)
105                .map(|k| Self::P256(k)),
106            _ => Err(Error::UnsupportedAlgorithm),
107        }
108    }
109}
110
111impl PublicKey {
112    /// Encode public key.
113    pub fn encode(&self) -> Vec<u8> {
114        match self {
115            PublicKey::X25519(k) => k.0.to_vec(),
116            PublicKey::P256(k) => k.0.to_vec(),
117        }
118    }
119
120    /// Decode a public key.
121    pub fn decode(alg: Algorithm, bytes: &[u8]) -> Result<Self, Error> {
122        match alg {
123            Algorithm::X25519 => bytes
124                .try_into()
125                .map_err(|_| Error::InvalidPublicKey)
126                .map(|k| Self::X25519(k)),
127            Algorithm::Secp256r1 => bytes
128                .try_into()
129                .map_err(|_| Error::InvalidPublicKey)
130                .map(|k| Self::P256(k)),
131            _ => Err(Error::UnsupportedAlgorithm),
132        }
133    }
134}
135
136impl Ss {
137    /// Encode a shared secret.
138    pub fn encode(&self) -> Vec<u8> {
139        match self {
140            Ss::X25519(k) => k.0.to_vec(),
141            Ss::P256(k) => k.0.to_vec(),
142        }
143    }
144}
145
146impl Ct {
147    /// Encode a ciphertext.
148    pub fn encode(&self) -> Vec<u8> {
149        match self {
150            Ct::X25519(k) => k.0.to_vec(),
151            Ct::P256(k) => k.0.to_vec(),
152        }
153    }
154
155    /// Decode a ciphertext.
156    pub fn decode(alg: Algorithm, bytes: &[u8]) -> Result<Self, Error> {
157        match alg {
158            Algorithm::X25519 => bytes
159                .try_into()
160                .map_err(|_| Error::InvalidCiphertext)
161                .map(|ct| Self::X25519(ct)),
162            Algorithm::Secp256r1 => bytes
163                .try_into()
164                .map_err(|_| Error::InvalidCiphertext)
165                .map(|ct| Self::P256(ct)),
166            _ => Err(Error::UnsupportedAlgorithm),
167        }
168    }
169}
170
171/// Compute the public key for a private key of the given [`Algorithm`].
172/// Applicable only to X25519 and secp256r1.
173pub fn secret_to_public(alg: Algorithm, sk: impl AsRef<[u8]>) -> Result<Vec<u8>, Error> {
174    match alg {
175        Algorithm::X25519 | Algorithm::Secp256r1 => {
176            ecdh::secret_to_public(alg.try_into().unwrap(), sk.as_ref()).map_err(|e| e.into())
177        }
178        _ => Err(Error::UnsupportedAlgorithm),
179    }
180}
181
182/// Generate a key pair for the [`Algorithm`] using the provided rng.
183///
184/// The function returns a fresh key or a [`Error::KeyGen`] error if
185/// * not enough entropy was available
186/// * it was not possible to generate a valid key within a reasonable amount of iterations.
187pub fn key_gen(
188    alg: Algorithm,
189    rng: &mut (impl CryptoRng + Rng),
190) -> Result<(PrivateKey, PublicKey), Error> {
191    match alg {
192        Algorithm::X25519 => ecdh::x25519_key_gen(rng)
193            .map_err(|e| e.into())
194            .map(|(private, public)| (PrivateKey::X25519(private), PublicKey::X25519(public))),
195        Algorithm::Secp256r1 => ecdh::p256_key_gen(rng)
196            .map_err(|e| e.into())
197            .map(|(private, public)| (PrivateKey::P256(private), PublicKey::P256(public))),
198        _ => Err(Error::UnsupportedAlgorithm),
199    }
200}
201
202/// Encapsulate a shared secret to the provided `pk` and return the `(Key, Enc)` tuple.
203pub fn encapsulate(pk: &PublicKey, rng: &mut (impl CryptoRng + Rng)) -> Result<(Ss, Ct), Error> {
204    match pk {
205        PublicKey::X25519(pk) => {
206            let (new_sk, new_pk) = ecdh::x25519_key_gen(rng)?;
207            let gxy = x25519::derive(pk, &new_sk)?;
208            Ok((Ss::X25519(gxy), Ct::X25519(new_pk)))
209        }
210        PublicKey::P256(pk) => {
211            let (new_sk, new_pk) = ecdh::p256_key_gen(rng)?;
212            let gxy = p256_derive(pk, &new_sk)?;
213            Ok((Ss::P256(gxy), Ct::P256(new_pk)))
214        }
215    }
216}
217
218/// Decapsulate the shared secret in `ct` using the private key `sk`.
219pub fn decapsulate(ct: &Ct, sk: &PrivateKey) -> Result<Ss, Error> {
220    match ct {
221        Ct::X25519(ct) => {
222            let sk = if let PrivateKey::X25519(k) = sk {
223                k
224            } else {
225                return Err(Error::InvalidPrivateKey);
226            };
227            x25519::derive(ct, sk)
228                .map_err(|e| e.into())
229                .map(|k| Ss::X25519(k))
230        }
231        Ct::P256(ct) => {
232            let sk = if let PrivateKey::P256(k) = sk {
233                k
234            } else {
235                return Err(Error::InvalidPrivateKey);
236            };
237            p256_derive(ct, sk)
238                .map_err(|e| e.into())
239                .map(|k| Ss::P256(k))
240        }
241    }
242}