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 with automatic memory zeroization
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161#[cfg_attr(feature = "wasm", wasm_bindgen)]
162pub struct KemKeypair {
163    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
164    pub public_key: KemPublicKey,
165    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
166    pub secret_key: KemSecretKey,
167}
168
169/// KEM public key
170#[derive(Clone, Debug, PartialEq, Eq)]
171#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
172#[cfg_attr(feature = "wasm", wasm_bindgen)]
173pub struct KemPublicKey {
174    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
175    #[cfg(feature = "alloc")]
176    pub data: Vec<u8>,
177    #[cfg(not(feature = "alloc"))]
178    pub data: &'static [u8],
179}
180
181/// KEM secret key with automatic memory zeroization
182#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
183#[cfg_attr(feature = "wasm", wasm_bindgen)]
184pub struct KemSecretKey {
185    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
186    #[cfg(feature = "alloc")]
187    pub data: Vec<u8>,
188    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
189    #[cfg(not(feature = "alloc"))]
190    pub data: &'static [u8],
191}
192
193#[cfg(feature = "alloc")]
194impl Zeroize for KemSecretKey {
195    fn zeroize(&mut self) {
196        self.data.zeroize();
197    }
198}
199
200#[cfg(feature = "alloc")]
201impl ZeroizeOnDrop for KemSecretKey {}
202
203/// Signature keypair with automatic memory zeroization
204#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
205#[cfg_attr(feature = "wasm", wasm_bindgen)]
206pub struct SigKeypair {
207    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
208    pub public_key: SigPublicKey,
209    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
210    pub secret_key: SigSecretKey,
211}
212
213/// Signature public key
214#[derive(Clone, Debug, PartialEq, Eq)]
215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
216#[cfg_attr(feature = "wasm", wasm_bindgen)]
217pub struct SigPublicKey {
218    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
219    #[cfg(feature = "alloc")]
220    pub data: Vec<u8>,
221    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
222    #[cfg(not(feature = "alloc"))]
223    pub data: &'static [u8],
224}
225
226/// Signature secret key with automatic memory zeroization
227#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
228#[cfg_attr(feature = "wasm", wasm_bindgen)]
229pub struct SigSecretKey {
230    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
231    #[cfg(feature = "alloc")]
232    pub data: Vec<u8>,
233    #[cfg_attr(feature = "wasm", wasm_bindgen(skip))]
234    #[cfg(not(feature = "alloc"))]
235    pub data: &'static [u8],
236}
237
238#[cfg(feature = "alloc")]
239impl Zeroize for SigSecretKey {
240    fn zeroize(&mut self) {
241        self.data.zeroize();
242    }
243}
244
245#[cfg(feature = "alloc")]
246impl ZeroizeOnDrop for SigSecretKey {}
247
248/// AEAD key
249pub struct AeadKey {
250    #[cfg(feature = "alloc")]
251    pub data: Vec<u8>,
252    #[cfg(not(feature = "alloc"))]
253    pub data: &'static [u8],
254}
255
256#[cfg(feature = "alloc")]
257impl Zeroize for AeadKey {
258    fn zeroize(&mut self) {
259        self.data.zeroize();
260    }
261}
262
263#[cfg(feature = "alloc")]
264impl ZeroizeOnDrop for AeadKey {}
265
266/// Nonce for AEAD operations
267#[derive(Clone, Debug, PartialEq, Eq)]
268pub struct Nonce {
269    #[cfg(feature = "alloc")]
270    pub data: Vec<u8>,
271    #[cfg(not(feature = "alloc"))]
272    pub data: &'static [u8],
273}
274
275// Implementations for key types
276impl KemKeypair {
277    #[cfg(feature = "alloc")]
278    pub fn new(public_key: Vec<u8>, secret_key: Vec<u8>) -> Self {
279        Self {
280            public_key: KemPublicKey { data: public_key },
281            secret_key: KemSecretKey { data: secret_key },
282        }
283    }
284
285    #[cfg(not(feature = "alloc"))]
286    pub fn new(public_key: &'static [u8], secret_key: &'static [u8]) -> Self {
287        Self {
288            public_key: KemPublicKey { data: public_key },
289            secret_key: KemSecretKey { data: secret_key },
290        }
291    }
292
293    pub fn public_key(&self) -> &KemPublicKey {
294        &self.public_key
295    }
296
297    pub fn secret_key(&self) -> &KemSecretKey {
298        &self.secret_key
299    }
300}
301
302#[cfg(feature = "wasm")]
303#[wasm_bindgen]
304impl KemKeypair {
305    /// Create a new KEM keypair from bytes for WASM
306    #[wasm_bindgen(constructor)]
307    pub fn new_wasm(public_key: Vec<u8>, secret_key: Vec<u8>) -> KemKeypair {
308        Self::new(public_key, secret_key)
309    }
310
311    /// Get the public key as bytes for WASM
312    pub fn public_key_bytes(&self) -> Vec<u8> {
313        self.public_key.data.to_vec()
314    }
315
316    /// Copy the secret key into a new `Uint8Array` for WASM (avoids returning an owned non-zeroizing `Vec<u8>`).
317    pub fn secret_key_bytes(&self) -> Uint8Array {
318        let n =
319            u32::try_from(self.secret_key.data.len()).expect("KEM secret key length fits in u32");
320        let out = Uint8Array::new_with_length(n);
321        out.copy_from(&self.secret_key.data);
322        out
323    }
324}
325
326impl SigKeypair {
327    #[cfg(feature = "alloc")]
328    pub fn new(public_key: Vec<u8>, secret_key: Vec<u8>) -> Self {
329        Self {
330            public_key: SigPublicKey { data: public_key },
331            secret_key: SigSecretKey { data: secret_key },
332        }
333    }
334
335    #[cfg(not(feature = "alloc"))]
336    pub fn new(public_key: &'static [u8], secret_key: &'static [u8]) -> Self {
337        Self {
338            public_key: SigPublicKey { data: public_key },
339            secret_key: SigSecretKey { data: secret_key },
340        }
341    }
342
343    pub fn public_key(&self) -> &SigPublicKey {
344        &self.public_key
345    }
346
347    pub fn secret_key(&self) -> &SigSecretKey {
348        &self.secret_key
349    }
350}
351
352impl KemPublicKey {
353    #[cfg(feature = "alloc")]
354    pub fn new(data: Vec<u8>) -> Self {
355        Self { data }
356    }
357
358    #[cfg(not(feature = "alloc"))]
359    pub fn new(data: &'static [u8]) -> Self {
360        Self { data }
361    }
362
363    pub fn as_bytes(&self) -> &[u8] {
364        &self.data
365    }
366}
367
368#[cfg(feature = "wasm")]
369#[wasm_bindgen]
370impl KemPublicKey {
371    /// Create a new KEM public key from bytes for WASM
372    #[wasm_bindgen(constructor)]
373    pub fn new_from_bytes(data: Vec<u8>) -> KemPublicKey {
374        Self::new(data)
375    }
376
377    /// Get the key data as bytes for WASM
378    pub fn bytes(&self) -> Vec<u8> {
379        self.data.to_vec()
380    }
381}
382
383impl KemSecretKey {
384    #[cfg(feature = "alloc")]
385    pub fn new(data: Vec<u8>) -> Self {
386        Self { data }
387    }
388
389    #[cfg(not(feature = "alloc"))]
390    pub fn new(data: &'static [u8]) -> Self {
391        Self { data }
392    }
393
394    pub fn as_bytes(&self) -> &[u8] {
395        &self.data
396    }
397}
398
399#[cfg(feature = "wasm")]
400#[wasm_bindgen]
401impl KemSecretKey {
402    /// Create a new KEM secret key from bytes for WASM
403    #[wasm_bindgen(constructor)]
404    pub fn new_from_bytes(data: Vec<u8>) -> KemSecretKey {
405        Self::new(data)
406    }
407
408    /// Copy the key material into a new `Uint8Array` for WASM (avoids returning an owned non-zeroizing `Vec<u8>`).
409    pub fn bytes(&self) -> Uint8Array {
410        let n = u32::try_from(self.data.len()).expect("KEM secret key length fits in u32");
411        let out = Uint8Array::new_with_length(n);
412        out.copy_from(&self.data);
413        out
414    }
415}
416
417impl SigPublicKey {
418    #[cfg(feature = "alloc")]
419    pub fn new(data: Vec<u8>) -> Self {
420        Self { data }
421    }
422
423    #[cfg(not(feature = "alloc"))]
424    pub fn new(data: &'static [u8]) -> Self {
425        Self { data }
426    }
427
428    pub fn as_bytes(&self) -> &[u8] {
429        &self.data
430    }
431}
432
433impl SigSecretKey {
434    #[cfg(feature = "alloc")]
435    pub fn new(data: Vec<u8>) -> Self {
436        Self { data }
437    }
438
439    #[cfg(not(feature = "alloc"))]
440    pub fn new(data: &'static [u8]) -> Self {
441        Self { data }
442    }
443
444    pub fn as_bytes(&self) -> &[u8] {
445        &self.data
446    }
447}
448
449impl AeadKey {
450    #[cfg(feature = "alloc")]
451    pub fn new(data: Vec<u8>) -> Self {
452        Self { data }
453    }
454
455    #[cfg(not(feature = "alloc"))]
456    pub fn new(data: &'static [u8]) -> Self {
457        Self { data }
458    }
459
460    pub fn as_bytes(&self) -> &[u8] {
461        &self.data
462    }
463}
464
465impl Nonce {
466    #[cfg(feature = "alloc")]
467    pub fn new(data: Vec<u8>) -> Self {
468        Self { data }
469    }
470
471    #[cfg(not(feature = "alloc"))]
472    pub fn new(data: &'static [u8]) -> Self {
473        Self { data }
474    }
475
476    pub fn as_bytes(&self) -> &[u8] {
477        &self.data
478    }
479}