Skip to main content

lib_q_hqc/
provider.rs

1//! HQC Provider Implementation for libQ
2//!
3//! This module provides the libQ provider implementation for HQC KEM operations.
4
5#[cfg(feature = "alloc")]
6extern crate alloc;
7#[cfg(feature = "alloc")]
8use alloc::{
9    format,
10    string::String,
11    vec,
12    vec::Vec,
13};
14
15use lib_q_core::{
16    Algorithm,
17    CryptoProvider,
18    Error,
19    KemOperations,
20    Result,
21};
22#[cfg(all(feature = "alloc", feature = "random"))]
23use zeroize::Zeroizing;
24
25use crate::hqc_core::{
26    Hqc1,
27    Hqc3,
28    Hqc5,
29    HqcCore,
30};
31
32/// RNG for KEM encapsulation: OS CSPRNG by default, or a KAT-compatible SHAKE256 PRNG when
33/// `randomness` is supplied (first up to 48 bytes seed the PRNG; remainder zero-padded).
34/// This matches the approach documented in `tests/integration_test.rs` and avoids rare
35/// encapsulation/decapsulation mismatches from non-deterministic encapsulation entropy.
36#[cfg(all(feature = "alloc", feature = "random"))]
37fn kem_encapsulation_rng(randomness: Option<&[u8]>) -> Result<lib_q_random::LibQRng> {
38    use crate::shake256_prng::create_shake256_prng_rng;
39
40    match randomness {
41        Some(seed) => {
42            let mut entropy = [0u8; 48];
43            let n = core::cmp::min(seed.len(), 48);
44            entropy[..n].copy_from_slice(&seed[..n]);
45            Ok(create_shake256_prng_rng(entropy))
46        }
47        None => lib_q_random::LibQRng::new_secure().map_err(|_| Error::InternalError {
48            operation: String::from("RNG initialization"),
49            details: String::from("Failed to initialize secure random number generator"),
50        }),
51    }
52}
53
54/// HQC provider for libQ integration
55#[derive(Debug, Clone, PartialEq)]
56pub struct LibQHqcProvider;
57
58impl LibQHqcProvider {
59    /// Create a new HQC provider
60    pub fn new() -> Result<Self> {
61        Ok(Self)
62    }
63
64    /// Get the provider name
65    pub fn name(&self) -> &'static str {
66        "libQ HQC Provider"
67    }
68
69    /// Get the provider priority
70    pub fn priority(&self) -> u32 {
71        100
72    }
73
74    /// Get the provider capabilities
75    pub fn capabilities(&self) -> Vec<Algorithm> {
76        vec![Algorithm::Hqc128, Algorithm::Hqc192, Algorithm::Hqc256]
77    }
78
79    /// Check if the provider supports a specific algorithm
80    pub fn supports_algorithm(&self, algorithm: Algorithm) -> bool {
81        matches!(
82            algorithm,
83            Algorithm::Hqc128 | Algorithm::Hqc192 | Algorithm::Hqc256
84        )
85    }
86}
87
88#[cfg(all(feature = "alloc", feature = "random"))]
89impl KemOperations for LibQHqcProvider {
90    fn generate_keypair(
91        &self,
92        algorithm: Algorithm,
93        randomness: Option<&[u8]>,
94    ) -> Result<lib_q_core::KemKeypair> {
95        let mut rng = kem_encapsulation_rng(randomness)?;
96
97        match algorithm {
98            Algorithm::Hqc128 => {
99                let (secret_key, public_key) =
100                    Hqc1::generate_keypair(&mut rng).map_err(|e| Error::InternalError {
101                        operation: String::from("HQC-128 key generation"),
102                        details: format!("Failed to generate HQC-128 keypair: {:?}", e),
103                    })?;
104                Ok(lib_q_core::KemKeypair {
105                    public_key: lib_q_core::KemPublicKey::new(Vec::from(public_key.as_bytes())),
106                    secret_key: lib_q_core::KemSecretKey::new(secret_key.as_bytes()),
107                })
108            }
109            Algorithm::Hqc192 => {
110                let (secret_key, public_key) =
111                    Hqc3::generate_keypair(&mut rng).map_err(|e| Error::InternalError {
112                        operation: String::from("HQC-192 key generation"),
113                        details: format!("Failed to generate HQC-192 keypair: {:?}", e),
114                    })?;
115                Ok(lib_q_core::KemKeypair {
116                    public_key: lib_q_core::KemPublicKey::new(Vec::from(public_key.as_bytes())),
117                    secret_key: lib_q_core::KemSecretKey::new(secret_key.as_bytes()),
118                })
119            }
120            Algorithm::Hqc256 => {
121                let (secret_key, public_key) =
122                    Hqc5::generate_keypair(&mut rng).map_err(|e| Error::InternalError {
123                        operation: String::from("HQC-256 key generation"),
124                        details: format!("Failed to generate HQC-256 keypair: {:?}", e),
125                    })?;
126                Ok(lib_q_core::KemKeypair {
127                    public_key: lib_q_core::KemPublicKey::new(Vec::from(public_key.as_bytes())),
128                    secret_key: lib_q_core::KemSecretKey::new(secret_key.as_bytes()),
129                })
130            }
131            _ => Err(Error::InvalidAlgorithm {
132                algorithm: "Unsupported algorithm",
133            }),
134        }
135    }
136
137    fn encapsulate(
138        &self,
139        algorithm: Algorithm,
140        public_key: &lib_q_core::KemPublicKey,
141        randomness: Option<&[u8]>,
142    ) -> Result<(Vec<u8>, Vec<u8>)> {
143        use crate::hqc_core::{
144            Hqc1PublicKey,
145            Hqc3PublicKey,
146            Hqc5PublicKey,
147        };
148        use crate::hqc_kem::HqcKemPublicKey;
149        use crate::hqc_pke::HqcPkePublicKey;
150        #[allow(unused_imports)] // HqcParams trait needed for associated constants access
151        use crate::params::{
152            Hqc1Params,
153            Hqc3Params,
154            Hqc5Params,
155            HqcParams,
156        };
157
158        match algorithm {
159            Algorithm::Hqc128 => {
160                let pke_pk = HqcPkePublicKey::<Hqc1Params>::new(public_key.data.clone());
161                let kem_pk = HqcKemPublicKey::new(pke_pk);
162                let pk = Hqc1PublicKey::new(kem_pk);
163                let mut rng = kem_encapsulation_rng(randomness)?;
164                let (ciphertext, shared_secret) =
165                    Hqc1::encapsulate(&pk, &mut rng).map_err(|e| Error::InternalError {
166                        operation: String::from("HQC-128 encapsulation"),
167                        details: format!("Failed to encapsulate HQC-128: {:?}", e),
168                    })?;
169                Ok((ciphertext.as_bytes(), Vec::from(shared_secret.as_bytes())))
170            }
171            Algorithm::Hqc192 => {
172                let pke_pk = HqcPkePublicKey::<Hqc3Params>::new(public_key.data.clone());
173                let kem_pk = HqcKemPublicKey::new(pke_pk);
174                let pk = Hqc3PublicKey::new(kem_pk);
175                let mut rng = kem_encapsulation_rng(randomness)?;
176                let (ciphertext, shared_secret) =
177                    Hqc3::encapsulate(&pk, &mut rng).map_err(|e| Error::InternalError {
178                        operation: String::from("HQC-192 encapsulation"),
179                        details: format!("Failed to encapsulate HQC-192: {:?}", e),
180                    })?;
181                Ok((ciphertext.as_bytes(), Vec::from(shared_secret.as_bytes())))
182            }
183            Algorithm::Hqc256 => {
184                let pke_pk = HqcPkePublicKey::<Hqc5Params>::new(public_key.data.clone());
185                let kem_pk = HqcKemPublicKey::new(pke_pk);
186                let pk = Hqc5PublicKey::new(kem_pk);
187                let mut rng = kem_encapsulation_rng(randomness)?;
188                let (ciphertext, shared_secret) =
189                    Hqc5::encapsulate(&pk, &mut rng).map_err(|e| Error::InternalError {
190                        operation: String::from("HQC-256 encapsulation"),
191                        details: format!("Failed to encapsulate HQC-256: {:?}", e),
192                    })?;
193                Ok((ciphertext.as_bytes(), Vec::from(shared_secret.as_bytes())))
194            }
195            _ => Err(Error::InvalidAlgorithm {
196                algorithm: "Unsupported algorithm",
197            }),
198        }
199    }
200
201    fn decapsulate(
202        &self,
203        algorithm: Algorithm,
204        secret_key: &lib_q_core::KemSecretKey,
205        ciphertext: &[u8],
206    ) -> Result<Vec<u8>> {
207        use crate::hqc_core::{
208            Hqc1Ciphertext,
209            Hqc1SecretKey,
210            Hqc3Ciphertext,
211            Hqc3SecretKey,
212            Hqc5Ciphertext,
213            Hqc5SecretKey,
214        };
215        use crate::hqc_kem::{
216            HqcKemCiphertext,
217            HqcKemSecretKey,
218            MAX_SECURITY_BYTES,
219        };
220        use crate::hqc_pke::{
221            HqcPkeCiphertext,
222            HqcPkePublicKey,
223            HqcPkeSecretKey,
224        };
225        #[allow(unused_imports)] // HqcParams trait needed for associated constants access
226        use crate::params::{
227            Hqc1Params,
228            Hqc3Params,
229            Hqc5Params,
230            HqcParams,
231        };
232
233        match algorithm {
234            Algorithm::Hqc128 => {
235                // Parse secret key: ek_pke (PUBLIC_KEY_BYTES) + dk_pke (32) + sigma
236                // (PARAM_SECURITY_BYTES = K, i.e. 16/24/32 by level) + seed_kem (48)
237                let sk_bytes = &secret_key.data;
238                if sk_bytes.len() != Hqc1Params::SECRET_KEY_BYTES {
239                    return Err(Error::InvalidKeySize {
240                        expected: Hqc1Params::SECRET_KEY_BYTES,
241                        actual: sk_bytes.len(),
242                    });
243                }
244                let ek_pke_bytes = &sk_bytes[..Hqc1Params::PUBLIC_KEY_BYTES];
245                let dk_pke_start = Hqc1Params::PUBLIC_KEY_BYTES;
246                let dk_pke_bytes = &sk_bytes[dk_pke_start..dk_pke_start + 32];
247                let sigma_start = dk_pke_start + 32;
248                let sigma_len = Hqc1Params::K;
249                let mut sigma = Zeroizing::new([0u8; MAX_SECURITY_BYTES]);
250                sigma[..sigma_len].copy_from_slice(&sk_bytes[sigma_start..sigma_start + sigma_len]);
251                let seed_kem_start = sigma_start + sigma_len;
252                let mut seed_kem = Zeroizing::new([0u8; 48]);
253                seed_kem.copy_from_slice(&sk_bytes[seed_kem_start..seed_kem_start + 48]);
254
255                let ek_pke = HqcPkePublicKey::<Hqc1Params>::new(ek_pke_bytes.to_vec());
256                let mut dk_pke_array = Zeroizing::new([0u8; 32]);
257                dk_pke_array.copy_from_slice(dk_pke_bytes);
258                let dk_pke = HqcPkeSecretKey::new(*dk_pke_array);
259                let kem_sk = HqcKemSecretKey::new(ek_pke, dk_pke, *sigma, *seed_kem);
260                let sk = Hqc1SecretKey::new(kem_sk);
261
262                // Parse ciphertext: c_pke (VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES) + salt (16)
263                let pke_ct_size = Hqc1Params::VEC_N_SIZE_BYTES + Hqc1Params::VEC_N1N2_SIZE_BYTES;
264                if ciphertext.len() != pke_ct_size + 16 {
265                    return Err(Error::InvalidKeySize {
266                        expected: pke_ct_size + 16,
267                        actual: ciphertext.len(),
268                    });
269                }
270                let c_pke_bytes = &ciphertext[..pke_ct_size];
271                let mut salt = Zeroizing::new([0u8; 16]);
272                salt.copy_from_slice(&ciphertext[pke_ct_size..pke_ct_size + 16]);
273                let c_pke = HqcPkeCiphertext::<Hqc1Params>::new(c_pke_bytes.to_vec());
274                let kem_ct = HqcKemCiphertext::new(c_pke, *salt);
275                let ct = Hqc1Ciphertext::new(kem_ct);
276
277                let shared_secret =
278                    Hqc1::decapsulate::<lib_q_random::LibQRng>(&sk, &ct).map_err(|e| {
279                        Error::InternalError {
280                            operation: String::from("HQC-128 decapsulation"),
281                            details: format!("Failed to decapsulate HQC-128: {:?}", e),
282                        }
283                    })?;
284                Ok(Vec::from(shared_secret.as_bytes()))
285            }
286            Algorithm::Hqc192 => {
287                // Parse secret key
288                let sk_bytes = &secret_key.data;
289                if sk_bytes.len() != Hqc3Params::SECRET_KEY_BYTES {
290                    return Err(Error::InvalidKeySize {
291                        expected: Hqc3Params::SECRET_KEY_BYTES,
292                        actual: sk_bytes.len(),
293                    });
294                }
295                let ek_pke_bytes = &sk_bytes[..Hqc3Params::PUBLIC_KEY_BYTES];
296                let dk_pke_start = Hqc3Params::PUBLIC_KEY_BYTES;
297                let dk_pke_bytes = &sk_bytes[dk_pke_start..dk_pke_start + 32];
298                let sigma_start = dk_pke_start + 32;
299                let sigma_len = Hqc3Params::K;
300                let mut sigma = Zeroizing::new([0u8; MAX_SECURITY_BYTES]);
301                sigma[..sigma_len].copy_from_slice(&sk_bytes[sigma_start..sigma_start + sigma_len]);
302                let seed_kem_start = sigma_start + sigma_len;
303                let mut seed_kem = Zeroizing::new([0u8; 48]);
304                seed_kem.copy_from_slice(&sk_bytes[seed_kem_start..seed_kem_start + 48]);
305
306                let ek_pke = HqcPkePublicKey::<Hqc3Params>::new(ek_pke_bytes.to_vec());
307                let mut dk_pke_array = Zeroizing::new([0u8; 32]);
308                dk_pke_array.copy_from_slice(dk_pke_bytes);
309                let dk_pke = HqcPkeSecretKey::new(*dk_pke_array);
310                let kem_sk = HqcKemSecretKey::new(ek_pke, dk_pke, *sigma, *seed_kem);
311                let sk = Hqc3SecretKey::new(kem_sk);
312
313                // Parse ciphertext: c_pke (VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES) + salt (16)
314                let pke_ct_size = Hqc3Params::VEC_N_SIZE_BYTES + Hqc3Params::VEC_N1N2_SIZE_BYTES;
315                if ciphertext.len() != pke_ct_size + 16 {
316                    return Err(Error::InvalidKeySize {
317                        expected: pke_ct_size + 16,
318                        actual: ciphertext.len(),
319                    });
320                }
321                let c_pke_bytes = &ciphertext[..pke_ct_size];
322                let mut salt = Zeroizing::new([0u8; 16]);
323                salt.copy_from_slice(&ciphertext[pke_ct_size..pke_ct_size + 16]);
324                let c_pke = HqcPkeCiphertext::<Hqc3Params>::new(c_pke_bytes.to_vec());
325                let kem_ct = HqcKemCiphertext::new(c_pke, *salt);
326                let ct = Hqc3Ciphertext::new(kem_ct);
327
328                let shared_secret =
329                    Hqc3::decapsulate::<lib_q_random::LibQRng>(&sk, &ct).map_err(|e| {
330                        Error::InternalError {
331                            operation: String::from("HQC-192 decapsulation"),
332                            details: format!("Failed to decapsulate HQC-192: {:?}", e),
333                        }
334                    })?;
335                Ok(Vec::from(shared_secret.as_bytes()))
336            }
337            Algorithm::Hqc256 => {
338                // Parse secret key
339                let sk_bytes = &secret_key.data;
340                if sk_bytes.len() != Hqc5Params::SECRET_KEY_BYTES {
341                    return Err(Error::InvalidKeySize {
342                        expected: Hqc5Params::SECRET_KEY_BYTES,
343                        actual: sk_bytes.len(),
344                    });
345                }
346                let ek_pke_bytes = &sk_bytes[..Hqc5Params::PUBLIC_KEY_BYTES];
347                let dk_pke_start = Hqc5Params::PUBLIC_KEY_BYTES;
348                let dk_pke_bytes = &sk_bytes[dk_pke_start..dk_pke_start + 32];
349                let sigma_start = dk_pke_start + 32;
350                let sigma_len = Hqc5Params::K;
351                let mut sigma = Zeroizing::new([0u8; MAX_SECURITY_BYTES]);
352                sigma[..sigma_len].copy_from_slice(&sk_bytes[sigma_start..sigma_start + sigma_len]);
353                let seed_kem_start = sigma_start + sigma_len;
354                let mut seed_kem = Zeroizing::new([0u8; 48]);
355                seed_kem.copy_from_slice(&sk_bytes[seed_kem_start..seed_kem_start + 48]);
356
357                let ek_pke = HqcPkePublicKey::<Hqc5Params>::new(ek_pke_bytes.to_vec());
358                let mut dk_pke_array = Zeroizing::new([0u8; 32]);
359                dk_pke_array.copy_from_slice(dk_pke_bytes);
360                let dk_pke = HqcPkeSecretKey::new(*dk_pke_array);
361                let kem_sk = HqcKemSecretKey::new(ek_pke, dk_pke, *sigma, *seed_kem);
362                let sk = Hqc5SecretKey::new(kem_sk);
363
364                // Parse ciphertext: c_pke (VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES) + salt (16)
365                let pke_ct_size = Hqc5Params::VEC_N_SIZE_BYTES + Hqc5Params::VEC_N1N2_SIZE_BYTES;
366                if ciphertext.len() != pke_ct_size + 16 {
367                    return Err(Error::InvalidKeySize {
368                        expected: pke_ct_size + 16,
369                        actual: ciphertext.len(),
370                    });
371                }
372                let c_pke_bytes = &ciphertext[..pke_ct_size];
373                let mut salt = Zeroizing::new([0u8; 16]);
374                salt.copy_from_slice(&ciphertext[pke_ct_size..pke_ct_size + 16]);
375                let c_pke = HqcPkeCiphertext::<Hqc5Params>::new(c_pke_bytes.to_vec());
376                let kem_ct = HqcKemCiphertext::new(c_pke, *salt);
377                let ct = Hqc5Ciphertext::new(kem_ct);
378
379                let shared_secret =
380                    Hqc5::decapsulate::<lib_q_random::LibQRng>(&sk, &ct).map_err(|e| {
381                        Error::InternalError {
382                            operation: String::from("HQC-256 decapsulation"),
383                            details: format!("Failed to decapsulate HQC-256: {:?}", e),
384                        }
385                    })?;
386                Ok(Vec::from(shared_secret.as_bytes()))
387            }
388            _ => Err(Error::InvalidAlgorithm {
389                algorithm: "Unsupported algorithm",
390            }),
391        }
392    }
393
394    fn derive_public_key(
395        &self,
396        algorithm: Algorithm,
397        secret_key: &lib_q_core::KemSecretKey,
398    ) -> Result<lib_q_core::KemPublicKey> {
399        #[allow(unused_imports)] // HqcParams trait needed for associated constants access
400        use crate::params::{
401            Hqc1Params,
402            Hqc3Params,
403            Hqc5Params,
404            HqcParams,
405        };
406
407        match algorithm {
408            Algorithm::Hqc128 => {
409                // Validate secret key size
410                let sk_bytes = &secret_key.data;
411                if sk_bytes.len() != Hqc1Params::SECRET_KEY_BYTES {
412                    return Err(Error::InvalidKeySize {
413                        expected: Hqc1Params::SECRET_KEY_BYTES,
414                        actual: sk_bytes.len(),
415                    });
416                }
417
418                // Extract ek_pke (public key) from first PUBLIC_KEY_BYTES of secret key
419                // Secret key structure: ek_pke (PUBLIC_KEY_BYTES) + dk_pke (32) + sigma (16) + seed_kem (48)
420                let ek_pke_bytes = &sk_bytes[..Hqc1Params::PUBLIC_KEY_BYTES];
421
422                Ok(lib_q_core::KemPublicKey::new(Vec::from(ek_pke_bytes)))
423            }
424            Algorithm::Hqc192 => {
425                // Validate secret key size
426                let sk_bytes = &secret_key.data;
427                if sk_bytes.len() != Hqc3Params::SECRET_KEY_BYTES {
428                    return Err(Error::InvalidKeySize {
429                        expected: Hqc3Params::SECRET_KEY_BYTES,
430                        actual: sk_bytes.len(),
431                    });
432                }
433
434                // Extract ek_pke (public key) from first PUBLIC_KEY_BYTES of secret key
435                let ek_pke_bytes = &sk_bytes[..Hqc3Params::PUBLIC_KEY_BYTES];
436
437                Ok(lib_q_core::KemPublicKey::new(Vec::from(ek_pke_bytes)))
438            }
439            Algorithm::Hqc256 => {
440                // Validate secret key size
441                let sk_bytes = &secret_key.data;
442                if sk_bytes.len() != Hqc5Params::SECRET_KEY_BYTES {
443                    return Err(Error::InvalidKeySize {
444                        expected: Hqc5Params::SECRET_KEY_BYTES,
445                        actual: sk_bytes.len(),
446                    });
447                }
448
449                // Extract ek_pke (public key) from first PUBLIC_KEY_BYTES of secret key
450                let ek_pke_bytes = &sk_bytes[..Hqc5Params::PUBLIC_KEY_BYTES];
451
452                Ok(lib_q_core::KemPublicKey::new(Vec::from(ek_pke_bytes)))
453            }
454            _ => Err(Error::InvalidAlgorithm {
455                algorithm: "Unsupported algorithm for HQC derive_public_key",
456            }),
457        }
458    }
459}
460
461impl Default for LibQHqcProvider {
462    fn default() -> Self {
463        Self::new().expect("HQC provider should always be creatable")
464    }
465}
466
467#[cfg(all(feature = "alloc", feature = "random"))]
468impl CryptoProvider for LibQHqcProvider {
469    fn kem(&self) -> Option<&dyn KemOperations> {
470        Some(self)
471    }
472
473    fn signature(&self) -> Option<&dyn lib_q_core::SignatureOperations> {
474        None
475    }
476
477    fn hash(&self) -> Option<&dyn lib_q_core::HashOperations> {
478        None
479    }
480
481    fn aead(&self) -> Option<&dyn lib_q_core::AeadOperations> {
482        None
483    }
484}
485
486#[cfg(test)]
487mod tests {
488    #[cfg(feature = "alloc")]
489    extern crate alloc;
490
491    use lib_q_core::Algorithm;
492
493    use super::*;
494
495    #[test]
496    fn test_derive_public_key_hqc128() {
497        let provider = LibQHqcProvider::new().expect("Failed to create provider");
498
499        // Generate keypair
500        let keypair = provider
501            .generate_keypair(Algorithm::Hqc128, None)
502            .expect("Failed to generate keypair");
503
504        // Derive public key from secret key
505        let derived_pk = provider
506            .derive_public_key(Algorithm::Hqc128, &keypair.secret_key)
507            .expect("Failed to derive public key");
508
509        // Verify derived public key matches original
510        assert_eq!(
511            derived_pk.data, keypair.public_key.data,
512            "Derived public key should match original public key"
513        );
514        assert_eq!(
515            derived_pk.data.len(),
516            keypair.public_key.data.len(),
517            "Derived public key size should match original"
518        );
519    }
520
521    #[test]
522    fn test_derive_public_key_hqc192() {
523        let provider = LibQHqcProvider::new().expect("Failed to create provider");
524
525        // Generate keypair
526        let keypair = provider
527            .generate_keypair(Algorithm::Hqc192, None)
528            .expect("Failed to generate keypair");
529
530        // Derive public key from secret key
531        let derived_pk = provider
532            .derive_public_key(Algorithm::Hqc192, &keypair.secret_key)
533            .expect("Failed to derive public key");
534
535        // Verify derived public key matches original
536        assert_eq!(
537            derived_pk.data, keypair.public_key.data,
538            "Derived public key should match original public key"
539        );
540        assert_eq!(
541            derived_pk.data.len(),
542            keypair.public_key.data.len(),
543            "Derived public key size should match original"
544        );
545    }
546
547    #[test]
548    fn test_derive_public_key_hqc256() {
549        let provider = LibQHqcProvider::new().expect("Failed to create provider");
550
551        // Generate keypair
552        let keypair = provider
553            .generate_keypair(Algorithm::Hqc256, None)
554            .expect("Failed to generate keypair");
555
556        // Derive public key from secret key
557        let derived_pk = provider
558            .derive_public_key(Algorithm::Hqc256, &keypair.secret_key)
559            .expect("Failed to derive public key");
560
561        // Verify derived public key matches original
562        assert_eq!(
563            derived_pk.data, keypair.public_key.data,
564            "Derived public key should match original public key"
565        );
566        assert_eq!(
567            derived_pk.data.len(),
568            keypair.public_key.data.len(),
569            "Derived public key size should match original"
570        );
571    }
572
573    #[test]
574    fn test_derive_public_key_invalid_key_size() {
575        let provider = LibQHqcProvider::new().expect("Failed to create provider");
576
577        // Create a secret key with invalid size
578        let invalid_sk = lib_q_core::KemSecretKey::new(alloc::vec![0u8; 100]);
579
580        // Should return error for invalid key size
581        let result = provider.derive_public_key(Algorithm::Hqc128, &invalid_sk);
582        assert!(result.is_err(), "Should return error for invalid key size");
583        if let Err(Error::InvalidKeySize { .. }) = result {
584            // Expected error type
585        } else {
586            panic!("Expected InvalidKeySize error, got: {:?}", result);
587        }
588    }
589
590    #[test]
591    fn test_derive_public_key_unsupported_algorithm() {
592        let provider = LibQHqcProvider::new().expect("Failed to create provider");
593
594        // Generate a valid HQC-128 keypair
595        let keypair = provider
596            .generate_keypair(Algorithm::Hqc128, None)
597            .expect("Failed to generate keypair");
598
599        // Try to derive with unsupported algorithm
600        let result = provider.derive_public_key(Algorithm::MlKem512, &keypair.secret_key);
601        assert!(
602            result.is_err(),
603            "Should return error for unsupported algorithm"
604        );
605        if let Err(Error::InvalidAlgorithm { .. }) = result {
606            // Expected error type
607        } else {
608            panic!("Expected InvalidAlgorithm error, got: {:?}", result);
609        }
610    }
611
612    #[test]
613    fn test_derive_public_key_round_trip_encapsulation() {
614        let provider = LibQHqcProvider::new().expect("Failed to create provider");
615
616        let keypair = provider
617            .generate_keypair(Algorithm::Hqc128, None)
618            .expect("Failed to generate keypair");
619
620        let derived_pk = provider
621            .derive_public_key(Algorithm::Hqc128, &keypair.secret_key)
622            .expect("Failed to derive public key");
623
624        let mut enc_seed = [0u8; 48];
625        enc_seed[0] = 0xC1;
626        let (ciphertext, shared_secret1) = provider
627            .encapsulate(Algorithm::Hqc128, &derived_pk, Some(&enc_seed))
628            .expect("Failed to encapsulate with derived public key");
629
630        let shared_secret2 = provider
631            .decapsulate(Algorithm::Hqc128, &keypair.secret_key, &ciphertext)
632            .expect("Failed to decapsulate");
633
634        assert_eq!(
635            shared_secret1, shared_secret2,
636            "Shared secrets should match when using derived public key"
637        );
638    }
639
640    #[test]
641    fn test_derive_public_key_multiple_round_trips() {
642        let provider = LibQHqcProvider::new().expect("Failed to create provider");
643
644        let keypair = provider
645            .generate_keypair(Algorithm::Hqc192, None)
646            .expect("Failed to generate keypair");
647
648        let derived_pk = provider
649            .derive_public_key(Algorithm::Hqc192, &keypair.secret_key)
650            .expect("Failed to derive public key");
651
652        for i in 0u8..3 {
653            let mut enc_seed = [0u8; 48];
654            enc_seed[0] = i;
655            enc_seed[1] = 0xE5;
656            let (ciphertext, shared_secret1) = provider
657                .encapsulate(Algorithm::Hqc192, &derived_pk, Some(&enc_seed))
658                .expect("Failed to encapsulate");
659
660            let shared_secret2 = provider
661                .decapsulate(Algorithm::Hqc192, &keypair.secret_key, &ciphertext)
662                .expect("Failed to decapsulate");
663
664            assert_eq!(
665                shared_secret1, shared_secret2,
666                "Shared secrets should match in round-trip test"
667            );
668        }
669    }
670
671    /// Full generate/encapsulate/decapsulate round trip for HQC-256 THROUGH THE PROVIDER (not
672    /// just `hqc_core::Hqc5` directly). Previously only `generate_keypair` +
673    /// `derive_public_key` were exercised for HQC-256 in this file, leaving `encapsulate`'s and
674    /// `decapsulate`'s `Algorithm::Hqc256` match arms (provider.rs's own secret-key/ciphertext
675    /// byte-slicing, not just the underlying `Hqc5` call) untested.
676    #[test]
677    fn test_hqc256_provider_round_trip() {
678        let provider = LibQHqcProvider::new().expect("Failed to create provider");
679
680        let keypair = provider
681            .generate_keypair(Algorithm::Hqc256, None)
682            .expect("Failed to generate HQC-256 keypair");
683
684        let mut enc_seed = [0u8; 48];
685        enc_seed[0] = 0xF5;
686        let (ciphertext, shared_secret1) = provider
687            .encapsulate(Algorithm::Hqc256, &keypair.public_key, Some(&enc_seed))
688            .expect("Failed to encapsulate HQC-256");
689
690        let shared_secret2 = provider
691            .decapsulate(Algorithm::Hqc256, &keypair.secret_key, &ciphertext)
692            .expect("Failed to decapsulate HQC-256");
693
694        assert_eq!(
695            shared_secret1, shared_secret2,
696            "HQC-256 shared secrets must match through the provider round trip"
697        );
698    }
699
700    /// `encapsulate`/`decapsulate` must reject an unsupported algorithm the same way
701    /// `derive_public_key` already does (`test_derive_public_key_unsupported_algorithm`
702    /// covers that one).
703    #[test]
704    fn test_encapsulate_and_decapsulate_reject_unsupported_algorithm() {
705        let provider = LibQHqcProvider::new().expect("Failed to create provider");
706
707        let keypair = provider
708            .generate_keypair(Algorithm::Hqc128, None)
709            .expect("Failed to generate keypair");
710
711        let result = provider.encapsulate(Algorithm::MlKem512, &keypair.public_key, None);
712        assert!(matches!(result, Err(Error::InvalidAlgorithm { .. })));
713
714        let result = provider.decapsulate(Algorithm::MlKem512, &keypair.secret_key, &[0u8; 16]);
715        assert!(matches!(result, Err(Error::InvalidAlgorithm { .. })));
716    }
717
718    /// Negative path: `decapsulate` must reject a too-short secret key with `InvalidKeySize`
719    /// (never panic on an out-of-bounds slice) for every HQC variant.
720    #[test]
721    fn test_decapsulate_rejects_undersized_secret_key_all_variants() {
722        let provider = LibQHqcProvider::new().expect("Failed to create provider");
723
724        for algorithm in [Algorithm::Hqc128, Algorithm::Hqc192, Algorithm::Hqc256] {
725            let too_short_sk = lib_q_core::KemSecretKey::new(alloc::vec![0u8; 4]);
726            let result = provider.decapsulate(algorithm, &too_short_sk, &[0u8; 16]);
727            assert!(
728                matches!(result, Err(Error::InvalidKeySize { .. })),
729                "expected InvalidKeySize for {algorithm:?}, got {result:?}"
730            );
731        }
732    }
733
734    /// Negative path: `decapsulate` must reject a too-short ciphertext with `InvalidKeySize`
735    /// (the size-mismatch variant this code path uses) once the secret key itself is
736    /// well-formed, rather than indexing past the end of the ciphertext slice.
737    #[test]
738    fn test_decapsulate_rejects_undersized_ciphertext_all_variants() {
739        let provider = LibQHqcProvider::new().expect("Failed to create provider");
740
741        for algorithm in [Algorithm::Hqc128, Algorithm::Hqc192, Algorithm::Hqc256] {
742            let keypair = provider
743                .generate_keypair(algorithm, None)
744                .unwrap_or_else(|_| panic!("keygen failed for {algorithm:?}"));
745            let result = provider.decapsulate(algorithm, &keypair.secret_key, &[0u8; 4]);
746            assert!(
747                matches!(result, Err(Error::InvalidKeySize { .. })),
748                "expected InvalidKeySize for {algorithm:?}, got {result:?}"
749            );
750        }
751    }
752
753    /// `LibQHqcProvider::name`/`priority`/`capabilities`/`supports_algorithm` and `Default` are
754    /// simple accessors never called by any other test in this file.
755    #[test]
756    fn test_provider_accessors_and_default() {
757        let provider = LibQHqcProvider::new().expect("Failed to create provider");
758        assert_eq!(provider.name(), "libQ HQC Provider");
759        assert_eq!(provider.priority(), 100);
760        assert_eq!(
761            provider.capabilities(),
762            alloc::vec![Algorithm::Hqc128, Algorithm::Hqc192, Algorithm::Hqc256]
763        );
764        assert!(provider.supports_algorithm(Algorithm::Hqc128));
765        assert!(provider.supports_algorithm(Algorithm::Hqc192));
766        assert!(provider.supports_algorithm(Algorithm::Hqc256));
767        assert!(!provider.supports_algorithm(Algorithm::MlKem512));
768
769        // `Default::default()` rather than `LibQHqcProvider::default()`: clippy's
770        // `default_constructed_unit_structs` sees a fieldless struct and suggests writing the
771        // struct literal directly instead of calling `::default()` -- correct advice for
772        // constructing a value, but wrong for this test's actual purpose, which is to exercise
773        // the hand-written `impl Default for LibQHqcProvider` itself (it calls through
774        // `Self::new().expect(..)`, not a derive, so it CAN fail/panic if that ever changes);
775        // writing the struct literal would skip the impl under test entirely. The generic
776        // `Default::default()` form still calls the same impl without matching the lint's
777        // `Type::default()` call-path pattern.
778        let default_provider: LibQHqcProvider = Default::default();
779        assert_eq!(default_provider, provider);
780    }
781
782    /// `CryptoProvider` trait accessors: `kem()` returns `Some`, the rest `None`.
783    #[test]
784    fn test_provider_crypto_provider_trait_accessors() {
785        let provider = LibQHqcProvider::new().expect("Failed to create provider");
786        assert!(CryptoProvider::kem(&provider).is_some());
787        assert!(CryptoProvider::signature(&provider).is_none());
788        assert!(CryptoProvider::hash(&provider).is_none());
789        assert!(CryptoProvider::aead(&provider).is_none());
790    }
791
792    /// `generate_keypair` must reject an unsupported algorithm the same way `encapsulate`/
793    /// `decapsulate` already do (see `test_encapsulate_and_decapsulate_reject_unsupported_algorithm`).
794    #[test]
795    fn test_generate_keypair_rejects_unsupported_algorithm() {
796        let provider = LibQHqcProvider::new().expect("Failed to create provider");
797        let result = provider.generate_keypair(Algorithm::MlKem512, None);
798        assert!(matches!(result, Err(Error::InvalidAlgorithm { .. })));
799    }
800
801    /// `derive_public_key` must reject an undersized secret key for HQC-192 and HQC-256 too
802    /// (only HQC-128 was covered by `test_derive_public_key_invalid_key_size`).
803    #[test]
804    fn test_derive_public_key_invalid_key_size_hqc192_and_hqc256() {
805        let provider = LibQHqcProvider::new().expect("Failed to create provider");
806        let invalid_sk = lib_q_core::KemSecretKey::new(alloc::vec![0u8; 4]);
807
808        let result = provider.derive_public_key(Algorithm::Hqc192, &invalid_sk);
809        assert!(matches!(result, Err(Error::InvalidKeySize { .. })));
810
811        let result = provider.derive_public_key(Algorithm::Hqc256, &invalid_sk);
812        assert!(matches!(result, Err(Error::InvalidKeySize { .. })));
813    }
814
815    #[test]
816    fn test_derive_public_key_all_algorithms() {
817        let provider = LibQHqcProvider::new().expect("Failed to create provider");
818
819        let algorithms = [Algorithm::Hqc128, Algorithm::Hqc192, Algorithm::Hqc256];
820
821        for algorithm in algorithms {
822            // Generate keypair
823            let keypair = provider
824                .generate_keypair(algorithm, None)
825                .unwrap_or_else(|_| panic!("Failed to generate keypair for {algorithm:?}"));
826
827            // Derive public key
828            let derived_pk = provider
829                .derive_public_key(algorithm, &keypair.secret_key)
830                .unwrap_or_else(|_| panic!("Failed to derive public key for {algorithm:?}"));
831
832            // Verify match
833            assert_eq!(
834                derived_pk.data, keypair.public_key.data,
835                "Derived public key should match for {:?}",
836                algorithm
837            );
838        }
839    }
840}