Skip to main content

kopis/
impls.rs

1//! This file implements the external-facing API of our KEM
2
3use crate::{
4    consts::*,
5    kem::{KemPublicKey, KemSecretKey},
6    pke::ciphertext_len,
7};
8
9use rand_core::CryptoRng;
10use zeroize::{Zeroize, ZeroizeOnDrop};
11
12/// A shared secret of a KEM execution. This is just a `[u8; 32]` that zeroes itself from memory
13/// when it goes out of scope.
14#[derive(Zeroize, ZeroizeOnDrop)]
15pub struct SharedSecret([u8; 32]);
16
17impl SharedSecret {
18    /// Returns the shared secret as a slice
19    #[inline]
20    pub fn as_bytes(&self) -> &[u8; 32] {
21        &self.0
22    }
23}
24
25/// Defines convenience types and impls for a given Kopis variant
26macro_rules! variant_impl {
27    (
28        $variant_name:ident,
29        $mod_doc:expr,
30        $pubkey_name:ident,
31        $privkey_name:ident,
32        $ciphertext_name:ident,
33        $ciphertext_len_name:ident,
34        $variant_ell:expr,
35        $variant_mu:expr,
36        $variant_modt_bits:expr
37    ) => {
38        #[doc = $mod_doc]
39        pub mod $variant_name {
40            use super::*;
41
42            /// A secret key for this KEM
43            #[derive(ZeroizeOnDrop)]
44            pub struct $privkey_name(KemSecretKey<$variant_ell>);
45
46            /// A public key for this KEM
47            pub struct $pubkey_name(KemPublicKey<$variant_ell>);
48
49            /// The length of a ciphertext, or "encapsulated key", for this KEM
50            pub const $ciphertext_len_name: usize =
51                ciphertext_len::<$variant_ell, $variant_modt_bits>();
52
53            /// A ciphertext, or "encapsulated key", for this KEM. This is just a bytestring with
54            /// length `
55            #[doc = stringify!($ciphertext_len_name)]
56            /// `.
57            pub type $ciphertext_name = [u8; $ciphertext_len_name];
58
59            impl $privkey_name {
60                /// Generate a fresh secret key
61                pub fn generate(rng: &mut impl CryptoRng) -> Self {
62                    Self(KemSecretKey::generate::<$variant_mu>(rng))
63                }
64
65                /// Returns the seed that produced this secret key
66                pub fn seed(&self) -> &[u8; 32] {
67                    self.0.seed()
68                }
69
70                /// Deserializes a secret key from a 32-byte seed
71                pub fn expand_from_seed(bytes: &[u8; 32]) -> Self {
72                    Self(KemSecretKey::expand_from_seed::<$variant_mu>(bytes))
73                }
74
75                /// Returns the public key corresponding to this secret key
76                pub fn public_key(&self) -> $pubkey_name {
77                    $pubkey_name(self.0.public_key())
78                }
79            }
80
81            impl $pubkey_name {
82                /// The length of the public key when serialized to bytes
83                pub const SERIALIZED_LEN: usize = KemPublicKey::<$variant_ell>::SERIALIZED_LEN;
84
85                /// Serializes this public key into `out_buf`, of length `Self::SERIALIZED_LEN`
86                pub fn serialize(&self, out_buf: &mut [u8; Self::SERIALIZED_LEN]) {
87                    self.0.serialize(out_buf);
88                }
89
90                /// Deserializes a public key from `bytes`, of length `Self::SERIALIZED_LEN`
91                pub fn from_bytes(bytes: &[u8; Self::SERIALIZED_LEN]) -> Self {
92                    Self(KemPublicKey::from_bytes(bytes))
93                }
94            }
95
96            impl $pubkey_name {
97                /// Encapsulates a fresh shared secret
98                pub fn encapsulate(
99                    &self,
100                    rng: &mut impl CryptoRng,
101                ) -> ($ciphertext_name, SharedSecret) {
102                    let mut randomness = [0u8; 32];
103                    rng.fill_bytes(&mut randomness);
104                    let out = self.encapsulate_deterministic(&randomness);
105
106                    randomness.zeroize();
107                    out
108                }
109
110                /// Encapsulates a shared secret using the given 32-byte `randomness`. This is
111                /// deterministic given `randomness`, and is primarily useful for testing and
112                /// known-answer test (KAT) vectors.
113                pub fn encapsulate_deterministic(
114                    &self,
115                    randomness: &[u8; 32],
116                ) -> ($ciphertext_name, SharedSecret) {
117                    let mut ct = [0u8; $ciphertext_len_name];
118                    let ss = crate::kem::encap_deterministic::<
119                        $variant_ell,
120                        $variant_mu,
121                        $variant_modt_bits,
122                    >(randomness, &self.0, &mut ct);
123
124                    (ct, SharedSecret(ss))
125                }
126            }
127
128            impl $privkey_name {
129                /// Decapsulates an encapsulated key and returns the resulting shared secret. If
130                /// the encapsulated key is invalid, then the shared secret will be pseudorandom
131                /// garbage.
132                pub fn decapsulate(&self, encapsulated_key: &$ciphertext_name) -> SharedSecret {
133                    SharedSecret(crate::kem::decap::<
134                        $variant_ell,
135                        $variant_mu,
136                        $variant_modt_bits,
137                    >(&self.0, encapsulated_key))
138                }
139            }
140
141            /// Basic test that keygen, encap, decap, ser, and deser work
142            #[test]
143            fn test_api() {
144                let mut rng = rand::rng();
145                let sk = $privkey_name::generate(&mut rng);
146                let pk = sk.public_key();
147
148                // Serialize and deserialize the keys
149                let sk_seed = sk.seed();
150                let sk = $privkey_name::expand_from_seed(&sk_seed);
151
152                let mut pk_bytes = [0u8; $pubkey_name::SERIALIZED_LEN];
153                pk.serialize(&mut pk_bytes);
154                let pk = $pubkey_name::from_bytes(&pk_bytes);
155
156                let (ct, ss1) = pk.encapsulate(&mut rng);
157                let ct_bytes = ct.as_ref();
158
159                let ct_arr = ct_bytes.try_into().unwrap();
160                let ss2 = sk.decapsulate(&ct_arr);
161
162                assert_eq!(ss1.as_bytes(), ss2.as_bytes());
163            }
164        }
165    };
166}
167
168variant_impl!(
169    kopis512,
170    "Kopis-512 is designed to have security close to that of AES-128",
171    Kopis512PublicKey,
172    Kopis512SecretKey,
173    Kopis512Ciphertext,
174    KOPIS512_CIPHERTEXT_LEN,
175    KOPIS512_L,
176    KOPIS512_MU,
177    KOPIS512_T
178);
179
180variant_impl!(
181    kopis768,
182    "Kopis-768 is designed to have security close to that of AES-192",
183    Kopis768PublicKey,
184    Kopis768SecretKey,
185    Kopis768Ciphertext,
186    KOPIS768_CIPHERTEXT_LEN,
187    KOPIS768_L,
188    KOPIS768_MU,
189    KOPIS768_T
190);
191
192variant_impl!(
193    kopis1024,
194    "Kopis-1024 is designed to have security close to that of AES-256",
195    Kopis1024PublicKey,
196    Kopis1024SecretKey,
197    Kopis1024Ciphertext,
198    KOPIS1024_CIPHERTEXT_LEN,
199    KOPIS1024_L,
200    KOPIS1024_MU,
201    KOPIS1024_T
202);