Skip to content

Commit cdf6375

Browse files
committed
chore: integration test
1 parent ef66e95 commit cdf6375

File tree

4 files changed

+258
-144
lines changed

4 files changed

+258
-144
lines changed

core-client/src/keygen.rs

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,34 @@ use tfhe::{CompactPublicKey, ServerKey};
2323
use tokio::task::JoinSet;
2424
use tonic::transport::Channel;
2525

26+
/// Build a `KeySetConfig` from compressed and keyset_type parameters.
27+
/// Returns `None` when both `compressed` is false and `keyset_type` is `None`.
28+
pub(crate) fn build_keyset_config(
29+
compressed: bool,
30+
use_existing: bool,
31+
) -> Option<kms_grpc::kms::v1::KeySetConfig> {
32+
if compressed || use_existing {
33+
Some(kms_grpc::kms::v1::KeySetConfig {
34+
keyset_type: kms_grpc::kms::v1::KeySetType::Standard as i32,
35+
standard_keyset_config: Some(kms_grpc::kms::v1::StandardKeySetConfig {
36+
compute_key_type: 0, // CPU
37+
secret_key_config: if use_existing {
38+
kms_grpc::kms::v1::KeyGenSecretKeyConfig::UseExisting as i32
39+
} else {
40+
kms_grpc::kms::v1::KeyGenSecretKeyConfig::GenerateAll as i32
41+
},
42+
compressed_key_config: if compressed {
43+
kms_grpc::kms::v1::CompressedKeyConfig::CompressedAll.into()
44+
} else {
45+
kms_grpc::kms::v1::CompressedKeyConfig::CompressedNone.into()
46+
},
47+
}),
48+
})
49+
} else {
50+
None
51+
}
52+
}
53+
2654
#[allow(clippy::too_many_arguments)]
2755
pub(crate) async fn do_keygen(
2856
internal_client: &mut Client,
@@ -49,34 +77,24 @@ pub(crate) async fn do_keygen(
4977

5078
// NOTE: If we do not use dummy_domain here, then
5179
// this needs changing too in the KeyGenResult command.
52-
let keyset_config = if shared_config.compressed || shared_config.keyset_type.is_some() {
53-
Some(kms_grpc::kms::v1::KeySetConfig {
54-
keyset_type: shared_config
55-
.keyset_type
56-
.clone()
57-
.map(|x| kms_grpc::kms::v1::KeySetType::from(x) as i32)
58-
.unwrap_or(kms_grpc::kms::v1::KeySetType::Standard as i32),
59-
standard_keyset_config: Some(kms_grpc::kms::v1::StandardKeySetConfig {
60-
compute_key_type: 0, // CPU
61-
secret_key_config: 0, // Generate all secret keys
62-
compressed_key_config: if shared_config.compressed {
63-
kms_grpc::kms::v1::CompressedKeyConfig::CompressedAll.into()
64-
} else {
65-
kms_grpc::kms::v1::CompressedKeyConfig::CompressedNone.into()
66-
},
67-
}),
68-
})
69-
} else {
70-
None
71-
};
80+
let use_existing = shared_config.existing_keyset_id.is_some();
81+
let keyset_config = build_keyset_config(shared_config.compressed, use_existing);
82+
let keyset_added_info =
83+
shared_config
84+
.existing_keyset_id
85+
.map(|id| kms_grpc::kms::v1::KeySetAddedInfo {
86+
existing_keyset_id: Some(id.into()),
87+
existing_epoch_id: shared_config.existing_epoch_id.map(Into::into),
88+
..Default::default()
89+
});
7290
let dkg_req = internal_client.key_gen_request(
7391
&req_id,
7492
&preproc_id,
7593
shared_config.context_id.as_ref(),
7694
shared_config.epoch_id.as_ref(),
7795
Some(param),
7896
keyset_config,
79-
None,
97+
keyset_added_info,
8098
dummy_domain(),
8199
)?;
82100

@@ -447,6 +465,7 @@ pub(crate) async fn do_preproc(
447465
fhe_params: FheParameter,
448466
context_id: Option<&ContextId>,
449467
epoch_id: Option<&EpochId>,
468+
keyset_config: Option<kms_grpc::kms::v1::KeySetConfig>,
450469
) -> anyhow::Result<RequestId> {
451470
let req_id = RequestId::new_random(rng);
452471

@@ -459,9 +478,9 @@ pub(crate) async fn do_preproc(
459478
Some(fhe_params),
460479
context_id,
461480
epoch_id,
462-
None,
481+
keyset_config,
463482
&domain,
464-
)?; //TODO keyset config
483+
)?;
465484

466485
// make parallel requests by calling insecure keygen in a thread
467486
let mut req_tasks = JoinSet::new();

core-client/src/lib.rs

Lines changed: 79 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::keygen::{
2626
use crate::mpc_context::{do_destroy_mpc_context, do_new_mpc_context};
2727
use crate::mpc_epoch::{do_destroy_mpc_epoch, do_new_epoch};
2828
use aes_prng::AesRng;
29-
use clap::{Args, Parser, Subcommand, ValueEnum};
29+
use clap::{Args, Parser, Subcommand};
3030
use core::str;
3131
use kms_grpc::identifiers::EpochId;
3232
use kms_grpc::kms::v1::{CiphertextFormat, FheParameter, TypedCiphertext, TypedPlaintext};
@@ -562,37 +562,19 @@ pub struct CipherWithParams {
562562
cipher: Vec<u8>,
563563
}
564564

565-
/// Keyset type for key generation, matching the KeySetType in the protofile.
566-
///
567-
/// It only supports the standard keyset type for now, which is to generate the full keyset
568-
/// rather than individual keys from a standard keyset.
569-
#[derive(ValueEnum, Debug, Clone, Default)]
570-
pub enum KeySetType {
571-
#[default]
572-
Standard,
573-
// TODO(#2799)
574-
// DecompressionOnly, // we'll support this in the future
575-
}
576-
577-
impl From<KeySetType> for kms_grpc::kms::v1::KeySetType {
578-
fn from(value: KeySetType) -> Self {
579-
match value {
580-
KeySetType::Standard => kms_grpc::kms::v1::KeySetType::Standard,
581-
}
582-
}
583-
}
584-
585565
#[derive(Args, Debug, Clone, Default)]
586566
pub struct SharedKeyGenParameters {
587-
/// Keyset type for key generation (e.g., "standard")
588-
#[clap(value_enum, long, short = 't')]
589-
pub keyset_type: Option<KeySetType>,
590567
/// Generate compressed keys using XOF-seeded compression
591568
#[clap(long, short = 'c', default_value_t = false)]
592569
pub compressed: bool,
593-
// TODO(#2799)
594-
// #[command(flatten)]
595-
// pub keyset_added_info: Option<KeySetAddedInfo>,
570+
/// Existing keyset ID to reuse all secret shares from.
571+
/// When set, generates new public keys from existing private key shares
572+
/// instead of running full distributed keygen.
573+
#[clap(long)]
574+
pub existing_keyset_id: Option<RequestId>,
575+
/// Epoch ID for the existing keyset (optional, defaults to the request's epoch).
576+
#[clap(long)]
577+
pub existing_epoch_id: Option<EpochId>,
596578
pub context_id: Option<ContextId>,
597579
pub epoch_id: Option<EpochId>,
598580
}
@@ -746,6 +728,12 @@ pub struct KeyGenPreprocParameters {
746728
/// Defaults to the default epoch if not specified.
747729
#[clap(long)]
748730
pub epoch_id: Option<EpochId>,
731+
/// Generate compressed keys using XOF-seeded compression
732+
#[clap(long, short = 'c', default_value_t = false)]
733+
pub compressed: bool,
734+
/// Do preprocessing that's needed to generate a key from existing shares.
735+
#[clap(long, default_value_t = false)]
736+
pub from_existing_shares: bool,
749737
}
750738

751739
#[derive(Debug, Parser, Clone)]
@@ -899,6 +887,51 @@ pub async fn fetch_ctxt_from_file(
899887
}
900888

901889
/// encrypt a given value and return the ciphertext
890+
/// Try to fetch keys for the given key ID, auto-detecting whether they are regular or compressed.
891+
///
892+
/// If `compressed_keys` is explicitly `true`, fetches `[CompressedXofKeySet]` only.
893+
/// Otherwise, tries `[PublicKey, ServerKey]` first; on failure, falls back to `[CompressedXofKeySet]`.
894+
/// Returns the fetched party confs and a boolean indicating whether compressed keys were found.
895+
async fn fetch_keys_auto_detect(
896+
key_id: &str,
897+
compressed_keys: bool,
898+
cc_conf: &CoreClientConfig,
899+
destination_prefix: &Path,
900+
) -> anyhow::Result<(Vec<CoreConf>, bool)> {
901+
let compressed_key_types = vec![PubDataType::CompressedXofKeySet];
902+
903+
if compressed_keys {
904+
let confs = fetch_public_elements(
905+
key_id,
906+
&compressed_key_types,
907+
cc_conf,
908+
destination_prefix,
909+
false,
910+
)
911+
.await?;
912+
return Ok((confs, true));
913+
}
914+
915+
let key_types = vec![PubDataType::PublicKey, PubDataType::ServerKey];
916+
match fetch_public_elements(key_id, &key_types, cc_conf, destination_prefix, false).await {
917+
Ok(confs) => Ok((confs, false)),
918+
Err(_) => {
919+
tracing::info!(
920+
"Regular keys [PublicKey, ServerKey] not found, trying CompressedXofKeySet..."
921+
);
922+
let confs = fetch_public_elements(
923+
key_id,
924+
&compressed_key_types,
925+
cc_conf,
926+
destination_prefix,
927+
false,
928+
)
929+
.await?;
930+
Ok((confs, true))
931+
}
932+
}
933+
}
934+
902935
/// parameters:
903936
/// - `keys_folder`: the root of the storage of the core client
904937
/// - `party_id`: the 1-indexed ID of the KMS core whose public keys we will use (should not matter as long as the server is online)
@@ -1231,9 +1264,6 @@ pub async fn execute_cmd(
12311264

12321265
let kms_addrs = Arc::new(addr_vec);
12331266

1234-
let key_types = vec![PubDataType::PublicKey, PubDataType::ServerKey];
1235-
let compressed_key_types = vec![PubDataType::CompressedXofKeySet];
1236-
12371267
let command_timer_start = tokio::time::Instant::now();
12381268
// Execute the command
12391269
let res = match command {
@@ -1257,18 +1287,11 @@ pub async fn execute_cmd(
12571287
}
12581288
CipherArguments::FromArgs(cipher_parameters) => {
12591289
//Only need to fetch tfhe keys if we are not sourcing the ctxt from file
1260-
let fetch_types = if cipher_parameters.compressed_keys {
1261-
&compressed_key_types
1262-
} else {
1263-
&key_types
1264-
};
1265-
tracing::info!("Fetching keys {fetch_types:?}. ({command:?})");
1266-
let party_confs = fetch_public_elements(
1290+
let (party_confs, detected_compressed) = fetch_keys_auto_detect(
12671291
&cipher_parameters.key_id.as_str(),
1268-
fetch_types,
1292+
cipher_parameters.compressed_keys,
12691293
&cc_conf,
12701294
destination_prefix,
1271-
false,
12721295
)
12731296
.await?;
12741297
let storage_prefix = Some(
@@ -1280,12 +1303,9 @@ pub async fn execute_cmd(
12801303
.object_folder
12811304
.as_str(),
12821305
);
1283-
encrypt(
1284-
destination_prefix,
1285-
storage_prefix,
1286-
cipher_parameters.clone(),
1287-
)
1288-
.await?
1306+
let mut cipher_parameters = cipher_parameters.clone();
1307+
cipher_parameters.compressed_keys = detected_compressed;
1308+
encrypt(destination_prefix, storage_prefix, cipher_parameters).await?
12891309
}
12901310
};
12911311

@@ -1342,18 +1362,11 @@ pub async fn execute_cmd(
13421362
}
13431363
CipherArguments::FromArgs(cipher_parameters) => {
13441364
//Only need to fetch tfhe keys if we are not sourcing the ctxt from file
1345-
let fetch_types = if cipher_parameters.compressed_keys {
1346-
&compressed_key_types
1347-
} else {
1348-
&key_types
1349-
};
1350-
tracing::info!("Fetching keys {fetch_types:?}. ({command:?})");
1351-
let party_confs = fetch_public_elements(
1365+
let (party_confs, detected_compressed) = fetch_keys_auto_detect(
13521366
&cipher_parameters.key_id.as_str(),
1353-
fetch_types,
1367+
cipher_parameters.compressed_keys,
13541368
&cc_conf,
13551369
destination_prefix,
1356-
false,
13571370
)
13581371
.await?;
13591372
let storage_prefix = Some(
@@ -1365,13 +1378,9 @@ pub async fn execute_cmd(
13651378
.object_folder
13661379
.as_str(),
13671380
);
1368-
1369-
encrypt(
1370-
destination_prefix,
1371-
storage_prefix,
1372-
cipher_parameters.clone(),
1373-
)
1374-
.await?
1381+
let mut cipher_parameters = cipher_parameters.clone();
1382+
cipher_parameters.compressed_keys = detected_compressed;
1383+
encrypt(destination_prefix, storage_prefix, cipher_parameters).await?
13751384
}
13761385
};
13771386

@@ -1505,10 +1514,13 @@ pub async fn execute_cmd(
15051514
CCCommand::PreprocKeyGen(KeyGenPreprocParameters {
15061515
context_id,
15071516
epoch_id,
1517+
compressed,
1518+
from_existing_shares,
15081519
}) => {
15091520
let mut internal_client = internal_client.unwrap();
15101521
tracing::info!("Preprocessing with parameter {}.", fhe_params.as_str_name());
15111522

1523+
let keyset_config = keygen::build_keyset_config(*compressed, *from_existing_shares);
15121524
let req_id = do_preproc(
15131525
&mut internal_client,
15141526
&core_endpoints_req,
@@ -1518,6 +1530,7 @@ pub async fn execute_cmd(
15181530
fhe_params,
15191531
context_id.as_ref(),
15201532
epoch_id.as_ref(),
1533+
keyset_config,
15211534
)
15221535
.await?;
15231536
vec![(Some(req_id), "preproc done".to_string())]
@@ -1554,13 +1567,11 @@ pub async fn execute_cmd(
15541567
vec![(None, String::new())]
15551568
}
15561569
CCCommand::Encrypt(cipher_parameters) => {
1557-
tracing::info!("Fetching keys {key_types:?}. ({command:?})");
1558-
let party_confs = fetch_public_elements(
1570+
let (party_confs, detected_compressed) = fetch_keys_auto_detect(
15591571
&cipher_parameters.key_id.as_str(),
1560-
&key_types,
1572+
cipher_parameters.compressed_keys,
15611573
&cc_conf,
15621574
destination_prefix,
1563-
false,
15641575
)
15651576
.await?;
15661577
let storage_prefix = Some(
@@ -1572,12 +1583,9 @@ pub async fn execute_cmd(
15721583
.object_folder
15731584
.as_str(),
15741585
);
1575-
encrypt(
1576-
destination_prefix,
1577-
storage_prefix,
1578-
cipher_parameters.clone(),
1579-
)
1580-
.await?;
1586+
let mut cipher_parameters = cipher_parameters.clone();
1587+
cipher_parameters.compressed_keys = detected_compressed;
1588+
encrypt(destination_prefix, storage_prefix, cipher_parameters).await?;
15811589
vec![(None, "Encryption generated".to_string())]
15821590
}
15831591
CCCommand::PreprocKeyGenResult(result_parameters) => {

0 commit comments

Comments
 (0)