@@ -32,9 +32,10 @@ use tfhe_backward_compat_data::load::{
3232use tfhe_backward_compat_data:: {
3333 DataKind , HlBoolCiphertextTest , HlCiphertextTest , HlClientKeyTest , HlCompressedKVStoreTest ,
3434 HlCompressedSquashedNoiseCiphertextListTest , HlCompressedXofKeySetTest ,
35- HlHeterogeneousCiphertextListTest , HlPublicKeyTest , HlServerKeyTest , HlSignedCiphertextTest ,
36- HlSquashedNoiseBoolCiphertextTest , HlSquashedNoiseSignedCiphertextTest ,
37- HlSquashedNoiseUnsignedCiphertextTest , TestMetadata , TestType , Testcase , ZkPkePublicParamsTest ,
35+ HlHeterogeneousCiphertextListTest , HlPublicKeyTest , HlSeededCompactCiphertextListTest ,
36+ HlServerKeyTest , HlSignedCiphertextTest , HlSquashedNoiseBoolCiphertextTest ,
37+ HlSquashedNoiseSignedCiphertextTest , HlSquashedNoiseUnsignedCiphertextTest , TestMetadata ,
38+ TestType , Testcase , ZkPkePublicParamsTest , ZkProofAuxiliaryInfo ,
3839} ;
3940use tfhe_versionable:: Unversionize ;
4041
@@ -249,44 +250,206 @@ pub fn test_hl_heterogeneous_ciphertext_list_elements<CtList: CiphertextList>(
249250 key : & ClientKey ,
250251 test : & HlHeterogeneousCiphertextListTest ,
251252) -> Result < ( ) , String > {
252- for idx in 0 ..( list. len ( ) ) {
253- match test. data_kinds [ idx] {
253+ verify_expanded_values ( & list, & test. clear_values , & test. data_kinds , key)
254+ }
255+
256+ fn verify_expanded_values < CtList > (
257+ list : & CtList ,
258+ clear_values : & [ i64 ] ,
259+ data_kinds : & [ DataKind ] ,
260+ key : & ClientKey ,
261+ ) -> Result < ( ) , String >
262+ where
263+ CtList : CiphertextList ,
264+ {
265+ if clear_values. len ( ) != data_kinds. len ( ) {
266+ return Err ( format ! (
267+ "Number of clear values ({}) is not the same if the number of data kind ({})" ,
268+ clear_values. len( ) ,
269+ data_kinds. len( ) ,
270+ ) ) ;
271+ }
272+
273+ if clear_values. len ( ) != list. len ( ) {
274+ return Err ( format ! (
275+ "Number of clear values ({}) is not the same if the number of values in the expander({})" ,
276+ clear_values. len( ) ,
277+ list. len( ) ,
278+ ) ) ;
279+ }
280+
281+ for ( idx, ( value, kind) ) in clear_values. iter ( ) . zip ( data_kinds. iter ( ) ) . enumerate ( ) {
282+ match kind {
254283 DataKind :: Bool => {
255- let ct: FheBool = list. get ( idx) . unwrap ( ) . unwrap ( ) ;
284+ let ct: FheBool = list
285+ . get ( idx)
286+ . map_err ( |e| format ! ( "Failed to get bool at index {idx}: {e}" ) ) ?
287+ . ok_or_else ( || format ! ( "No value at index {idx}" ) ) ?;
256288 let clear = ct. decrypt ( key) ;
257- if clear != ( test . clear_values [ idx ] != 0 ) {
289+ if clear != ( * value != 0 ) {
258290 return Err ( format ! (
259- "Invalid decrypted cleartext: \n Expected : \n {:?}\n Got: \n { :?}" ,
260- clear , test . clear_values [ idx ]
291+ "Invalid decrypted bool at index {idx}: expected {:?}, got {clear :?}" ,
292+ * value != 0
261293 ) ) ;
262294 }
263295 }
264296 DataKind :: Signed => {
265- let ct: FheInt8 = list. get ( idx) . unwrap ( ) . unwrap ( ) ;
297+ let ct: FheInt8 = list
298+ . get ( idx)
299+ . map_err ( |e| format ! ( "Failed to get signed at index {idx}: {e}" ) ) ?
300+ . ok_or_else ( || format ! ( "No value at index {idx}" ) ) ?;
266301 let clear: i8 = ct. decrypt ( key) ;
267- if clear != test . clear_values [ idx ] as i8 {
302+ if clear != * value as i8 {
268303 return Err ( format ! (
269- "Invalid decrypted cleartext:\n Expected :\n {:?}\n Got:\n {:?}" ,
270- clear,
271- ( test. clear_values[ idx] as i8 )
304+ "Invalid decrypted signed at index {idx}: expected {:?}, got {clear:?}" ,
305+ * value as i8
272306 ) ) ;
273307 }
274308 }
275309 DataKind :: Unsigned => {
276- let ct: FheUint8 = list. get ( idx) . unwrap ( ) . unwrap ( ) ;
310+ let ct: FheUint8 = list
311+ . get ( idx)
312+ . map_err ( |e| format ! ( "Failed to get unsigned at index {idx}: {e}" ) ) ?
313+ . ok_or_else ( || format ! ( "No value at index {idx}" ) ) ?;
277314 let clear: u8 = ct. decrypt ( key) ;
278- if clear != test . clear_values [ idx ] as u8 {
315+ if clear != * value as u8 {
279316 return Err ( format ! (
280- "Invalid decrypted cleartext: \n Expected : \n {:?}\n Got: \n { :?}" ,
281- clear , test . clear_values [ idx ]
317+ "Invalid decrypted unsigned at index {idx}: expected {:?}, got {clear :?}" ,
318+ * value as u8
282319 ) ) ;
283320 }
284321 }
285- } ;
322+ }
286323 }
287324 Ok ( ( ) )
288325}
289326
327+ /// Shared core for seeded compact ciphertext list backward compat tests.
328+ /// When `zk_proof_info` is `Some`, operates in ZK (proven) mode; otherwise plain mode.
329+ #[ allow( clippy:: too_many_arguments) ]
330+ fn test_hl_seeded_compact_list_core < T : TestType > (
331+ dir : & Path ,
332+ test : & T ,
333+ format : DataFormat ,
334+ key_filename : & str ,
335+ public_key_filename : & str ,
336+ clear_values : & [ i64 ] ,
337+ data_kinds : & [ DataKind ] ,
338+ seed : & [ u8 ] ,
339+ zk_proof_info : Option < & ZkProofAuxiliaryInfo > ,
340+ ) -> Result < TestSuccess , TestFailure > {
341+ #[ cfg( not( feature = "zk-pok" ) ) ]
342+ if zk_proof_info. is_some ( ) {
343+ return Ok ( test. success ( format) ) ;
344+ }
345+
346+ let key_file = dir. join ( key_filename) ;
347+ let key = ClientKey :: unversionize (
348+ load_versioned_auxiliary ( key_file) . map_err ( |e| test. failure ( e, format) ) ?,
349+ )
350+ . map_err ( |e| test. failure ( e, format) ) ?;
351+
352+ let server_key = key. generate_server_key ( ) ;
353+ set_server_key ( server_key) ;
354+
355+ let pubkey_file = dir. join ( public_key_filename) ;
356+ let pubkey = CompactPublicKey :: unversionize (
357+ load_versioned_auxiliary ( pubkey_file) . map_err ( |e| test. failure ( e, format) ) ?,
358+ )
359+ . map_err ( |e| test. failure ( e, format) ) ?;
360+
361+ let mut builder = CompactCiphertextList :: builder ( & pubkey) ;
362+ for ( value, kind) in clear_values. iter ( ) . zip ( data_kinds. iter ( ) ) {
363+ match kind {
364+ DataKind :: Unsigned => {
365+ builder. push ( * value as u8 ) ;
366+ }
367+ DataKind :: Signed => {
368+ builder. push ( * value as i8 ) ;
369+ }
370+ DataKind :: Bool => {
371+ builder. push ( * value != 0 ) ;
372+ }
373+ }
374+ }
375+
376+ if let Some ( _proof_info) = zk_proof_info {
377+ #[ cfg( feature = "zk-pok" ) ]
378+ {
379+ use tfhe:: zk:: ZkComputeLoad ;
380+
381+ let crs_file = dir. join ( & * _proof_info. params_filename ) ;
382+ let crs = CompactPkeCrs :: unversionize (
383+ load_versioned_auxiliary ( crs_file) . map_err ( |e| test. failure ( e, format) ) ?,
384+ )
385+ . map_err ( |e| test. failure ( e, format) ) ?;
386+
387+ let pregenerated: ProvenCompactCiphertextList =
388+ load_and_unversionize ( dir, test, format) ?;
389+
390+ let rebuilt = builder
391+ . build_with_proof_packed_seeded (
392+ & crs,
393+ _proof_info. metadata . as_bytes ( ) ,
394+ ZkComputeLoad :: Proof ,
395+ seed,
396+ )
397+ . map_err ( |e| test. failure ( e, format) ) ?;
398+
399+ if pregenerated != rebuilt {
400+ return Err ( test. failure (
401+ "Seeded proven compact list rebuilt from values/seed does not match pregenerated" ,
402+ format,
403+ ) ) ;
404+ }
405+
406+ let expanded = pregenerated
407+ . verify_and_expand ( & crs, & pubkey, _proof_info. metadata . as_bytes ( ) )
408+ . map_err ( |e| test. failure ( e, format) ) ?;
409+ verify_expanded_values ( & expanded, clear_values, data_kinds, & key)
410+ . map_err ( |e| test. failure ( e, format) ) ?;
411+ }
412+ } else {
413+ let pregenerated: CompactCiphertextList = load_and_unversionize ( dir, test, format) ?;
414+
415+ let rebuilt = builder. build_packed_seeded ( seed) . unwrap ( ) ;
416+
417+ if pregenerated != rebuilt {
418+ return Err ( test. failure (
419+ "Seeded compact list rebuilt from values/seed does not match pregenerated" ,
420+ format,
421+ ) ) ;
422+ }
423+
424+ let expanded = pregenerated. expand ( ) . map_err ( |e| test. failure ( e, format) ) ?;
425+ verify_expanded_values ( & expanded, clear_values, data_kinds, & key)
426+ . map_err ( |e| test. failure ( e, format) ) ?;
427+ }
428+
429+ Ok ( test. success ( format) )
430+ }
431+
432+ /// Test seeded compact ciphertext list: loads pregenerated list, rebuilds from
433+ /// stored values/seed, asserts they match via PartialEq.
434+ pub fn test_hl_seeded_compact_ciphertext_list (
435+ dir : & Path ,
436+ test : & HlSeededCompactCiphertextListTest ,
437+ format : DataFormat ,
438+ ) -> Result < TestSuccess , TestFailure > {
439+ test_hl_seeded_compact_list_core (
440+ dir,
441+ test,
442+ format,
443+ & test. key_filename ,
444+ & test. public_key_filename ,
445+ & test. clear_values ,
446+ & test. data_kinds ,
447+ & test. seed ,
448+ test. proof_info . as_ref ( ) ,
449+ )
450+ }
451+
452+ /// Test seeded proven compact ciphertext list
290453/// Test HL client key: loads the key and checks the parameters using the values stored in
291454/// the test metadata.
292455pub fn test_hl_clientkey (
@@ -778,21 +941,20 @@ pub fn test_hl_compressed_squashed_noise_ciphertext_list(
778941 }
779942
780943 for i in 0 ..list. len ( ) {
781- let decrypted = match test. data_kinds [ i] {
944+ let decrypted: i64 = match test. data_kinds [ i] {
782945 DataKind :: Unsigned => {
783946 let ct: SquashedNoiseFheUint = list. get ( i) . unwrap ( ) . unwrap ( ) ;
784947 let clear: u64 = ct. decrypt ( & key) ;
785- clear
948+ clear as i64
786949 }
787950 DataKind :: Signed => {
788951 let ct: SquashedNoiseFheInt = list. get ( i) . unwrap ( ) . unwrap ( ) ;
789- let clear: i64 = ct. decrypt ( & key) ;
790- clear as u64
952+ ct. decrypt ( & key)
791953 }
792954 DataKind :: Bool => {
793955 let ct: SquashedNoiseFheBool = list. get ( i) . unwrap ( ) . unwrap ( ) ;
794956 let clear: bool = ct. decrypt ( & key) ;
795- clear as u64
957+ clear as i64
796958 }
797959 } ;
798960
@@ -953,6 +1115,9 @@ impl TestedModule for Hl {
9531115 TestMetadata :: HlCompressedXofKeySet ( test) => {
9541116 test_hl_compressed_xof_key_set_test ( test_dir. as_ref ( ) , test, format) . into ( )
9551117 }
1118+ TestMetadata :: HlSeededCompactCiphertextList ( test) => {
1119+ test_hl_seeded_compact_ciphertext_list ( test_dir. as_ref ( ) , test, format) . into ( )
1120+ }
9561121 _ => {
9571122 println ! ( "WARNING: missing test: {:?}" , testcase. metadata) ;
9581123 TestResult :: Skipped ( testcase. skip ( ) )
0 commit comments