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_correct::{
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_correct::{
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_correct::{
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_correct::{
208            Hqc1Ciphertext,
209            Hqc1SecretKey,
210            Hqc3Ciphertext,
211            Hqc3SecretKey,
212            Hqc5Ciphertext,
213            Hqc5SecretKey,
214        };
215        use crate::hqc_kem::{
216            HqcKemCiphertext,
217            HqcKemSecretKey,
218        };
219        use crate::hqc_pke::{
220            HqcPkeCiphertext,
221            HqcPkePublicKey,
222            HqcPkeSecretKey,
223        };
224        #[allow(unused_imports)] // HqcParams trait needed for associated constants access
225        use crate::params_correct::{
226            Hqc1Params,
227            Hqc3Params,
228            Hqc5Params,
229            HqcParams,
230        };
231
232        match algorithm {
233            Algorithm::Hqc128 => {
234                // Parse secret key: ek_pke (PUBLIC_KEY_BYTES) + dk_pke (32) + sigma (16) + seed_kem (48)
235                let sk_bytes = &secret_key.data;
236                if sk_bytes.len() < Hqc1Params::SECRET_KEY_BYTES {
237                    return Err(Error::InvalidKeySize {
238                        expected: Hqc1Params::SECRET_KEY_BYTES,
239                        actual: sk_bytes.len(),
240                    });
241                }
242                let ek_pke_bytes = &sk_bytes[..Hqc1Params::PUBLIC_KEY_BYTES];
243                let dk_pke_start = Hqc1Params::PUBLIC_KEY_BYTES;
244                let dk_pke_bytes = &sk_bytes[dk_pke_start..dk_pke_start + 32];
245                let sigma_start = dk_pke_start + 32;
246                let mut sigma = Zeroizing::new([0u8; 16]);
247                sigma.copy_from_slice(&sk_bytes[sigma_start..sigma_start + 16]);
248                let seed_kem_start = sigma_start + 16;
249                let mut seed_kem = Zeroizing::new([0u8; 48]);
250                seed_kem.copy_from_slice(&sk_bytes[seed_kem_start..seed_kem_start + 48]);
251
252                let ek_pke = HqcPkePublicKey::<Hqc1Params>::new(ek_pke_bytes.to_vec());
253                let mut dk_pke_array = Zeroizing::new([0u8; 32]);
254                dk_pke_array.copy_from_slice(dk_pke_bytes);
255                let dk_pke = HqcPkeSecretKey::new(*dk_pke_array);
256                let kem_sk = HqcKemSecretKey::new(ek_pke, dk_pke, *sigma, *seed_kem);
257                let sk = Hqc1SecretKey::new(kem_sk);
258
259                // Parse ciphertext: c_pke (VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES) + salt (16)
260                let pke_ct_size = Hqc1Params::VEC_N_SIZE_BYTES + Hqc1Params::VEC_N1N2_SIZE_BYTES;
261                if ciphertext.len() < pke_ct_size + 16 {
262                    return Err(Error::InvalidKeySize {
263                        expected: pke_ct_size + 16,
264                        actual: ciphertext.len(),
265                    });
266                }
267                let c_pke_bytes = &ciphertext[..pke_ct_size];
268                let mut salt = Zeroizing::new([0u8; 16]);
269                salt.copy_from_slice(&ciphertext[pke_ct_size..pke_ct_size + 16]);
270                let c_pke = HqcPkeCiphertext::<Hqc1Params>::new(c_pke_bytes.to_vec());
271                let kem_ct = HqcKemCiphertext::new(c_pke, *salt);
272                let ct = Hqc1Ciphertext::new(kem_ct);
273
274                let shared_secret =
275                    Hqc1::decapsulate::<lib_q_random::LibQRng>(&sk, &ct).map_err(|e| {
276                        Error::InternalError {
277                            operation: String::from("HQC-128 decapsulation"),
278                            details: format!("Failed to decapsulate HQC-128: {:?}", e),
279                        }
280                    })?;
281                Ok(Vec::from(shared_secret.as_bytes()))
282            }
283            Algorithm::Hqc192 => {
284                // Parse secret key
285                let sk_bytes = &secret_key.data;
286                if sk_bytes.len() < Hqc3Params::SECRET_KEY_BYTES {
287                    return Err(Error::InvalidKeySize {
288                        expected: Hqc3Params::SECRET_KEY_BYTES,
289                        actual: sk_bytes.len(),
290                    });
291                }
292                let ek_pke_bytes = &sk_bytes[..Hqc3Params::PUBLIC_KEY_BYTES];
293                let dk_pke_start = Hqc3Params::PUBLIC_KEY_BYTES;
294                let dk_pke_bytes = &sk_bytes[dk_pke_start..dk_pke_start + 32];
295                let sigma_start = dk_pke_start + 32;
296                let mut sigma = Zeroizing::new([0u8; 16]);
297                sigma.copy_from_slice(&sk_bytes[sigma_start..sigma_start + 16]);
298                let seed_kem_start = sigma_start + 16;
299                let mut seed_kem = Zeroizing::new([0u8; 48]);
300                seed_kem.copy_from_slice(&sk_bytes[seed_kem_start..seed_kem_start + 48]);
301
302                let ek_pke = HqcPkePublicKey::<Hqc3Params>::new(ek_pke_bytes.to_vec());
303                let mut dk_pke_array = Zeroizing::new([0u8; 32]);
304                dk_pke_array.copy_from_slice(dk_pke_bytes);
305                let dk_pke = HqcPkeSecretKey::new(*dk_pke_array);
306                let kem_sk = HqcKemSecretKey::new(ek_pke, dk_pke, *sigma, *seed_kem);
307                let sk = Hqc3SecretKey::new(kem_sk);
308
309                // Parse ciphertext: c_pke (VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES) + salt (16)
310                let pke_ct_size = Hqc3Params::VEC_N_SIZE_BYTES + Hqc3Params::VEC_N1N2_SIZE_BYTES;
311                if ciphertext.len() < pke_ct_size + 16 {
312                    return Err(Error::InvalidKeySize {
313                        expected: pke_ct_size + 16,
314                        actual: ciphertext.len(),
315                    });
316                }
317                let c_pke_bytes = &ciphertext[..pke_ct_size];
318                let mut salt = Zeroizing::new([0u8; 16]);
319                salt.copy_from_slice(&ciphertext[pke_ct_size..pke_ct_size + 16]);
320                let c_pke = HqcPkeCiphertext::<Hqc3Params>::new(c_pke_bytes.to_vec());
321                let kem_ct = HqcKemCiphertext::new(c_pke, *salt);
322                let ct = Hqc3Ciphertext::new(kem_ct);
323
324                let shared_secret =
325                    Hqc3::decapsulate::<lib_q_random::LibQRng>(&sk, &ct).map_err(|e| {
326                        Error::InternalError {
327                            operation: String::from("HQC-192 decapsulation"),
328                            details: format!("Failed to decapsulate HQC-192: {:?}", e),
329                        }
330                    })?;
331                Ok(Vec::from(shared_secret.as_bytes()))
332            }
333            Algorithm::Hqc256 => {
334                // Parse secret key
335                let sk_bytes = &secret_key.data;
336                if sk_bytes.len() < Hqc5Params::SECRET_KEY_BYTES {
337                    return Err(Error::InvalidKeySize {
338                        expected: Hqc5Params::SECRET_KEY_BYTES,
339                        actual: sk_bytes.len(),
340                    });
341                }
342                let ek_pke_bytes = &sk_bytes[..Hqc5Params::PUBLIC_KEY_BYTES];
343                let dk_pke_start = Hqc5Params::PUBLIC_KEY_BYTES;
344                let dk_pke_bytes = &sk_bytes[dk_pke_start..dk_pke_start + 32];
345                let sigma_start = dk_pke_start + 32;
346                let mut sigma = Zeroizing::new([0u8; 16]);
347                sigma.copy_from_slice(&sk_bytes[sigma_start..sigma_start + 16]);
348                let seed_kem_start = sigma_start + 16;
349                let mut seed_kem = Zeroizing::new([0u8; 48]);
350                seed_kem.copy_from_slice(&sk_bytes[seed_kem_start..seed_kem_start + 48]);
351
352                let ek_pke = HqcPkePublicKey::<Hqc5Params>::new(ek_pke_bytes.to_vec());
353                let mut dk_pke_array = Zeroizing::new([0u8; 32]);
354                dk_pke_array.copy_from_slice(dk_pke_bytes);
355                let dk_pke = HqcPkeSecretKey::new(*dk_pke_array);
356                let kem_sk = HqcKemSecretKey::new(ek_pke, dk_pke, *sigma, *seed_kem);
357                let sk = Hqc5SecretKey::new(kem_sk);
358
359                // Parse ciphertext: c_pke (VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES) + salt (16)
360                let pke_ct_size = Hqc5Params::VEC_N_SIZE_BYTES + Hqc5Params::VEC_N1N2_SIZE_BYTES;
361                if ciphertext.len() < pke_ct_size + 16 {
362                    return Err(Error::InvalidKeySize {
363                        expected: pke_ct_size + 16,
364                        actual: ciphertext.len(),
365                    });
366                }
367                let c_pke_bytes = &ciphertext[..pke_ct_size];
368                let mut salt = Zeroizing::new([0u8; 16]);
369                salt.copy_from_slice(&ciphertext[pke_ct_size..pke_ct_size + 16]);
370                let c_pke = HqcPkeCiphertext::<Hqc5Params>::new(c_pke_bytes.to_vec());
371                let kem_ct = HqcKemCiphertext::new(c_pke, *salt);
372                let ct = Hqc5Ciphertext::new(kem_ct);
373
374                let shared_secret =
375                    Hqc5::decapsulate::<lib_q_random::LibQRng>(&sk, &ct).map_err(|e| {
376                        Error::InternalError {
377                            operation: String::from("HQC-256 decapsulation"),
378                            details: format!("Failed to decapsulate HQC-256: {:?}", e),
379                        }
380                    })?;
381                Ok(Vec::from(shared_secret.as_bytes()))
382            }
383            _ => Err(Error::InvalidAlgorithm {
384                algorithm: "Unsupported algorithm",
385            }),
386        }
387    }
388
389    fn derive_public_key(
390        &self,
391        algorithm: Algorithm,
392        secret_key: &lib_q_core::KemSecretKey,
393    ) -> Result<lib_q_core::KemPublicKey> {
394        #[allow(unused_imports)] // HqcParams trait needed for associated constants access
395        use crate::params_correct::{
396            Hqc1Params,
397            Hqc3Params,
398            Hqc5Params,
399            HqcParams,
400        };
401
402        match algorithm {
403            Algorithm::Hqc128 => {
404                // Validate secret key size
405                let sk_bytes = &secret_key.data;
406                if sk_bytes.len() < Hqc1Params::SECRET_KEY_BYTES {
407                    return Err(Error::InvalidKeySize {
408                        expected: Hqc1Params::SECRET_KEY_BYTES,
409                        actual: sk_bytes.len(),
410                    });
411                }
412
413                // Extract ek_pke (public key) from first PUBLIC_KEY_BYTES of secret key
414                // Secret key structure: ek_pke (PUBLIC_KEY_BYTES) + dk_pke (32) + sigma (16) + seed_kem (48)
415                let ek_pke_bytes = &sk_bytes[..Hqc1Params::PUBLIC_KEY_BYTES];
416
417                Ok(lib_q_core::KemPublicKey::new(Vec::from(ek_pke_bytes)))
418            }
419            Algorithm::Hqc192 => {
420                // Validate secret key size
421                let sk_bytes = &secret_key.data;
422                if sk_bytes.len() < Hqc3Params::SECRET_KEY_BYTES {
423                    return Err(Error::InvalidKeySize {
424                        expected: Hqc3Params::SECRET_KEY_BYTES,
425                        actual: sk_bytes.len(),
426                    });
427                }
428
429                // Extract ek_pke (public key) from first PUBLIC_KEY_BYTES of secret key
430                let ek_pke_bytes = &sk_bytes[..Hqc3Params::PUBLIC_KEY_BYTES];
431
432                Ok(lib_q_core::KemPublicKey::new(Vec::from(ek_pke_bytes)))
433            }
434            Algorithm::Hqc256 => {
435                // Validate secret key size
436                let sk_bytes = &secret_key.data;
437                if sk_bytes.len() < Hqc5Params::SECRET_KEY_BYTES {
438                    return Err(Error::InvalidKeySize {
439                        expected: Hqc5Params::SECRET_KEY_BYTES,
440                        actual: sk_bytes.len(),
441                    });
442                }
443
444                // Extract ek_pke (public key) from first PUBLIC_KEY_BYTES of secret key
445                let ek_pke_bytes = &sk_bytes[..Hqc5Params::PUBLIC_KEY_BYTES];
446
447                Ok(lib_q_core::KemPublicKey::new(Vec::from(ek_pke_bytes)))
448            }
449            _ => Err(Error::InvalidAlgorithm {
450                algorithm: "Unsupported algorithm for HQC derive_public_key",
451            }),
452        }
453    }
454}
455
456impl Default for LibQHqcProvider {
457    fn default() -> Self {
458        Self::new().expect("HQC provider should always be creatable")
459    }
460}
461
462#[cfg(all(feature = "alloc", feature = "random"))]
463impl CryptoProvider for LibQHqcProvider {
464    fn kem(&self) -> Option<&dyn KemOperations> {
465        Some(self)
466    }
467
468    fn signature(&self) -> Option<&dyn lib_q_core::SignatureOperations> {
469        None
470    }
471
472    fn hash(&self) -> Option<&dyn lib_q_core::HashOperations> {
473        None
474    }
475
476    fn aead(&self) -> Option<&dyn lib_q_core::AeadOperations> {
477        None
478    }
479}
480
481#[cfg(test)]
482mod tests {
483    #[cfg(feature = "alloc")]
484    extern crate alloc;
485
486    use lib_q_core::Algorithm;
487
488    use super::*;
489
490    #[test]
491    fn test_derive_public_key_hqc128() {
492        let provider = LibQHqcProvider::new().expect("Failed to create provider");
493
494        // Generate keypair
495        let keypair = provider
496            .generate_keypair(Algorithm::Hqc128, None)
497            .expect("Failed to generate keypair");
498
499        // Derive public key from secret key
500        let derived_pk = provider
501            .derive_public_key(Algorithm::Hqc128, &keypair.secret_key)
502            .expect("Failed to derive public key");
503
504        // Verify derived public key matches original
505        assert_eq!(
506            derived_pk.data, keypair.public_key.data,
507            "Derived public key should match original public key"
508        );
509        assert_eq!(
510            derived_pk.data.len(),
511            keypair.public_key.data.len(),
512            "Derived public key size should match original"
513        );
514    }
515
516    #[test]
517    fn test_derive_public_key_hqc192() {
518        let provider = LibQHqcProvider::new().expect("Failed to create provider");
519
520        // Generate keypair
521        let keypair = provider
522            .generate_keypair(Algorithm::Hqc192, None)
523            .expect("Failed to generate keypair");
524
525        // Derive public key from secret key
526        let derived_pk = provider
527            .derive_public_key(Algorithm::Hqc192, &keypair.secret_key)
528            .expect("Failed to derive public key");
529
530        // Verify derived public key matches original
531        assert_eq!(
532            derived_pk.data, keypair.public_key.data,
533            "Derived public key should match original public key"
534        );
535        assert_eq!(
536            derived_pk.data.len(),
537            keypair.public_key.data.len(),
538            "Derived public key size should match original"
539        );
540    }
541
542    #[test]
543    fn test_derive_public_key_hqc256() {
544        let provider = LibQHqcProvider::new().expect("Failed to create provider");
545
546        // Generate keypair
547        let keypair = provider
548            .generate_keypair(Algorithm::Hqc256, None)
549            .expect("Failed to generate keypair");
550
551        // Derive public key from secret key
552        let derived_pk = provider
553            .derive_public_key(Algorithm::Hqc256, &keypair.secret_key)
554            .expect("Failed to derive public key");
555
556        // Verify derived public key matches original
557        assert_eq!(
558            derived_pk.data, keypair.public_key.data,
559            "Derived public key should match original public key"
560        );
561        assert_eq!(
562            derived_pk.data.len(),
563            keypair.public_key.data.len(),
564            "Derived public key size should match original"
565        );
566    }
567
568    #[test]
569    fn test_derive_public_key_invalid_key_size() {
570        let provider = LibQHqcProvider::new().expect("Failed to create provider");
571
572        // Create a secret key with invalid size
573        let invalid_sk = lib_q_core::KemSecretKey::new(alloc::vec![0u8; 100]);
574
575        // Should return error for invalid key size
576        let result = provider.derive_public_key(Algorithm::Hqc128, &invalid_sk);
577        assert!(result.is_err(), "Should return error for invalid key size");
578        if let Err(Error::InvalidKeySize { .. }) = result {
579            // Expected error type
580        } else {
581            panic!("Expected InvalidKeySize error, got: {:?}", result);
582        }
583    }
584
585    #[test]
586    fn test_derive_public_key_unsupported_algorithm() {
587        let provider = LibQHqcProvider::new().expect("Failed to create provider");
588
589        // Generate a valid HQC-128 keypair
590        let keypair = provider
591            .generate_keypair(Algorithm::Hqc128, None)
592            .expect("Failed to generate keypair");
593
594        // Try to derive with unsupported algorithm
595        let result = provider.derive_public_key(Algorithm::MlKem512, &keypair.secret_key);
596        assert!(
597            result.is_err(),
598            "Should return error for unsupported algorithm"
599        );
600        if let Err(Error::InvalidAlgorithm { .. }) = result {
601            // Expected error type
602        } else {
603            panic!("Expected InvalidAlgorithm error, got: {:?}", result);
604        }
605    }
606
607    #[test]
608    fn test_derive_public_key_round_trip_encapsulation() {
609        let provider = LibQHqcProvider::new().expect("Failed to create provider");
610
611        let keypair = provider
612            .generate_keypair(Algorithm::Hqc128, None)
613            .expect("Failed to generate keypair");
614
615        let derived_pk = provider
616            .derive_public_key(Algorithm::Hqc128, &keypair.secret_key)
617            .expect("Failed to derive public key");
618
619        let mut enc_seed = [0u8; 48];
620        enc_seed[0] = 0xC1;
621        let (ciphertext, shared_secret1) = provider
622            .encapsulate(Algorithm::Hqc128, &derived_pk, Some(&enc_seed))
623            .expect("Failed to encapsulate with derived public key");
624
625        let shared_secret2 = provider
626            .decapsulate(Algorithm::Hqc128, &keypair.secret_key, &ciphertext)
627            .expect("Failed to decapsulate");
628
629        assert_eq!(
630            shared_secret1, shared_secret2,
631            "Shared secrets should match when using derived public key"
632        );
633    }
634
635    #[test]
636    fn test_derive_public_key_multiple_round_trips() {
637        let provider = LibQHqcProvider::new().expect("Failed to create provider");
638
639        let keypair = provider
640            .generate_keypair(Algorithm::Hqc192, None)
641            .expect("Failed to generate keypair");
642
643        let derived_pk = provider
644            .derive_public_key(Algorithm::Hqc192, &keypair.secret_key)
645            .expect("Failed to derive public key");
646
647        for i in 0u8..3 {
648            let mut enc_seed = [0u8; 48];
649            enc_seed[0] = i;
650            enc_seed[1] = 0xE5;
651            let (ciphertext, shared_secret1) = provider
652                .encapsulate(Algorithm::Hqc192, &derived_pk, Some(&enc_seed))
653                .expect("Failed to encapsulate");
654
655            let shared_secret2 = provider
656                .decapsulate(Algorithm::Hqc192, &keypair.secret_key, &ciphertext)
657                .expect("Failed to decapsulate");
658
659            assert_eq!(
660                shared_secret1, shared_secret2,
661                "Shared secrets should match in round-trip test"
662            );
663        }
664    }
665
666    #[test]
667    fn test_derive_public_key_all_algorithms() {
668        let provider = LibQHqcProvider::new().expect("Failed to create provider");
669
670        let algorithms = [Algorithm::Hqc128, Algorithm::Hqc192, Algorithm::Hqc256];
671
672        for algorithm in algorithms {
673            // Generate keypair
674            let keypair = provider
675                .generate_keypair(algorithm, None)
676                .unwrap_or_else(|_| panic!("Failed to generate keypair for {algorithm:?}"));
677
678            // Derive public key
679            let derived_pk = provider
680                .derive_public_key(algorithm, &keypair.secret_key)
681                .unwrap_or_else(|_| panic!("Failed to derive public key for {algorithm:?}"));
682
683            // Verify match
684            assert_eq!(
685                derived_pk.data, keypair.public_key.data,
686                "Derived public key should match for {:?}",
687                algorithm
688            );
689        }
690    }
691}