Skip to main content

lib_q_core/
traits.rs

1//! Common traits for lib-Q cryptographic operations
2
3#[cfg(feature = "alloc")]
4use zeroize::{
5    Zeroize,
6    ZeroizeOnDrop,
7};
8
9use crate::error::Result;
10
11#[cfg(feature = "alloc")]
12extern crate alloc;
13#[cfg(feature = "alloc")]
14use alloc::vec::Vec;
15
16#[cfg(feature = "wasm")]
17use js_sys::Uint8Array;
18#[cfg(feature = "wasm")]
19use wasm_bindgen::prelude::*;
20
21/// Trait for key encapsulation mechanisms
22pub trait Kem {
23    /// Generate a keypair
24    fn generate_keypair(&self) -> Result<KemKeypair>;
25
26    /// Encapsulate a shared secret
27    #[cfg(feature = "alloc")]
28    fn encapsulate(&self, public_key: &KemPublicKey) -> Result<(Vec<u8>, Vec<u8>)>;
29    #[cfg(not(feature = "alloc"))]
30    fn encapsulate(&self, public_key: &KemPublicKey) -> Result<(&'static [u8], &'static [u8])>;
31
32    /// Decapsulate a shared secret
33    #[cfg(feature = "alloc")]
34    fn decapsulate(&self, secret_key: &KemSecretKey, ciphertext: &[u8]) -> Result<Vec<u8>>;
35    #[cfg(not(feature = "alloc"))]
36    fn decapsulate(&self, secret_key: &KemSecretKey, ciphertext: &[u8]) -> Result<&'static [u8]>;
37
38    /// Derive public key from secret key
39    #[cfg(feature = "alloc")]
40    fn derive_public_key(&self, secret_key: &KemSecretKey) -> Result<KemPublicKey>;
41    #[cfg(not(feature = "alloc"))]
42    fn derive_public_key(&self, secret_key: &KemSecretKey) -> Result<KemPublicKey>;
43
44    /// Authenticated encapsulation (RFC 9180 AuthEncap)
45    #[cfg(feature = "alloc")]
46    fn auth_encapsulate(
47        &self,
48        sender_sk: &KemSecretKey,
49        recipient_pk: &KemPublicKey,
50    ) -> Result<(Vec<u8>, Vec<u8>)>;
51    #[cfg(not(feature = "alloc"))]
52    fn auth_encapsulate(
53        &self,
54        sender_sk: &KemSecretKey,
55        recipient_pk: &KemPublicKey,
56    ) -> Result<(&'static [u8], &'static [u8])>;
57
58    /// Authenticated decapsulation (RFC 9180 AuthDecap)
59    #[cfg(feature = "alloc")]
60    fn auth_decapsulate(
61        &self,
62        recipient_sk: &KemSecretKey,
63        ciphertext: &[u8],
64        sender_pk: &KemPublicKey,
65    ) -> Result<Vec<u8>>;
66    #[cfg(not(feature = "alloc"))]
67    fn auth_decapsulate(
68        &self,
69        recipient_sk: &KemSecretKey,
70        ciphertext: &[u8],
71        sender_pk: &KemPublicKey,
72    ) -> Result<&'static [u8]>;
73}
74
75/// Trait for digital signatures
76pub trait Signature {
77    /// Generate a keypair
78    fn generate_keypair(&self) -> Result<SigKeypair>;
79
80    /// Sign a message
81    #[cfg(feature = "alloc")]
82    fn sign(&self, secret_key: &SigSecretKey, message: &[u8]) -> Result<Vec<u8>>;
83    #[cfg(not(feature = "alloc"))]
84    fn sign(&self, secret_key: &SigSecretKey, message: &[u8]) -> Result<&'static [u8]>;
85
86    /// Verify a signature
87    fn verify(&self, public_key: &SigPublicKey, message: &[u8], signature: &[u8]) -> Result<bool>;
88}
89
90/// Trait for hash functions
91pub trait Hash {
92    /// Hash data
93    #[cfg(feature = "alloc")]
94    fn hash(&self, data: &[u8]) -> Result<Vec<u8>>;
95    #[cfg(not(feature = "alloc"))]
96    fn hash(&self, data: &[u8]) -> Result<&'static [u8]>;
97
98    /// Get the output size in bytes
99    fn output_size(&self) -> usize;
100}
101
102/// Trait for authenticated encryption with associated data (AEAD).
103///
104/// # Verification timing and the `Result` API
105///
106/// Implementations **should** complete symmetric decryption (or an equivalent fixed
107/// schedule) before branching on authentication success, so bulk **cryptographic** cost does
108/// not depend on whether the tag or equivalent check passes. Tag and MAC comparisons
109/// **must** use constant-time equality on secret material (for example
110/// [`Utils::constant_time_compare`](crate::Utils::constant_time_compare)).
111///
112/// A normal Rust [`Result`] still maps verification to [`Result::Ok`] versus
113/// [`Result::Err`]: that discriminant is visible to control flow and wall-clock timing at
114/// this API boundary. Callers that must hide verification outcome from remote observers
115/// need a higher layer (fixed-latency envelope, scheduling isolation, or a non-`Result`
116/// cryptographic API designed for that threat model). When the `alloc` feature is enabled,
117/// see also `crate::security::timing` for related utilities, and [`crate::AeadDecryptSemantic`] /
118/// [`DecryptSemanticOutcome`](crate::DecryptSemanticOutcome) for **Layer B** (semantic
119/// outcome without plaintext on `AuthenticationFailed`; see `docs/adr/003-aead-decrypt-layers.md`).
120pub trait Aead {
121    /// Encrypt data
122    #[cfg(feature = "alloc")]
123    fn encrypt(
124        &self,
125        key: &AeadKey,
126        nonce: &Nonce,
127        plaintext: &[u8],
128        associated_data: Option<&[u8]>,
129    ) -> Result<Vec<u8>>;
130    #[cfg(not(feature = "alloc"))]
131    fn encrypt(
132        &self,
133        key: &AeadKey,
134        nonce: &Nonce,
135        plaintext: &[u8],
136        associated_data: Option<&[u8]>,
137    ) -> Result<&'static [u8]>;
138
139    /// Decrypt data
140    #[cfg(feature = "alloc")]
141    fn decrypt(
142        &self,
143        key: &AeadKey,
144        nonce: &Nonce,
145        ciphertext: &[u8],
146        associated_data: Option<&[u8]>,
147    ) -> Result<Vec<u8>>;
148    #[cfg(not(feature = "alloc"))]
149    fn decrypt(
150        &self,
151        key: &AeadKey,
152        nonce: &Nonce,
153        ciphertext: &[u8],
154        associated_data: Option<&[u8]>,
155    ) -> Result<&'static [u8]>;
156}
157
158// Key types
159/// KEM keypair.
160///
161/// # Zeroization
162///
163/// The contained secret key zeroizes its buffer on drop (see [`KemSecretKey`]);
164/// the public key is not wiped.
165#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
166#[cfg_attr(feature = "wasm", wasm_bindgen)]
167pub struct KemKeypair {
168    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
169    pub public_key: KemPublicKey,
170    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
171    pub secret_key: KemSecretKey,
172}
173
174/// KEM public key
175#[derive(Clone, Debug, PartialEq, Eq)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177#[cfg_attr(feature = "wasm", wasm_bindgen)]
178pub struct KemPublicKey {
179    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
180    #[cfg(feature = "alloc")]
181    pub data: Vec<u8>,
182    #[cfg(not(feature = "alloc"))]
183    pub data: &'static [u8],
184}
185
186/// KEM secret key.
187///
188/// # Zeroization
189///
190/// With the `alloc` feature, the key bytes in `data` are zeroized when the value
191/// is dropped (`#[derive(ZeroizeOnDrop)]`), and can be wiped earlier by calling
192/// [`Zeroize::zeroize`]. This is best-effort and covers **only the current
193/// allocation**:
194///
195/// - If the `Vec` has ever reallocated (growth, `shrink_to_fit`), earlier copies
196///   of its contents may remain elsewhere on the heap; the wipe cannot reach
197///   them. Construct keys with their final contents and do not grow `data`.
198/// - `data` is a public field: moving the buffer out (for example with
199///   `core::mem::take(&mut key.data)`) transfers ownership of the secret bytes
200///   — and responsibility for wiping them — to the caller.
201/// - Copies made through accessors (`as_bytes`, serialization, the wasm
202///   `bytes()` method) are outside the scope of this wipe.
203/// - Without the `alloc` feature, `data` is a borrowed `&'static [u8]` and is
204///   deliberately **not** zeroized: the borrow is not this type's to wipe.
205/// - On wasm32 (feature `wasm`), the value is dropped — and therefore wiped —
206///   when JavaScript calls `.free()` on the handle or its `FinalizationRegistry`
207///   entry runs; if neither happens, the wipe never runs.
208#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
209#[cfg_attr(feature = "alloc", derive(ZeroizeOnDrop))]
210#[cfg_attr(feature = "wasm", wasm_bindgen)]
211pub struct KemSecretKey {
212    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
213    #[cfg(feature = "alloc")]
214    pub data: Vec<u8>,
215    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
216    #[cfg(not(feature = "alloc"))]
217    pub data: &'static [u8],
218}
219
220#[cfg(feature = "alloc")]
221impl Zeroize for KemSecretKey {
222    fn zeroize(&mut self) {
223        self.data.zeroize();
224    }
225}
226
227/// Signature keypair.
228///
229/// # Zeroization
230///
231/// The contained secret key zeroizes its buffer on drop (see [`SigSecretKey`]);
232/// the public key is not wiped.
233#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
234#[cfg_attr(feature = "wasm", wasm_bindgen)]
235pub struct SigKeypair {
236    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
237    pub public_key: SigPublicKey,
238    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
239    pub secret_key: SigSecretKey,
240}
241
242/// Signature public key
243#[derive(Clone, Debug, PartialEq, Eq)]
244#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
245#[cfg_attr(feature = "wasm", wasm_bindgen)]
246pub struct SigPublicKey {
247    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
248    #[cfg(feature = "alloc")]
249    pub data: Vec<u8>,
250    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
251    #[cfg(not(feature = "alloc"))]
252    pub data: &'static [u8],
253}
254
255/// Signature secret key.
256///
257/// # Zeroization
258///
259/// With the `alloc` feature, the key bytes in `data` are zeroized when the value
260/// is dropped (`#[derive(ZeroizeOnDrop)]`), and can be wiped earlier by calling
261/// [`Zeroize::zeroize`]. This is best-effort and covers **only the current
262/// allocation**:
263///
264/// - If the `Vec` has ever reallocated (growth, `shrink_to_fit`), earlier copies
265///   of its contents may remain elsewhere on the heap; the wipe cannot reach
266///   them. Construct keys with their final contents and do not grow `data`.
267/// - `data` is a public field: moving the buffer out (for example with
268///   `core::mem::take(&mut key.data)`) transfers ownership of the secret bytes
269///   — and responsibility for wiping them — to the caller.
270/// - Copies made through accessors (`as_bytes`, serialization, the wasm
271///   `bytes()` method) are outside the scope of this wipe.
272/// - Without the `alloc` feature, `data` is a borrowed `&'static [u8]` and is
273///   deliberately **not** zeroized: the borrow is not this type's to wipe.
274/// - On wasm32 (feature `wasm`), the value is dropped — and therefore wiped —
275///   when JavaScript calls `.free()` on the handle or its `FinalizationRegistry`
276///   entry runs; if neither happens, the wipe never runs.
277#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
278#[cfg_attr(feature = "alloc", derive(ZeroizeOnDrop))]
279#[cfg_attr(feature = "wasm", wasm_bindgen)]
280pub struct SigSecretKey {
281    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
282    #[cfg(feature = "alloc")]
283    pub data: Vec<u8>,
284    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
285    #[cfg(not(feature = "alloc"))]
286    pub data: &'static [u8],
287}
288
289#[cfg(feature = "alloc")]
290impl Zeroize for SigSecretKey {
291    fn zeroize(&mut self) {
292        self.data.zeroize();
293    }
294}
295
296/// AEAD key.
297///
298/// # Zeroization
299///
300/// With the `alloc` feature, the key bytes in `data` are zeroized when the value
301/// is dropped (`#[derive(ZeroizeOnDrop)]`), and can be wiped earlier by calling
302/// [`Zeroize::zeroize`]. This is best-effort and covers **only the current
303/// allocation**:
304///
305/// - If the `Vec` has ever reallocated (growth, `shrink_to_fit`), earlier copies
306///   of its contents may remain elsewhere on the heap; the wipe cannot reach
307///   them. Construct keys with their final contents and do not grow `data`.
308/// - `data` is a public field: moving the buffer out (for example with
309///   `core::mem::take(&mut key.data)`) transfers ownership of the secret bytes
310///   — and responsibility for wiping them — to the caller.
311/// - Copies made through accessors (for example `as_bytes`) are outside the
312///   scope of this wipe.
313/// - Without the `alloc` feature, `data` is a borrowed `&'static [u8]` and is
314///   deliberately **not** zeroized: the borrow is not this type's to wipe.
315#[cfg_attr(feature = "alloc", derive(ZeroizeOnDrop))]
316pub struct AeadKey {
317    #[cfg(feature = "alloc")]
318    pub data: Vec<u8>,
319    #[cfg(not(feature = "alloc"))]
320    pub data: &'static [u8],
321}
322
323#[cfg(feature = "alloc")]
324impl Zeroize for AeadKey {
325    fn zeroize(&mut self) {
326        self.data.zeroize();
327    }
328}
329
330/// Nonce for AEAD operations
331#[derive(Clone, Debug, PartialEq, Eq)]
332pub struct Nonce {
333    #[cfg(feature = "alloc")]
334    pub data: Vec<u8>,
335    #[cfg(not(feature = "alloc"))]
336    pub data: &'static [u8],
337}
338
339// Implementations for key types
340impl KemKeypair {
341    #[cfg(feature = "alloc")]
342    pub fn new(public_key: Vec<u8>, secret_key: Vec<u8>) -> Self {
343        Self {
344            public_key: KemPublicKey { data: public_key },
345            secret_key: KemSecretKey { data: secret_key },
346        }
347    }
348
349    #[cfg(not(feature = "alloc"))]
350    pub fn new(public_key: &'static [u8], secret_key: &'static [u8]) -> Self {
351        Self {
352            public_key: KemPublicKey { data: public_key },
353            secret_key: KemSecretKey { data: secret_key },
354        }
355    }
356
357    pub fn public_key(&self) -> &KemPublicKey {
358        &self.public_key
359    }
360
361    pub fn secret_key(&self) -> &KemSecretKey {
362        &self.secret_key
363    }
364}
365
366#[cfg(feature = "wasm")]
367#[wasm_bindgen]
368impl KemKeypair {
369    /// Create a new KEM keypair from bytes for WASM
370    #[wasm_bindgen(constructor)]
371    pub fn new_wasm(public_key: Vec<u8>, secret_key: Vec<u8>) -> KemKeypair {
372        Self::new(public_key, secret_key)
373    }
374
375    /// Get the public key as bytes for WASM
376    pub fn public_key_bytes(&self) -> Vec<u8> {
377        self.public_key.data.to_vec()
378    }
379
380    /// Copy the secret key into a new `Uint8Array` for WASM (avoids returning an owned non-zeroizing `Vec<u8>`).
381    pub fn secret_key_bytes(&self) -> Uint8Array {
382        let n =
383            u32::try_from(self.secret_key.data.len()).expect("KEM secret key length fits in u32");
384        let out = Uint8Array::new_with_length(n);
385        out.copy_from(&self.secret_key.data);
386        out
387    }
388}
389
390impl SigKeypair {
391    #[cfg(feature = "alloc")]
392    pub fn new(public_key: Vec<u8>, secret_key: Vec<u8>) -> Self {
393        Self {
394            public_key: SigPublicKey { data: public_key },
395            secret_key: SigSecretKey { data: secret_key },
396        }
397    }
398
399    #[cfg(not(feature = "alloc"))]
400    pub fn new(public_key: &'static [u8], secret_key: &'static [u8]) -> Self {
401        Self {
402            public_key: SigPublicKey { data: public_key },
403            secret_key: SigSecretKey { data: secret_key },
404        }
405    }
406
407    pub fn public_key(&self) -> &SigPublicKey {
408        &self.public_key
409    }
410
411    pub fn secret_key(&self) -> &SigSecretKey {
412        &self.secret_key
413    }
414}
415
416impl KemPublicKey {
417    #[cfg(feature = "alloc")]
418    pub fn new(data: Vec<u8>) -> Self {
419        Self { data }
420    }
421
422    #[cfg(not(feature = "alloc"))]
423    pub fn new(data: &'static [u8]) -> Self {
424        Self { data }
425    }
426
427    pub fn as_bytes(&self) -> &[u8] {
428        &self.data
429    }
430}
431
432#[cfg(feature = "wasm")]
433#[wasm_bindgen]
434impl KemPublicKey {
435    /// Create a new KEM public key from bytes for WASM
436    #[wasm_bindgen(constructor)]
437    pub fn new_from_bytes(data: Vec<u8>) -> KemPublicKey {
438        Self::new(data)
439    }
440
441    /// Get the key data as bytes for WASM
442    pub fn bytes(&self) -> Vec<u8> {
443        self.data.to_vec()
444    }
445}
446
447impl KemSecretKey {
448    #[cfg(feature = "alloc")]
449    pub fn new(data: Vec<u8>) -> Self {
450        Self { data }
451    }
452
453    #[cfg(not(feature = "alloc"))]
454    pub fn new(data: &'static [u8]) -> Self {
455        Self { data }
456    }
457
458    pub fn as_bytes(&self) -> &[u8] {
459        &self.data
460    }
461}
462
463#[cfg(feature = "wasm")]
464#[wasm_bindgen]
465impl KemSecretKey {
466    /// Create a new KEM secret key from bytes for WASM
467    #[wasm_bindgen(constructor)]
468    pub fn new_from_bytes(data: Vec<u8>) -> KemSecretKey {
469        Self::new(data)
470    }
471
472    /// Copy the key material into a new `Uint8Array` for WASM (avoids returning an owned non-zeroizing `Vec<u8>`).
473    pub fn bytes(&self) -> Uint8Array {
474        let n = u32::try_from(self.data.len()).expect("KEM secret key length fits in u32");
475        let out = Uint8Array::new_with_length(n);
476        out.copy_from(&self.data);
477        out
478    }
479}
480
481impl SigPublicKey {
482    #[cfg(feature = "alloc")]
483    pub fn new(data: Vec<u8>) -> Self {
484        Self { data }
485    }
486
487    #[cfg(not(feature = "alloc"))]
488    pub fn new(data: &'static [u8]) -> Self {
489        Self { data }
490    }
491
492    pub fn as_bytes(&self) -> &[u8] {
493        &self.data
494    }
495}
496
497impl SigSecretKey {
498    #[cfg(feature = "alloc")]
499    pub fn new(data: Vec<u8>) -> Self {
500        Self { data }
501    }
502
503    #[cfg(not(feature = "alloc"))]
504    pub fn new(data: &'static [u8]) -> Self {
505        Self { data }
506    }
507
508    pub fn as_bytes(&self) -> &[u8] {
509        &self.data
510    }
511}
512
513impl AeadKey {
514    #[cfg(feature = "alloc")]
515    pub fn new(data: Vec<u8>) -> Self {
516        Self { data }
517    }
518
519    #[cfg(not(feature = "alloc"))]
520    pub fn new(data: &'static [u8]) -> Self {
521        Self { data }
522    }
523
524    pub fn as_bytes(&self) -> &[u8] {
525        &self.data
526    }
527}
528
529impl Nonce {
530    #[cfg(feature = "alloc")]
531    pub fn new(data: Vec<u8>) -> Self {
532        Self { data }
533    }
534
535    #[cfg(not(feature = "alloc"))]
536    pub fn new(data: &'static [u8]) -> Self {
537        Self { data }
538    }
539
540    pub fn as_bytes(&self) -> &[u8] {
541        &self.data
542    }
543}