@@ -137,8 +137,17 @@ async fn secure_threshold_keygen_isolated() -> Result<()> {
137137 let keygen_id = derive_request_id ( "secure_threshold_keygen" ) ?;
138138
139139 // Run secure key generation with preprocessing
140- threshold_key_gen_secure_isolated ( & env. clients , & preproc_id, & keygen_id, FheParameter :: Test )
141- . await ?;
140+ threshold_key_gen_secure_isolated (
141+ & env. clients ,
142+ & preproc_id,
143+ & keygen_id,
144+ FheParameter :: Test ,
145+ None ,
146+ None ,
147+ None ,
148+ None ,
149+ )
150+ . await ?;
142151
143152 // Verify key was generated on all parties
144153 for client in env. all_clients ( ) {
@@ -374,6 +383,170 @@ async fn secure_threshold_keygen_crash_preprocessing_isolated() -> Result<()> {
374383 Ok ( ( ) )
375384}
376385
386+ /// Test secure threshold compressed key generation from existing secret shares.
387+ ///
388+ /// Generates a standard keyset first, then performs compressed key generation
389+ /// reusing the existing secret key shares from the first keygen. This validates
390+ /// the end-to-end flow of compressed keygen from existing secrets through the
391+ /// gRPC service layer.
392+ ///
393+ /// **Workflow:**
394+ /// 1. Standard keygen (preprocessing + online) to produce secret shares
395+ /// 2. Preprocessing for compressed keygen from existing
396+ /// 3. Compressed keygen from existing shares
397+ /// 4. Verify both keygens completed on all parties
398+ ///
399+ /// **Requires:**
400+ /// - `slow_tests` feature flag
401+ ///
402+ /// **Run with:** `cargo test --lib --features slow_tests,testing secure_threshold_compressed_keygen_from_existing_isolated`
403+ #[ tokio:: test]
404+ #[ cfg( feature = "slow_tests" ) ]
405+ async fn secure_threshold_compressed_keygen_from_existing_isolated ( ) -> Result < ( ) > {
406+ use crate :: client:: tests:: common:: compressed_from_existing_keygen_config;
407+ use crate :: consts:: DEFAULT_EPOCH_ID ;
408+
409+ let env = ThresholdTestEnv :: builder ( )
410+ . with_test_name ( "compressed_from_existing_keygen" )
411+ . with_party_count ( 4 )
412+ . with_threshold ( 1 )
413+ . with_prss ( )
414+ . build ( )
415+ . await ?;
416+
417+ let clients = & env. clients ;
418+
419+ // Step 1: Standard keygen (preprocessing + online)
420+ let preproc_id_1 = derive_request_id ( "compressed_existing_preproc_1" ) ?;
421+ let keygen_id_1 = derive_request_id ( "compressed_existing_keygen_1" ) ?;
422+
423+ threshold_key_gen_secure_isolated (
424+ clients,
425+ & preproc_id_1,
426+ & keygen_id_1,
427+ FheParameter :: Test ,
428+ None ,
429+ None ,
430+ None ,
431+ None ,
432+ )
433+ . await ?;
434+
435+ // Verify standard keygen completed on all parties
436+ for client in env. all_clients ( ) {
437+ let mut cur_client = client. clone ( ) ;
438+ let result = cur_client
439+ . get_key_gen_result ( tonic:: Request :: new ( keygen_id_1. into ( ) ) )
440+ . await ?;
441+ assert_eq ! ( result. into_inner( ) . request_id, Some ( keygen_id_1. into( ) ) ) ;
442+ }
443+
444+ // Step 2: Compressed keygen from existing secret shares (preprocessing + online)
445+ let preproc_id_2 = derive_request_id ( "compressed_existing_preproc_2" ) ?;
446+ let keygen_id_2 = derive_request_id ( "compressed_existing_keygen_2" ) ?;
447+
448+ let ( keyset_config, keyset_added_info) =
449+ compressed_from_existing_keygen_config ( & keygen_id_1, & DEFAULT_EPOCH_ID ) ;
450+
451+ threshold_key_gen_secure_isolated (
452+ clients,
453+ & preproc_id_2,
454+ & keygen_id_2,
455+ FheParameter :: Test ,
456+ keyset_config,
457+ keyset_added_info,
458+ None ,
459+ None ,
460+ )
461+ . await ?;
462+
463+ // Verify compressed keygen completed on all parties
464+ for client in env. all_clients ( ) {
465+ let mut cur_client = client. clone ( ) ;
466+ let result = cur_client
467+ . get_key_gen_result ( tonic:: Request :: new ( keygen_id_2. into ( ) ) )
468+ . await ?;
469+ assert_eq ! ( result. into_inner( ) . request_id, Some ( keygen_id_2. into( ) ) ) ;
470+ }
471+
472+ // Do distributed decryption to verify the generated key is ok
473+ // TODO this could be refactored
474+ use crate :: client:: tests:: threshold:: public_decryption_tests:: run_decryption_threshold;
475+ use crate :: util:: key_setup:: test_tools:: { EncryptionConfig , TestingPlaintext } ;
476+ let material_dir = env. material_dir ;
477+ let mut servers = env. servers ;
478+ let mut clients = env. clients ;
479+
480+ let material_path = material_dir. path ( ) ;
481+ let pub_storage_prefixes = & crate :: consts:: PUBLIC_STORAGE_PREFIX_THRESHOLD_ALL [ 0 ..4 ] ;
482+
483+ // Create internal client for decryption
484+ let mut pub_storage_map = std:: collections:: HashMap :: new ( ) ;
485+ for ( i, prefix) in pub_storage_prefixes. iter ( ) . enumerate ( ) {
486+ pub_storage_map. insert (
487+ ( i + 1 ) as u32 ,
488+ FileStorage :: new ( Some ( material_path) , StorageType :: PUB , prefix. as_deref ( ) ) ?,
489+ ) ;
490+ }
491+ let client_storage = FileStorage :: new ( Some ( material_path) , StorageType :: CLIENT , None ) ?;
492+ let mut internal_client = crate :: client:: client_wasm:: Client :: new_client (
493+ client_storage,
494+ pub_storage_map,
495+ & crate :: consts:: TEST_PARAM ,
496+ None ,
497+ )
498+ . await ?;
499+
500+ // Run ddec with the new keyset
501+ run_decryption_threshold (
502+ 4 ,
503+ & mut servers,
504+ & mut clients,
505+ & mut internal_client,
506+ None ,
507+ & keygen_id_2,
508+ None ,
509+ vec ! [ TestingPlaintext :: U32 ( 66 ) ] ,
510+ EncryptionConfig {
511+ compression : true ,
512+ precompute_sns : true ,
513+ } ,
514+ None ,
515+ 1 ,
516+ Some ( material_path) ,
517+ true ,
518+ )
519+ . await ;
520+
521+ // Run ddec by encrypting using the old public key but
522+ // still the new shares from the new keyset
523+ run_decryption_threshold (
524+ 4 ,
525+ & mut servers,
526+ & mut clients,
527+ & mut internal_client,
528+ Some ( & keygen_id_1) ,
529+ & keygen_id_2,
530+ None ,
531+ vec ! [ TestingPlaintext :: U32 ( 55 ) ] ,
532+ EncryptionConfig {
533+ compression : true ,
534+ precompute_sns : true ,
535+ } ,
536+ None ,
537+ 1 ,
538+ Some ( material_path) ,
539+ false , // we do not used compressed_keys since that was the old public key
540+ )
541+ . await ;
542+
543+ for ( _, server) in servers {
544+ server. assert_shutdown ( ) . await ;
545+ }
546+
547+ Ok ( ( ) )
548+ }
549+
377550/// Test insecure threshold decompression key generation with decryption validation.
378551///
379552/// Generates two regular keysets using insecure mode, then generates a decompression key
@@ -480,6 +653,8 @@ async fn test_insecure_threshold_decompression_keygen_isolated() -> Result<()> {
480653 compression_keyset_id : None ,
481654 from_keyset_id_decompression_only : Some ( key_id_1. into ( ) ) ,
482655 to_keyset_id_decompression_only : Some ( key_id_2. into ( ) ) ,
656+ existing_keyset_id : None ,
657+ existing_epoch_id : None ,
483658 } ) ,
484659 context_id : None ,
485660 epoch_id : None ,
@@ -561,12 +736,13 @@ async fn test_insecure_threshold_decompression_keygen_isolated() -> Result<()> {
561736 & mut servers,
562737 & mut clients,
563738 & mut internal_client,
739+ None ,
564740 & key_id_1,
565741 None ,
566742 vec ! [ TestingPlaintext :: U32 ( 42 ) ] ,
567743 EncryptionConfig {
568744 compression : true ,
569- precompute_sns : false ,
745+ precompute_sns : true ,
570746 } ,
571747 None ,
572748 1 ,
0 commit comments