Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
2abb882
chore: upgrade to 2024 edition
dvdplm Mar 31, 2026
0a415e5
chore: mechanical clippy fixes, collapsing multi-level if-let-if-clauses
dvdplm Mar 31, 2026
3b6f2a2
chore: more collapsed if-let-if chains
dvdplm Mar 31, 2026
81dfe1c
chore: formatting
dvdplm Mar 31, 2026
f80527a
chore: fixing lifetime mistak
dvdplm Apr 1, 2026
5ffa30c
Merge branch 'main' into dvdplm/chore/update-to-edition-2024
dvdplm Apr 2, 2026
d8f8835
chore: formatting
dvdplm Apr 2, 2026
aa36b4f
chore: sort out merge issues
dvdplm Apr 2, 2026
8ffcf38
chore: clippy --fix
dvdplm Apr 2, 2026
d26e8fd
chore: formatting, again
dvdplm Apr 2, 2026
aacde2a
Merge branch 'main' into dvdplm/chore/update-to-edition-2024
dvdplm Apr 2, 2026
fca2b98
chore: formatting after merge
dvdplm Apr 2, 2026
3cf386b
Merge branch 'dvdplm/chore/update-to-edition-2024'
dvdplm Apr 2, 2026
a069312
Merge branch 'main' of github.com:zama-ai/kms
dvdplm Apr 2, 2026
50b0469
Merge branch 'main' of github.com:zama-ai/kms
dvdplm Apr 7, 2026
ab07291
Merge branch 'main' of github.com:zama-ai/kms
dvdplm Apr 7, 2026
6853f46
Merge branch 'main' of github.com:zama-ai/kms
dvdplm Apr 7, 2026
9457d2e
Merge branch 'main' of github.com:zama-ai/kms
dvdplm Apr 8, 2026
176bae0
Merge branch 'main' into dvdplm/chore/make-compressed-keys-the-default
dvdplm Apr 10, 2026
f99d5e5
chore: switch the proto file to use compressed keys as the new default
dvdplm Apr 10, 2026
af5d2dd
chore: generate compressed keys by default
dvdplm Apr 10, 2026
4004c4c
chore: rename things so that `standard_keygen_config` is now `keygen_…
dvdplm Apr 10, 2026
a999161
chore: test fixes
dvdplm Apr 10, 2026
0e6ae5c
chore: more renaming noise
dvdplm Apr 10, 2026
1e383a5
defensive coding to cope with pre-existing keys
dvdplm Apr 10, 2026
19d1370
cleanup both compressed and uncompressed keys
dvdplm Apr 10, 2026
666710a
flipped defaults
dvdplm Apr 10, 2026
540f26d
renaming noise
dvdplm Apr 10, 2026
e9e415d
sort out fhe key copying
dvdplm Apr 10, 2026
780e23e
Only include PrssSetup when we need it
dvdplm Apr 10, 2026
bbcdc15
testing utilities defaults to compressed keys
dvdplm Apr 10, 2026
3aee8de
key setup for central server
dvdplm Apr 10, 2026
3a351c5
dead code removed
dvdplm Apr 10, 2026
5932433
test tooling fixes
dvdplm Apr 10, 2026
c7aefcf
Make the --force flag work
dvdplm Apr 10, 2026
7691c5f
keygen config builders
dvdplm Apr 10, 2026
e153159
naming churn
dvdplm Apr 10, 2026
611df1a
Add new gen_key_set that generates compressed keys, then decompresses…
dvdplm Apr 10, 2026
94efe44
Resolve a TODO, add another
dvdplm Apr 10, 2026
35850d0
rename churn
dvdplm Apr 10, 2026
dc6309b
Flipp defaults and fix up docs
dvdplm Apr 10, 2026
84722a4
merge main
dvdplm Apr 13, 2026
44d0fca
chore: fix benchmarks
dvdplm Apr 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 56 additions & 31 deletions core-client/src/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,45 @@ use tfhe::{CompactPublicKey, ServerKey};
use tokio::task::JoinSet;
use tonic::transport::Channel;

/// Build a `KeySetConfig` from compressed and keyset_type parameters.
/// Returns `None` when both `compressed` is false and `keyset_type` is `None`.
pub(crate) fn build_keyset_config(
compressed: bool,
use_existing: bool,
) -> Option<kms_grpc::kms::v1::KeySetConfig> {
if compressed || use_existing {
Some(kms_grpc::kms::v1::KeySetConfig {
keyset_type: kms_grpc::kms::v1::KeySetType::Standard as i32,
standard_keyset_config: Some(kms_grpc::kms::v1::StandardKeySetConfig {
compute_key_type: 0, // CPU
secret_key_config: if use_existing {
kms_grpc::kms::v1::KeyGenSecretKeyConfig::UseExisting as i32
} else {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum PublicKeyConfig {
Compressed,
Uncompressed,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SecretKeyConfig {
GenerateAll,
UseExisting,
}

/// Build an explicit standard `KeySetConfig`.
pub(crate) fn build_standard_keyset_config(
public_key_config: PublicKeyConfig,
secret_key_config: SecretKeyConfig,
) -> kms_grpc::kms::v1::KeySetConfig {
kms_grpc::kms::v1::KeySetConfig {
keyset_type: kms_grpc::kms::v1::KeySetType::Standard as i32,
standard_keyset_config: Some(kms_grpc::kms::v1::StandardKeySetConfig {
compute_key_type: 0, // CPU
secret_key_config: match secret_key_config {
SecretKeyConfig::GenerateAll => {
kms_grpc::kms::v1::KeyGenSecretKeyConfig::GenerateAll as i32
},
compressed_key_config: if compressed {
kms_grpc::kms::v1::CompressedKeyConfig::CompressedAll.into()
} else {
kms_grpc::kms::v1::CompressedKeyConfig::CompressedNone.into()
},
}),
})
} else {
None
}
SecretKeyConfig::UseExisting => {
kms_grpc::kms::v1::KeyGenSecretKeyConfig::UseExisting as i32
}
},
compressed_key_config: match public_key_config {
PublicKeyConfig::Compressed => {
kms_grpc::kms::v1::CompressedKeyConfig::CompressedAll
}
PublicKeyConfig::Uncompressed => {
kms_grpc::kms::v1::CompressedKeyConfig::CompressedNone
}
}
.into(),
}),
}
}

Expand Down Expand Up @@ -79,7 +93,18 @@ pub(crate) async fn do_keygen(
// NOTE: If we do not use dummy_domain here, then
// this needs changing too in the KeyGenResult command.
let use_existing = shared_config.existing_keyset_id.is_some();
let keyset_config = build_keyset_config(shared_config.compressed, use_existing);
let keyset_config = Some(build_standard_keyset_config(
if shared_config.uncompressed {
PublicKeyConfig::Uncompressed
} else {
PublicKeyConfig::Compressed
},
if use_existing {
SecretKeyConfig::UseExisting
} else {
SecretKeyConfig::GenerateAll
},
));
let keyset_added_info =
shared_config
.existing_keyset_id
Expand Down Expand Up @@ -166,7 +191,7 @@ pub(crate) async fn do_keygen(
extra_data,
resp_response_vec,
cmd_conf.download_all,
shared_config.compressed,
shared_config.uncompressed,
)
.await?;

Expand All @@ -184,7 +209,7 @@ pub(crate) async fn fetch_and_check_keygen(
extra_data: Vec<u8>,
responses: Vec<KeyGenResult>,
download_all: bool,
compressed: bool,
uncompressed: bool,
) -> anyhow::Result<()> {
if responses.len() < num_expected_responses {
anyhow::bail!(
Expand All @@ -195,10 +220,10 @@ pub(crate) async fn fetch_and_check_keygen(
}

// Download the generated keys.
let key_types = if compressed {
vec![PubDataType::CompressedXofKeySet]
} else {
let key_types = if uncompressed {
vec![PubDataType::PublicKey, PubDataType::ServerKey]
} else {
vec![PubDataType::CompressedXofKeySet]
};

let party_confs = fetch_public_elements(
Expand All @@ -218,7 +243,7 @@ pub(crate) async fn fetch_and_check_keygen(
// Even if we did not download all keys, we still check that they are identical
// by checking all signatures against the first downloaded keyset.
// If all signatures match, then all keys must be identical.
if compressed {
if !uncompressed {
let compressed_keyset: tfhe::xof_key_set::CompressedXofKeySet =
load_material_from_pub_storage(
Some(destination_prefix),
Expand Down
113 changes: 62 additions & 51 deletions core-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,11 +562,11 @@ pub struct CipherParameters {
#[serde(skip_serializing, skip_deserializing)]
#[clap(long, short = 'p', default_value_t = 0)]
pub parallel_requests: usize,
/// Whether the key was generated as a compressed keyset (CompressedXofKeySet).
/// When true, fetches CompressedXofKeySet instead of PublicKey/ServerKey.
/// Use legacy uncompressed public key material (`PublicKey` + `ServerKey`).
/// By default the client uses `CompressedXofKeySet`.
#[serde(skip_serializing, skip_deserializing)]
#[clap(long, default_value_t = false)]
pub compressed_keys: bool,
#[clap(long, short = 'u', default_value_t = false)]
pub uncompressed_keys: bool,
/// Optional extra data (hex-encoded) to include in the request.
/// Can optionally have a "0x" prefix.
#[serde(skip_serializing, skip_deserializing)]
Expand Down Expand Up @@ -607,9 +607,9 @@ pub struct CipherWithParams {

#[derive(Args, Debug, Clone, Default)]
pub struct SharedKeyGenParameters {
/// Generate compressed keys using XOF-seeded compression
#[clap(long, short = 'c', default_value_t = false)]
pub compressed: bool,
/// Generate legacy uncompressed public key material instead of the default compressed keyset.
#[clap(long, short = 'u', default_value_t = false)]
pub uncompressed: bool,
/// Existing keyset ID to reuse all secret shares from.
/// When set, generates new public keys from existing private key shares
/// instead of running full distributed keygen.
Expand Down Expand Up @@ -728,8 +728,9 @@ pub struct ResultParameters {
pub struct KeyGenResultParameters {
#[clap(long, short = 'i')]
pub request_id: RequestId,
#[clap(long, default_value_t = false)]
pub compressed: bool,
/// Fetch legacy uncompressed public key material instead of the default compressed keyset.
#[clap(long, short = 'u', default_value_t = false)]
pub uncompressed: bool,
}

#[derive(Debug, Parser, Clone)]
Expand Down Expand Up @@ -831,9 +832,9 @@ pub struct KeyGenPreprocParameters {
/// Defaults to the default epoch if not specified.
#[clap(long)]
pub epoch_id: Option<EpochId>,
/// Generate compressed keys using XOF-seeded compression
#[clap(long, short = 'c', default_value_t = false)]
pub compressed: bool,
/// Generate legacy uncompressed public key material instead of the default compressed keyset.
#[clap(long, short = 'u', default_value_t = false)]
pub uncompressed: bool,
/// Do preprocessing that's needed to generate a key from existing shares.
#[clap(long, default_value_t = false)]
pub from_existing_shares: bool,
Expand Down Expand Up @@ -1247,46 +1248,45 @@ pub async fn fetch_ctxt_from_file(
))
}

/// Try to fetch keys for the given key ID, auto-detecting whether they are regular or compressed.
/// Try to fetch keys for the given key ID, auto-detecting whether they use the default
/// compressed storage or the legacy uncompressed layout.
///
/// If `compressed_keys` is explicitly `true`, fetches `[CompressedXofKeySet]` only.
/// Otherwise, tries `[PublicKey, ServerKey]` first; on failure, falls back to `[CompressedXofKeySet]`.
/// Returns the fetched party confs and a boolean indicating whether compressed keys were found.
/// If `uncompressed_keys` is explicitly `true`, fetches `[PublicKey, ServerKey]` only.
/// Otherwise, tries `[CompressedXofKeySet]` first; on failure, falls back to
/// `[PublicKey, ServerKey]`.
/// Returns the fetched party confs and a boolean indicating whether uncompressed keys were found.
async fn fetch_keys_auto_detect(
key_id: &str,
compressed_keys: bool,
uncompressed_keys: bool,
cc_conf: &CoreClientConfig,
destination_prefix: &Path,
) -> anyhow::Result<(Vec<CoreConf>, bool)> {
let compressed_key_types = vec![PubDataType::CompressedXofKeySet];
let key_types = vec![PubDataType::PublicKey, PubDataType::ServerKey];

if compressed_keys {
let confs = fetch_public_elements(
key_id,
&compressed_key_types,
cc_conf,
destination_prefix,
false,
)
.await?;
if uncompressed_keys {
let confs =
fetch_public_elements(key_id, &key_types, cc_conf, destination_prefix, false).await?;
return Ok((confs, true));
}

let key_types = vec![PubDataType::PublicKey, PubDataType::ServerKey];
match fetch_public_elements(key_id, &key_types, cc_conf, destination_prefix, false).await {
match fetch_public_elements(
key_id,
&compressed_key_types,
cc_conf,
destination_prefix,
false,
)
.await
{
Ok(confs) => Ok((confs, false)),
Err(_) => {
tracing::info!(
"Regular keys [PublicKey, ServerKey] not found, trying CompressedXofKeySet..."
"CompressedXofKeySet not found, trying legacy [PublicKey, ServerKey]..."
);
let confs = fetch_public_elements(
key_id,
&compressed_key_types,
cc_conf,
destination_prefix,
false,
)
.await?;
let confs =
fetch_public_elements(key_id, &key_types, cc_conf, destination_prefix, false)
.await?;
Ok((confs, true))
}
}
Expand Down Expand Up @@ -1338,7 +1338,7 @@ pub async fn encrypt(
compression: !cipher_params.no_compression,
precompute_sns: !cipher_params.no_precompute_sns,
},
cipher_params.compressed_keys,
!cipher_params.uncompressed_keys,
)
.await;

Expand Down Expand Up @@ -1646,9 +1646,9 @@ pub async fn execute_cmd(
}
CipherArguments::FromArgs(cipher_parameters) => {
//Only need to fetch tfhe keys if we are not sourcing the ctxt from file
let (party_confs, detected_compressed) = fetch_keys_auto_detect(
let (party_confs, detected_uncompressed) = fetch_keys_auto_detect(
&cipher_parameters.key_id.as_str(),
cipher_parameters.compressed_keys,
cipher_parameters.uncompressed_keys,
&cc_conf,
destination_prefix,
)
Expand All @@ -1663,7 +1663,7 @@ pub async fn execute_cmd(
.as_str(),
);
let mut cipher_parameters = cipher_parameters.clone();
cipher_parameters.compressed_keys = detected_compressed;
cipher_parameters.uncompressed_keys = detected_uncompressed;
encrypt(destination_prefix, storage_prefix, cipher_parameters).await?
}
};
Expand Down Expand Up @@ -1723,9 +1723,9 @@ pub async fn execute_cmd(
}
CipherArguments::FromArgs(cipher_parameters) => {
//Only need to fetch tfhe keys if we are not sourcing the ctxt from file
let (party_confs, detected_compressed) = fetch_keys_auto_detect(
let (party_confs, detected_uncompressed) = fetch_keys_auto_detect(
&cipher_parameters.key_id.as_str(),
cipher_parameters.compressed_keys,
cipher_parameters.uncompressed_keys,
&cc_conf,
destination_prefix,
)
Expand All @@ -1740,7 +1740,7 @@ pub async fn execute_cmd(
.as_str(),
);
let mut cipher_parameters = cipher_parameters.clone();
cipher_parameters.compressed_keys = detected_compressed;
cipher_parameters.uncompressed_keys = detected_uncompressed;
encrypt(destination_prefix, storage_prefix, cipher_parameters).await?
}
};
Expand Down Expand Up @@ -1894,13 +1894,24 @@ pub async fn execute_cmd(
CCCommand::PreprocKeyGen(KeyGenPreprocParameters {
context_id,
epoch_id,
compressed,
uncompressed,
from_existing_shares,
}) => {
let mut internal_client = internal_client.unwrap();
tracing::info!("Preprocessing with parameter {}.", fhe_params.as_str_name());

let keyset_config = keygen::build_keyset_config(*compressed, *from_existing_shares);
let keyset_config = Some(keygen::build_standard_keyset_config(
if *uncompressed {
keygen::PublicKeyConfig::Uncompressed
} else {
keygen::PublicKeyConfig::Compressed
},
if *from_existing_shares {
keygen::SecretKeyConfig::UseExisting
} else {
keygen::SecretKeyConfig::GenerateAll
},
));
let req_id = do_preproc(
&mut internal_client,
&core_endpoints_req,
Expand Down Expand Up @@ -1947,9 +1958,9 @@ pub async fn execute_cmd(
vec![(None, String::new())]
}
CCCommand::Encrypt(cipher_parameters) => {
let (party_confs, detected_compressed) = fetch_keys_auto_detect(
let (party_confs, detected_uncompressed) = fetch_keys_auto_detect(
&cipher_parameters.key_id.as_str(),
cipher_parameters.compressed_keys,
cipher_parameters.uncompressed_keys,
&cc_conf,
destination_prefix,
)
Expand All @@ -1964,7 +1975,7 @@ pub async fn execute_cmd(
.as_str(),
);
let mut cipher_parameters = cipher_parameters.clone();
cipher_parameters.compressed_keys = detected_compressed;
cipher_parameters.uncompressed_keys = detected_uncompressed;
encrypt(destination_prefix, storage_prefix, cipher_parameters).await?;
vec![(None, "Encryption generated".to_string())]
}
Expand Down Expand Up @@ -2001,7 +2012,7 @@ pub async fn execute_cmd(
vec![],
resp_response_vec,
cmd_config.download_all,
result_parameters.compressed,
result_parameters.uncompressed,
)
.await?;
vec![(Some(req_id), "keygen result queried".to_string())]
Expand Down Expand Up @@ -2034,7 +2045,7 @@ pub async fn execute_cmd(
vec![],
resp_response_vec,
cmd_config.download_all,
result_parameters.compressed,
result_parameters.uncompressed,
)
.await?;
vec![(Some(req_id), "insecure keygen result queried".to_string())]
Expand Down
Loading
Loading