Skip to main content

dryoc/classic/
crypto_kem.rs

1//! # Key encapsulation
2//!
3//! Implements libsodium's `crypto_kem_*` functions, which use X-Wing: the
4//! hybrid of ML-KEM-768 and X25519 in [`crate::classic::crypto_kem_xwing`].
5//! X-Wing stays secure if either component does, so it protects against
6//! quantum computers without giving up the security of elliptic-curve
7//! cryptography.
8//!
9//! A key encapsulation mechanism (KEM) lets a sender create a fresh shared
10//! secret for the holder of a public key. [`crypto_kem_enc`] returns the
11//! shared secret and a ciphertext; the recipient recovers the same secret
12//! with [`crypto_kem_dec`] and its secret key. Only the recipient needs a key
13//! pair. Feed the shared secret to a key-derivation function such as
14//! [`crate::classic::crypto_kdf`]'s HKDF before using it as an encryption
15//! key. A KEM does not authenticate the sender; combine it with signatures
16//! or an authenticated key exchange when that matters.
17//!
18//! ```
19//! use dryoc::classic::crypto_kem::*;
20//!
21//! let (public_key, secret_key) = crypto_kem_keypair();
22//!
23//! let mut ciphertext = [0u8; dryoc::constants::CRYPTO_KEM_CIPHERTEXTBYTES];
24//! let mut sender_secret = SharedSecret::default();
25//! crypto_kem_enc(&mut ciphertext, &mut sender_secret, &public_key).expect("encapsulation failed");
26//!
27//! let mut recipient_secret = SharedSecret::default();
28//! crypto_kem_dec(&mut recipient_secret, &ciphertext, &secret_key).expect("decapsulation failed");
29//! assert_eq!(sender_secret, recipient_secret);
30//! ```
31
32pub use crate::classic::crypto_kem_xwing::{Ciphertext, PublicKey, SecretKey, Seed, SharedSecret};
33use crate::classic::crypto_kem_xwing::{
34    crypto_kem_xwing_dec, crypto_kem_xwing_enc, crypto_kem_xwing_keypair,
35    crypto_kem_xwing_keypair_inplace, crypto_kem_xwing_seed_keypair,
36    crypto_kem_xwing_seed_keypair_inplace,
37};
38use crate::error::Error;
39
40/// In-place variant of [`crypto_kem_seed_keypair`].
41pub fn crypto_kem_seed_keypair_inplace(
42    public_key: &mut PublicKey,
43    secret_key: &mut SecretKey,
44    seed: &Seed,
45) {
46    crypto_kem_xwing_seed_keypair_inplace(public_key, secret_key, seed)
47}
48
49/// Deterministically derives a key pair from `seed`.
50///
51/// Compatible with libsodium's `crypto_kem_seed_keypair`.
52#[must_use]
53pub fn crypto_kem_seed_keypair(seed: &Seed) -> (PublicKey, SecretKey) {
54    crypto_kem_xwing_seed_keypair(seed)
55}
56
57/// In-place variant of [`crypto_kem_keypair`].
58pub fn crypto_kem_keypair_inplace(public_key: &mut PublicKey, secret_key: &mut SecretKey) {
59    crypto_kem_xwing_keypair_inplace(public_key, secret_key)
60}
61
62/// Returns a randomly generated key pair.
63///
64/// Compatible with libsodium's `crypto_kem_keypair`.
65#[must_use]
66pub fn crypto_kem_keypair() -> (PublicKey, SecretKey) {
67    crypto_kem_xwing_keypair()
68}
69
70/// Creates a random shared secret for `public_key`, writing it to
71/// `shared_secret` and its encapsulation to `ciphertext`.
72///
73/// Compatible with libsodium's `crypto_kem_enc`.
74///
75/// # Errors
76///
77/// Returns [`Error::InvalidKey`] if `public_key` is not a valid X-Wing
78/// public key; see [`crypto_kem_xwing_enc`].
79pub fn crypto_kem_enc(
80    ciphertext: &mut Ciphertext,
81    shared_secret: &mut SharedSecret,
82    public_key: &PublicKey,
83) -> Result<(), Error> {
84    crypto_kem_xwing_enc(ciphertext, shared_secret, public_key)
85}
86
87/// Recovers the shared secret encapsulated in `ciphertext` with
88/// `secret_key`, writing it to `shared_secret`.
89///
90/// Compatible with libsodium's `crypto_kem_dec`.
91///
92/// # Errors
93///
94/// Returns [`Error::InvalidKey`] if `ciphertext` carries a low-order X25519
95/// point; see [`crypto_kem_xwing_dec`].
96pub fn crypto_kem_dec(
97    shared_secret: &mut SharedSecret,
98    ciphertext: &Ciphertext,
99    secret_key: &SecretKey,
100) -> Result<(), Error> {
101    crypto_kem_xwing_dec(shared_secret, ciphertext, secret_key)
102}
103
104/// Cross-checks against libsodium 1.0.22's generic `crypto_kem_*`.
105#[cfg(all(test, dryoc_native_tests))]
106mod native_tests {
107    use super::*;
108    use crate::classic::crypto_kem_mlkem768::native_tests::seeds;
109    use crate::constants::{CRYPTO_KEM_CIPHERTEXTBYTES, CRYPTO_KEM_SHAREDSECRETBYTES};
110    use crate::native_test_util as sodium;
111
112    /// The generic functions derive X-Wing key pairs in both libraries, and
113    /// encapsulations made by either decapsulate in the other through both
114    /// the generic and the X-Wing functions.
115    #[test]
116    fn test_generic_kem_is_xwing_like_libsodium() {
117        for seed in seeds::<32>() {
118            let keypair = crypto_kem_seed_keypair(&seed);
119            assert_eq!(
120                keypair,
121                sodium::crypto_kem_seed_keypair(&seed),
122                "seed {seed:02x?}"
123            );
124            assert_eq!(
125                keypair,
126                sodium::crypto_kem_xwing_seed_keypair(&seed),
127                "seed {seed:02x?}"
128            );
129            let (public_key, secret_key) = keypair;
130
131            let (so_ciphertext, so_sent) =
132                sodium::crypto_kem_enc(&public_key).expect("libsodium enc");
133            let mut received = [0u8; CRYPTO_KEM_SHAREDSECRETBYTES];
134            crypto_kem_dec(&mut received, &so_ciphertext, &secret_key).expect("dec");
135            assert_eq!(received, so_sent);
136            assert_eq!(
137                sodium::crypto_kem_xwing_dec(&so_ciphertext, &secret_key).expect("libsodium dec"),
138                so_sent
139            );
140
141            let mut ciphertext = [0u8; CRYPTO_KEM_CIPHERTEXTBYTES];
142            let mut sent = [0u8; CRYPTO_KEM_SHAREDSECRETBYTES];
143            crypto_kem_enc(&mut ciphertext, &mut sent, &public_key).expect("enc");
144            assert_eq!(
145                sodium::crypto_kem_dec(&ciphertext, &secret_key).expect("libsodium dec"),
146                sent
147            );
148            assert_eq!(
149                sodium::crypto_kem_xwing_dec(&ciphertext, &secret_key).expect("libsodium dec"),
150                sent
151            );
152        }
153    }
154}