@@ -18,6 +18,7 @@ use observability::{
1818 } ,
1919} ;
2020use tfhe:: integer:: compression_keys:: DecompressionKey ;
21+ use tfhe:: prelude:: Tagged ;
2122use tfhe:: xof_key_set:: CompressedXofKeySet ;
2223use threshold_fhe:: {
2324 algebra:: {
@@ -77,14 +78,18 @@ use crate::{
7778 } ,
7879 rate_limiter:: RateLimiter ,
7980 } ,
80- vault:: storage:: { crypto_material:: ThresholdCryptoMaterialStorage , Storage , StorageExt } ,
81+ vault:: storage:: {
82+ crypto_material:: { CryptoMaterialReader , ThresholdCryptoMaterialStorage } ,
83+ Storage , StorageExt ,
84+ } ,
8185} ;
8286
8387// === Current Module Imports ===
8488use super :: BucketMetaStore ;
8589
8690const DKG_Z64_SESSION_COUNTER : u64 = 1 ;
8791const DKG_Z128_SESSION_COUNTER : u64 = 2 ;
92+ const ERR_FAILED_TO_READ_EXISTING_TAG : & str = "Failed to read existing tag" ;
8893
8994struct DkgSessions {
9095 session_z64 : SmallSession < ResiduePolyF4Z64 > ,
@@ -342,6 +347,14 @@ impl<
342347 // we must validate the parameter before passing it into the background process
343348 internal_keyset_config. validate ( ) ?;
344349
350+ // Read existing key tag from public storage if needed
351+ let existing_key_tag: Option < tfhe:: Tag > = if internal_keyset_config. use_existing_key_tag ( ) {
352+ let existing_keyset_id = internal_keyset_config. get_existing_keyset_id ( ) ?;
353+ Some ( Self :: read_existing_key_tag ( & crypto_storage, & existing_keyset_id) . await ?)
354+ } else {
355+ None
356+ } ;
357+
345358 let keygen_background = async move {
346359 // Remove the preprocessing material, even if the request was cancelled we cannot reuse the preprocessing
347360 match & preproc_handle_w_mode {
@@ -366,6 +379,7 @@ impl<
366379 // Nothing to remove
367380 }
368381 }
382+
369383 match internal_keyset_config. keyset_config ( ) {
370384 ddec_keyset_config:: KeySetConfig :: Standard ( inner_config) => {
371385 Self :: key_gen_background (
@@ -382,6 +396,7 @@ impl<
382396 eip712_domain_copy,
383397 permit,
384398 op_tag,
399+ existing_key_tag,
385400 )
386401 . await
387402 }
@@ -1005,13 +1020,13 @@ impl<
10051020
10061021 #[ allow( clippy:: too_many_arguments) ]
10071022 async fn compressed_key_gen_from_existing_private_keyset < P > (
1008- req_id : & RequestId ,
10091023 dkg_sessions : & mut DkgSessions ,
10101024 crypto_storage : ThresholdCryptoMaterialStorage < PubS , PrivS > ,
10111025 params : DKGParams ,
10121026 existing_keyset_id : RequestId ,
10131027 existing_epoch_id : EpochId ,
10141028 preprocessing : & mut P ,
1029+ tag : tfhe:: Tag ,
10151030 ) -> anyhow:: Result < (
10161031 CompressedXofKeySet ,
10171032 PrivateKeySet < { ResiduePolyF4Z128 :: EXTENSION_DEGREE } > ,
@@ -1040,7 +1055,7 @@ impl<
10401055 & mut dkg_sessions. session_z128 ,
10411056 preprocessing,
10421057 params,
1043- req_id . into ( ) ,
1058+ tag ,
10441059 & existing_private_keys,
10451060 )
10461061 . await ?;
@@ -1049,13 +1064,13 @@ impl<
10491064
10501065 #[ allow( clippy:: too_many_arguments) ]
10511066 async fn key_gen_from_existing_private_keyset < P > (
1052- req_id : & RequestId ,
10531067 dkg_sessions : & mut DkgSessions ,
10541068 crypto_storage : ThresholdCryptoMaterialStorage < PubS , PrivS > ,
10551069 params : DKGParams ,
10561070 existing_keyset_id : RequestId ,
10571071 existing_epoch_id : EpochId ,
10581072 preprocessing : & mut P ,
1073+ tag : tfhe:: Tag ,
10591074 ) -> anyhow:: Result < (
10601075 FhePubKeySet ,
10611076 PrivateKeySet < { ResiduePolyF4Z128 :: EXTENSION_DEGREE } > ,
@@ -1084,7 +1099,7 @@ impl<
10841099 & mut dkg_sessions. session_z128 ,
10851100 preprocessing,
10861101 params,
1087- req_id . into ( ) ,
1102+ tag ,
10881103 & existing_private_keys,
10891104 )
10901105 . await ?;
@@ -1106,6 +1121,7 @@ impl<
11061121 eip712_domain : alloy_sol_types:: Eip712Domain ,
11071122 permit : OwnedSemaphorePermit ,
11081123 op_tag : & ' static str ,
1124+ existing_key_tag : Option < tfhe:: Tag > ,
11091125 ) {
11101126 let _permit = permit;
11111127 let start = Instant :: now ( ) ;
@@ -1209,14 +1225,15 @@ impl<
12091225 . get_existing_epoch_id ( )
12101226 . expect ( "validated" )
12111227 . unwrap_or ( * epoch_id) ;
1228+ let tag: tfhe:: Tag = existing_key_tag. unwrap_or_else ( || req_id. into ( ) ) ;
12121229 Self :: key_gen_from_existing_private_keyset (
1213- req_id,
12141230 & mut dkg_sessions,
12151231 crypto_storage. clone ( ) ,
12161232 params,
12171233 existing_keyset_id,
12181234 existing_epoch_id,
12191235 preproc_handle. as_mut ( ) ,
1236+ tag,
12201237 )
12211238 . await
12221239 . map ( |( pk, sk) | ThresholdKeyGenResult :: Uncompressed ( pk, sk) )
@@ -1233,14 +1250,15 @@ impl<
12331250 . get_existing_epoch_id ( )
12341251 . expect ( "validated" )
12351252 . unwrap_or ( * epoch_id) ;
1253+ let tag: tfhe:: Tag = existing_key_tag. unwrap_or_else ( || req_id. into ( ) ) ;
12361254 Self :: compressed_key_gen_from_existing_private_keyset (
1237- req_id,
12381255 & mut dkg_sessions,
12391256 crypto_storage. clone ( ) ,
12401257 params,
12411258 existing_keyset_id,
12421259 existing_epoch_id,
12431260 preproc_handle. as_mut ( ) ,
1261+ tag,
12441262 )
12451263 . await
12461264 . map ( |( compressed_keyset, sk) | {
@@ -1392,6 +1410,36 @@ impl<
13921410 start. elapsed( ) . as_millis( )
13931411 ) ;
13941412 }
1413+
1414+ /// Reads the tag from an existing keyset in public storage.
1415+ /// Tries CompressedXofKeySet first, then falls back to ServerKey.
1416+ async fn read_existing_key_tag (
1417+ crypto_storage : & ThresholdCryptoMaterialStorage < PubS , PrivS > ,
1418+ existing_keyset_id : & RequestId ,
1419+ ) -> anyhow:: Result < tfhe:: Tag > {
1420+ let pub_storage = crypto_storage. inner . public_storage . lock ( ) . await ;
1421+
1422+ let res = if let Ok ( compressed_keyset) =
1423+ <CompressedXofKeySet as CryptoMaterialReader >:: read_from_storage (
1424+ & * pub_storage,
1425+ existing_keyset_id,
1426+ )
1427+ . await
1428+ {
1429+ Ok ( compressed_keyset
1430+ . clone ( )
1431+ . into_raw_parts ( )
1432+ . 1
1433+ . into_raw_parts ( )
1434+ . 1 )
1435+ } else {
1436+ CryptoMaterialReader :: read_from_storage ( & * pub_storage, existing_keyset_id)
1437+ . await
1438+ . map ( |server_key : tfhe:: ServerKey | server_key. tag ( ) . clone ( ) )
1439+ } ;
1440+
1441+ res. map_err ( |e| anyhow:: anyhow!( "{}: {e}" , ERR_FAILED_TO_READ_EXISTING_TAG ) )
1442+ }
13951443}
13961444
13971445#[ tonic:: async_trait]
@@ -1800,6 +1848,54 @@ mod tests {
18001848 // we don't have a trait for meta store
18011849 }
18021850
1851+ #[ tokio:: test]
1852+ async fn use_existing_key_tag_with_wrong_keyset_id ( ) {
1853+ // When use_existing_key_tag is true but existing_keyset_id points to a
1854+ // non-existent key in storage, launch_dkg should fail with Internal
1855+ // because read_existing_key_tag cannot find any key material.
1856+ let ( prep_ids, kg) = setup_key_generator :: <
1857+ DroppingOnlineDistributedKeyGen128 < { ResiduePolyF4Z128 :: EXTENSION_DEGREE } > ,
1858+ > ( )
1859+ . await ;
1860+ let prep_id = prep_ids[ 0 ] ;
1861+ let key_id = RequestId :: new_random ( & mut OsRng ) ;
1862+ let wrong_keyset_id = RequestId :: new_random ( & mut OsRng ) ;
1863+
1864+ let domain = alloy_to_protobuf_domain ( & dummy_domain ( ) ) . unwrap ( ) ;
1865+ let keyset_config = KeySetConfig {
1866+ keyset_type : kms_grpc:: kms:: v1:: KeySetType :: Standard as i32 ,
1867+ standard_keyset_config : Some ( kms_grpc:: kms:: v1:: StandardKeySetConfig {
1868+ compute_key_type : 0 ,
1869+ secret_key_config : kms_grpc:: kms:: v1:: KeyGenSecretKeyConfig :: UseExisting as i32 ,
1870+ compressed_key_config : 0 ,
1871+ } ) ,
1872+ } ;
1873+ let keyset_added_info = KeySetAddedInfo {
1874+ existing_keyset_id : Some ( wrong_keyset_id. into ( ) ) ,
1875+ use_existing_key_tag : true ,
1876+ ..Default :: default ( )
1877+ } ;
1878+
1879+ let request = tonic:: Request :: new ( KeyGenRequest {
1880+ request_id : Some ( key_id. into ( ) ) ,
1881+ params : Some ( FheParameter :: Test as i32 ) ,
1882+ preproc_id : Some ( prep_id. into ( ) ) ,
1883+ domain : Some ( domain) ,
1884+ keyset_config : Some ( keyset_config) ,
1885+ keyset_added_info : Some ( keyset_added_info) ,
1886+ context_id : Some ( ( * DEFAULT_MPC_CONTEXT ) . into ( ) ) ,
1887+ epoch_id : None ,
1888+ } ) ;
1889+
1890+ let res = kg. key_gen ( request) . await . unwrap_err ( ) ;
1891+ assert_eq ! ( res. code( ) , tonic:: Code :: Internal ) ;
1892+
1893+ assert ! ( res
1894+ . internal_err( )
1895+ . to_string( )
1896+ . contains( ERR_FAILED_TO_READ_EXISTING_TAG ) ) ;
1897+ }
1898+
18031899 #[ tokio:: test]
18041900 async fn sunshine ( ) {
18051901 let ( prep_ids, kg) = setup_key_generator :: <
0 commit comments