diff --git a/frontends/concrete-rust/concrete-keygen/Cargo.lock b/frontends/concrete-rust/concrete-keygen/Cargo.lock index 62c34b2856..0cd345939d 100644 --- a/frontends/concrete-rust/concrete-keygen/Cargo.lock +++ b/frontends/concrete-rust/concrete-keygen/Cargo.lock @@ -436,6 +436,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", + "libz-rs-sys", "miniz_oxide", ] @@ -568,6 +569,15 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +[[package]] +name = "libz-rs-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" +dependencies = [ + "zlib-rs", +] + [[package]] name = "lockfree-object-pool" version = "0.1.6" @@ -852,9 +862,9 @@ dependencies = [ [[package]] name = "tfhe" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f0fea7aede7b88ff9cc1b697a7427323b37bbcd7eff543f5ceeb8c52d78c820" +checksum = "11e74f14e2812ac6a2fe516f2c090431b614d589c24c6e3bca3fe763d4de928e" dependencies = [ "aligned-vec", "bincode", @@ -879,9 +889,9 @@ dependencies = [ [[package]] name = "tfhe-csprng" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1a0b71a91c9e36e9e1e8e6a528a056086b01e193e173472ae59cfaac56fdad" +checksum = "06450766ae375cd305281c5d691a41d8877e40d3cc7510a33244775d62f60ad4" dependencies = [ "aes", "libc", @@ -916,9 +926,9 @@ dependencies = [ [[package]] name = "tfhe-versionable" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3a2785eb45d8bbb1cf26eb19277a7398b2fc818ad5136b57f5ee280bf0c430" +checksum = "d853f69b5e95332fa76424525cc403f34645838927212609776e26f8beab52c5" dependencies = [ "aligned-vec", "num-complex", @@ -928,9 +938,9 @@ dependencies = [ [[package]] name = "tfhe-versionable-derive" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f221336486fd08a09dcfd035845f18ae4b742bbf6b6c21dc2121f6b3322705f7" +checksum = "d916462cab867a9d95fe6097ed20ed491fc37cdac766e8c736b84b09859d4afd" dependencies = [ "proc-macro2", "quote", @@ -1191,16 +1201,15 @@ dependencies = [ [[package]] name = "zip" -version = "2.6.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dcb24d0152526ae49b9b96c1dcf71850ca1e0b882e4e28ed898a93c41334744" +checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" dependencies = [ "aes", "arbitrary", "bzip2", "constant_time_eq", "crc32fast", - "crossbeam-utils", "deflate64", "flate2", "getrandom 0.3.2", @@ -1217,6 +1226,12 @@ dependencies = [ "zstd", ] +[[package]] +name = "zlib-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" + [[package]] name = "zopfli" version = "0.8.1" diff --git a/frontends/concrete-rust/concrete-keygen/Cargo.toml b/frontends/concrete-rust/concrete-keygen/Cargo.toml index 43a136e8b0..4f3becacf7 100644 --- a/frontends/concrete-rust/concrete-keygen/Cargo.toml +++ b/frontends/concrete-rust/concrete-keygen/Cargo.toml @@ -8,7 +8,6 @@ readme = "./README.md" homepage = "https://zama.ai/" documentation = "https://docs.zama.ai/concrete" repository = "https://github.com/zama-ai/concrete" -license = "BSD-3-Clause-Clear" license-file = "../../../LICENSE.txt" description = "concrete-keygen allows you to manage keys to be used in Zama's Concrete framework. It also provides a way to manage keys in Wasm." @@ -44,8 +43,9 @@ wasm-bindgen-futures = { version = "0.4.50", optional = true } web-sys = { version = "0.3.77", features = ["std", "MessagePort"], optional = true } serde-json = { package = "serde_json", version = "1.0", optional = true } -tfhe = { version = "1.1" } -tfhe-csprng = { version = "0.5" } + +tfhe = { version = "1.3" } +tfhe-csprng = { version = "0.6" } [features] diff --git a/frontends/concrete-rust/concrete-keygen/src/keyasm.rs b/frontends/concrete-rust/concrete-keygen/src/keyasm.rs index 950f4456af..e7f7813e6f 100644 --- a/frontends/concrete-rust/concrete-keygen/src/keyasm.rs +++ b/frontends/concrete-rust/concrete-keygen/src/keyasm.rs @@ -2,7 +2,9 @@ use std::io::Read; use concrete_keygen::concrete_protocol_capnp; use tfhe::core_crypto::prelude::{ - allocate_and_assemble_lwe_bootstrap_key_from_chunks, LweBootstrapKeyChunk, + allocate_and_assemble_lwe_bootstrap_key_from_chunks, + allocate_and_assemble_lwe_keyswitch_key_from_chunks, LweBootstrapKeyChunk, + LweKeyswitchKeyChunk, }; use tfhe::safe_serialization::safe_deserialize; @@ -11,12 +13,16 @@ use zip::result::ZipResult; use zip::ZipArchive; const KEYSET_INFO_FILENAME: &str = "keyset_info.capnp"; -const KEYSET_FILENAME: &str = "keyset_no_bsk.capnp"; +const KEYSET_FILENAME: &str = "keyset_no_bsk_no_ksk.capnp"; fn bsk_chunk_filename(bsk_id: u32, chunk_id: u32) -> String { format!("bsk_{}_chunk_{}", bsk_id, chunk_id) } +fn ksk_chunk_filename(ksk_id: u32, chunk_id: u32) -> String { + format!("ksk_{}_chunk_{}", ksk_id, chunk_id) +} + fn new_zip_archive(path: &str) -> ZipResult> { let file = std::fs::File::open(path).unwrap(); ZipArchive::new(file) @@ -90,11 +96,59 @@ fn assemble_keyset_from_zip( let keyset = concrete_protocol_capnp::get_reader_from_message(&keyset_message).unwrap(); // Add BSks to keyset - let keyset_with_bsks = concrete_keygen::add_bsk_keys_to_keyset(keyset, bsk_readers); + let mut keyset_with_bsks = concrete_keygen::add_bsk_keys_to_keyset(keyset, bsk_readers); + + // Read and assemble keyswitch keys + let ksk_ids: Vec = keyset_info_proto + .get_lwe_keyswitch_keys() + .unwrap() + .into_iter() + .map(|ksk| ksk.get_id()) + .collect(); + let mut ksk_protos = Vec::new(); + for ksk_id in ksk_ids { + let mut chunks = Vec::new(); + let mut chunk_id = 0; + + loop { + let chunk_filename = ksk_chunk_filename(ksk_id, chunk_id); + let mut archive = new_zip_archive(zip_path).unwrap(); + if let Ok(mut chunk_file) = archive.by_name(&chunk_filename) { + let mut chunk_buffer = Vec::new(); + chunk_file.read_to_end(&mut chunk_buffer).unwrap(); + let serialized_size_limit = 10_000_000_000; + let chunk: LweKeyswitchKeyChunk> = + safe_deserialize(chunk_buffer.as_slice(), serialized_size_limit).unwrap(); + chunks.push(chunk); + chunk_id += 1; + } else { + break; + }; + } + + let ksk = allocate_and_assemble_lwe_keyswitch_key_from_chunks(&chunks); + let bsk_proto = concrete_keygen::build_ksk_proto(keyset_info_proto, ksk_id, &ksk); + ksk_protos.push(bsk_proto); + } + + let ksk_readers = ksk_protos + .iter_mut() + .map(|ksk_proto| { + concrete_protocol_capnp::get_reader_from_builder(ksk_proto) + .expect("Failed to get ksk reader") + }) + .collect(); + + // Add KSKs to keyset + let keyset_with_bsks_and_ksks = concrete_keygen::add_ksk_keys_to_keyset( + concrete_protocol_capnp::get_reader_from_builder(&mut keyset_with_bsks) + .expect("Failed to get keyset reader"), + ksk_readers, + ); // Write the final keyset to the output file let mut output_file = std::fs::File::create(output_keyset_path).unwrap(); - capnp::serialize::write_message(&mut output_file, &keyset_with_bsks).unwrap(); + capnp::serialize::write_message(&mut output_file, &keyset_with_bsks_and_ksks).unwrap(); Ok(()) } diff --git a/frontends/concrete-rust/concrete-keygen/src/lib.rs b/frontends/concrete-rust/concrete-keygen/src/lib.rs index c7f8725378..5e79aa8095 100644 --- a/frontends/concrete-rust/concrete-keygen/src/lib.rs +++ b/frontends/concrete-rust/concrete-keygen/src/lib.rs @@ -22,6 +22,7 @@ //! - `generate_pksk`: Generates an `LwePrivateFunctionalPackingKeyswitchKeyList` with the specified parameters and random generator. //! - `get_lwe_secret_key_from_client_keyset`: Retrieves an LWE secret key from a client keyset based on the provided key ID. //! - `chunked_bsk_keygen`: Generates an LWE bootstrap key in chunks and sends them to a `MessagePort`. +//! - `chunked_ksk_keygen`: Generates an LWE keyswitch key in chunks and sends them to a `MessagePort`. //! - `generate_keyset`: Generates a Cap'n Proto message containing the keyset based on the provided keyset information and seeds. //! - `generate_keyset_from_buffers`: Reads keyset information from a file, generates the keyset, and writes it to an output file. //! - `get_client_keyset`: Extracts the client keyset from a keyset and returns it as a Cap'n Proto message builder. @@ -29,6 +30,8 @@ //! - `explain_keyset_info`: Explains a keyset info buffer in JSON representation. //! - `add_bsk_keys_to_keyset`: Adds a list of bootstrap keys to an existing keyset. //! - `add_bsk_keys_to_keyset_from_buffer`: Adds a list of bootstrap keys to an existing keyset buffer. +//! - `add_ksk_keys_to_keyset`: Adds a list of keyswitch keys to an existing keyset. +//! - `add_ksk_keys_to_keyset_from_buffer`: Adds a list of keyswitch keys to an existing keyset buffer. //! //! # Macros //! - `build_key`: A macro for building a Cap'n Proto message for a key with the specified key information and builder type. @@ -807,6 +810,172 @@ pub fn add_bsk_keys_to_keyset_from_buffers( serialize::write_message_to_words(&builder) } +/// Builds a Cap'n Proto message containing a keyswitch key. +/// +/// # Arguments +/// +/// * `keyset_info`: The keyset information. This is used to get info about the keyswitch key. +/// * `ksk_id`: The ID of the keyswitch key. +/// * `ksk`: The keyswitch key to be included in the message. +/// +/// # Returns +/// +/// A Cap'n Proto message builder containing the keyswitch key. +pub fn build_ksk_proto( + keyset_info: concrete_protocol_capnp::keyset_info::Reader, + ksk_id: u32, + ksk: &LweKeyswitchKey>, +) -> capnp::message::Builder { + let mut builder = capnp::message::Builder::new_default(); + let mut ksk_builder = + builder.init_root::(); + ksk_builder + .set_info( + keyset_info + .get_lwe_keyswitch_keys() + .expect_or_throw("Failed to get LWE keyswitch key info") + .get(ksk_id), + ) + .expect_or_throw("Failed to set keyswitch key info"); + ksk_builder + .set_payload( + vector_to_payload(ksk.as_view().into_container()) + .get_root_as_reader() + .expect_or_throw("Failed to get payload"), + ) + .expect_or_throw("Failed to set keyswitch key payload"); + builder +} + +/// Adds a list of keyswitch keys to an existing keyset. +/// +/// This function takes an existing keyset, a list of keyswitch keys, and constructs a new keyset +/// by adding the keyswitch keys to the existing keyset. +/// +/// # Arguments +/// +/// * `keyset`: The existing keyset to which the keyswitch keys will be added. +/// * `ksks`: A vector of keyswitch keys to be added. +/// +/// # Returns +/// +/// A new keyset with the additional keyswitch keys. +pub fn add_ksk_keys_to_keyset( + keyset: concrete_protocol_capnp::keyset::Reader, + ksks: Vec, +) -> capnp::message::Builder { + let mut builder = capnp::message::Builder::new_default(); + let mut new_keyset = builder.init_root::(); + + // Copy existing client and server keysets + new_keyset + .reborrow() + .set_client( + keyset + .get_client() + .expect_or_throw("Failed to get client keyset"), + ) + .expect_or_throw("Failed to set client keyset"); + let mut server_keyset = new_keyset.init_server(); + server_keyset + .reborrow() + .set_lwe_bootstrap_keys( + keyset + .get_server() + .expect_or_throw("Failed to get server keyset") + .get_lwe_bootstrap_keys() + .expect_or_throw("Failed to get LWE bootstrap keys"), + ) + .expect_or_throw("Failed to set LWE bootstrap keys"); + server_keyset + .reborrow() + .set_packing_keyswitch_keys( + keyset + .get_server() + .expect_or_throw("Failed to get server keyset") + .get_packing_keyswitch_keys() + .expect_or_throw("Failed to get packing keyswitch keys"), + ) + .expect_or_throw("Failed to set packing keyswitch keys"); + + // Initialize keyswitch keys with existing ones + let existing_ksk_keys = keyset + .get_server() + .expect_or_throw("Failed to get server keyset") + .get_lwe_keyswitch_keys() + .expect_or_throw("Failed to get LWE keyswitch keys"); + let mut new_ksk_keys = + server_keyset.init_lwe_keyswitch_keys(existing_ksk_keys.len() + ksks.len() as u32); + for ksk in existing_ksk_keys.iter() { + new_ksk_keys + .set_with_caveats( + ksk.get_info() + .expect_or_throw("Failed to get keyswitch key info") + .get_id(), + ksk, + ) + .expect_or_throw("Failed to set existing keyswitch key"); + } + // Add new bootstrap keys + for ksk in ksks.iter() { + new_ksk_keys + .set_with_caveats( + ksk.get_info() + .expect_or_throw("Failed to get keyswitch key info") + .get_id(), + *ksk, + ) + .expect_or_throw("Failed to set new keyswitch key"); + } + + builder +} + +/// Adds a list of keyswitch keys to an existing keyset using buffers. +/// +/// This function takes an existing keyset buffer, a list of keyswitch key buffers, and constructs a new keyset +/// by adding the keyswitch keys to the existing keyset. +/// +/// # Arguments +/// +/// * `keyset_buffer`: A byte slice containing the serialized existing keyset (Capnp). +/// * `ksk_buffers`: A vector of byte slices, each containing a serialized keyswitch key (Capnp). +/// +/// # Returns +/// +/// A `Vec` containing the serialized new keyset. +pub fn add_ksk_keys_to_keyset_from_buffers( + keyset_buffer: &[u8], + ksk_buffers: Vec<&[u8]>, +) -> Vec { + // Deserialize the keyset buffer + let keyset_message = + read_capnp_from_buffer(keyset_buffer).expect_or_throw("Failed to read keyset buffer"); + let keyset_reader = + get_reader_from_message(&keyset_message).expect_or_throw("Failed to read keyset reader"); + + // Deserialize each bootstrap key buffer + let ksks_readers = ksk_buffers + .iter() + .map(|bsk_buffer| { + read_capnp_from_buffer(bsk_buffer).expect_or_throw("Failed to read keyswitch key") + }) + .collect::>(); + let ksks = ksks_readers + .iter() + .map(|bsk_reader| { + get_reader_from_message(bsk_reader) + .expect_or_throw("Failed to get keyswitch key reader") + }) + .collect::>(); + + // Call the original function + let builder = add_ksk_keys_to_keyset(keyset_reader, ksks); + + // Serialize the result to a buffer + serialize::write_message_to_words(&builder) +} + /// Generates a Cap'n Proto message containing the keyset based on the provided keyset information and seeds. /// /// The function generates secret keys, bootstrap keys, keyswitch keys, and packing keyswitch keys based on the keyset information. @@ -1336,6 +1505,144 @@ pub mod wasm { total_len } + /// Generates an LWE keyswitch key in chunks. + /// + /// This function reads the keyset information, input secret key, and output secret key from byte buffers, + /// generates the LWE keyswitch key in chunks, and sends each chunk to a specified `MessagePort`. + /// + /// # Arguments + /// + /// * `keyset_info_buffer` - A byte slice containing the serialized keyset information (Capnp). + /// * `input_secret_key_buffer` - A byte slice containing the serialized input secret key (Capnp). + /// * `output_secret_key_buffer` - A byte slice containing the serialized output secret key (Capnp). + /// * `ksk_id` - The ID of the keyswitch key to generate. + /// * `enc_seed` - The seed for the encryption random generator (used if no compression). + /// * `compression_seed` - The seed for the compression random generator (used if seeded key). + /// * `chunk_size` - The size of each chunk to generate. + /// * `port` - The `MessagePort` to send the generated chunks to. + /// + /// # Returns + /// + /// The total length of the generated keyswitch key in bytes. + /// + /// # Panics + /// + /// This function will throw an exception if it fails to read the keyset info buffer, input secret key buffer, + /// or output secret key buffer, or if it fails to post a message to the specified `MessagePort`. + #[wasm_bindgen] + pub async fn chunked_ksk_keygen( + keyset_info_buffer: &[u8], + input_secret_key_buffer: &[u8], + output_secret_key_buffer: &[u8], + ksk_id: u32, + enc_seed: u128, + compression_seed: u128, + chunk_size: usize, + port: web_sys::MessagePort, + ) -> usize { + // deserialize inputs + let message = read_capnp_from_buffer(keyset_info_buffer) + .expect_throw("Failed to read keyset info buffer"); + let key_set_info = + get_reader_from_message::(&message) + .expect_throw("Failed to get root keyset info reader"); + let message = read_capnp_from_buffer(input_secret_key_buffer) + .expect_throw("Failed to read input secret key buffer"); + let input_sk_reader = get_reader_from_message(&message) + .expect_throw("Failed to get root input secret key reader"); + let message = read_capnp_from_buffer(output_secret_key_buffer) + .expect_throw("Failed to read output secret key buffer"); + let output_sk_reader = get_reader_from_message(&message) + .expect_throw("Failed to get root output secret key reader"); + + // Parameters + let ksk_info = key_set_info + .get_lwe_keyswitch_keys() + .expect_throw("Failed to get LWE keyswitch keys") + .get(ksk_id); + let params = ksk_info + .get_params() + .expect_throw("Failed to get keyswitch key parameters"); + let compression = ksk_info + .get_compression() + .expect_throw("Failed to get compression"); + + let decomp_base_log = DecompositionBaseLog(params.get_base_log() as usize); + let decomp_level_count = DecompositionLevelCount(params.get_level_count() as usize); + let glwe_noise_distribution = Gaussian::from_dispersion_parameter( + Variance::from_variance(params.get_variance()).get_standard_dev(), + 0.0, + ); + let ciphertext_modulus: CiphertextModulus = CiphertextModulus::new_native(); + + // Input and output secret keys + let input_lwe_secret_key = reader_to_lwe_secret_key(&input_sk_reader); + let output_lwe_secret_key = reader_to_lwe_secret_key(&output_sk_reader); + + let mut total_len = 0; + let mut seeder = new_seeder(); + let seeder = seeder.as_mut(); + match compression { + Compression::None => { + let mut encryption_generator = + EncryptionRandomGenerator::::new( + Seed(enc_seed), + seeder, + ); + let chunk_generator = LweKeyswitchKeyChunkGenerator::new( + &mut encryption_generator, + ChunkSize(chunk_size), + decomp_base_log, + decomp_level_count, + ciphertext_modulus, + &input_lwe_secret_key, + &output_lwe_secret_key, + glwe_noise_distribution, + ); + for chunk in chunk_generator { + let mut serialized_data = Vec::new(); + let serialized_size = safe_serialized_size(&chunk) + .expect_throw("couldn't guess size of serialized chunk"); + safe_serialize(&chunk, &mut serialized_data, serialized_size) + .expect_throw("couldn't serialize chunk"); + total_len += serialized_data.len(); + let js_array = web_sys::js_sys::Uint8Array::from(serialized_data.as_slice()); + port.post_message(&js_array) + .expect_throw("Failed to post message to port"); + } + } + Compression::Seed => { + let chunk_generator = SeededLweKeyswitchKeyChunkGenerator::new( + ChunkSize(chunk_size), + decomp_base_log, + decomp_level_count, + ciphertext_modulus, + &input_lwe_secret_key, + &output_lwe_secret_key, + glwe_noise_distribution, + CompressionSeed { + seed: Seed(compression_seed), + }, + seeder, + ); + for chunk in chunk_generator { + let mut serialized_data = Vec::new(); + let serialized_size = safe_serialized_size(&chunk) + .expect_throw("couldn't guess size of serialized chunk"); + safe_serialize(&chunk, &mut serialized_data, serialized_size) + .expect_throw("couldn't serialize chunk"); + total_len += serialized_data.len(); + let js_array = web_sys::js_sys::Uint8Array::from(serialized_data.as_slice()); + port.post_message(&js_array) + .expect_throw("Failed to post message to port"); + } + } + Compression::Paillier => todo!("Paillier compression not implemented"), + } + port.close(); + total_len + } + /// Retrieves an LWE secret key from a client keyset based on the provided key ID. /// /// This function is intended to be used in a WebAssembly (WASM) environment. diff --git a/frontends/concrete-rust/concrete-keygen/tests/keygen_wasm.js b/frontends/concrete-rust/concrete-keygen/tests/keygen_wasm.js index 023e9dfee5..5749b41fda 100644 --- a/frontends/concrete-rust/concrete-keygen/tests/keygen_wasm.js +++ b/frontends/concrete-rust/concrete-keygen/tests/keygen_wasm.js @@ -43,7 +43,7 @@ console.log("keyset_info explained:"); console.dir(keyset_info_json, { depth: null }); let client_keyset_buffer = cr.generate_client_keyset(keyset_info_buffer, BigInt(128)); -let keyset_buffer_no_bsk = cr.generate_keyset(keyset_info_buffer, true, {}, false, {}, BigInt(128), BigInt(128), client_keyset_buffer); +let keyset_buffer_no_bsk_no_ksk = cr.generate_keyset(keyset_info_buffer, true, {}, true, {}, BigInt(128), BigInt(128), client_keyset_buffer); for (let bsk of keyset_info_json.lwe_bootstrap_keys) { let msgChannel = createChunkMessageChannel(`bsk_${bsk.id}_chunk`); @@ -66,6 +66,27 @@ for (let bsk of keyset_info_json.lwe_bootstrap_keys) { } } -// On server side, we should be able to assemble bsk keys first, -// then full keyset from keyset_buffer_no_bsk and those bsk keys -fs.writeFileSync(output_keyset_path, keyset_buffer_no_bsk) +for (let ksk of keyset_info_json.lwe_bootstrap_keys) { + let msgChannel = createChunkMessageChannel(`ksk_${ksk.id}_chunk`); + let sk_in = cr.get_lwe_secret_key_from_client_keyset(client_keyset_buffer, ksk.input_id); + let sk_out = cr.get_lwe_secret_key_from_client_keyset(client_keyset_buffer, ksk.output_id); + try { + let returned_value = await cr.chunked_ksk_keygen( + keyset_info_buffer, + sk_in, + sk_out, + ksk.id, + BigInt(0), + BigInt(0), + 8, + msgChannel.port2 + ); + console.log(`Keyswitch key ${ksk.id} generated successfully (returned value: ${returned_value})`); + } catch (error) { + console.error(`Error generating keyswitch key ${ksk.id}:`, error); + } +} + +// On server side, we should be able to assemble bsk/ksk keys first, +// then full keyset from keyset_buffer_no_bsk_no_ksk and those bsk and ksk keys +fs.writeFileSync(output_keyset_path, keyset_buffer_no_bsk_no_ksk) diff --git a/frontends/concrete-rust/concrete-keygen/webapp/keygen.js b/frontends/concrete-rust/concrete-keygen/webapp/keygen.js index a7dc50cdef..8632559413 100644 --- a/frontends/concrete-rust/concrete-keygen/webapp/keygen.js +++ b/frontends/concrete-rust/concrete-keygen/webapp/keygen.js @@ -37,11 +37,11 @@ async function handleKeygen(event) { console.log("Client keyset generation completed.") // Generate keyset without bootstrap keys - const keysetBufferNoBsk = cr.generate_keyset( + const keysetBufferNoBskNoKsk = cr.generate_keyset( keysetInfoBuffer, true, // no_bsk {}, // ignore_bsk - false, // no_ksk + true, // no_ksk {}, // ignore_ksk BigInt(0), BigInt(0), @@ -51,7 +51,7 @@ async function handleKeygen(event) { // Create a zip file const zipFile = new zip.ZipWriter(new zip.BlobWriter("application/zip"), { bufferedWrite: true }); - zipFile.add('keyset_no_bsk.capnp', new zip.Uint8ArrayReader(keysetBufferNoBsk)); + zipFile.add('keyset_no_bsk_no_ksk.capnp', new zip.Uint8ArrayReader(keysetBufferNoBskNoKsk)); zipFile.add('keyset_info.capnp', new zip.Uint8ArrayReader(keysetInfoBuffer)); // Generate bootstrap keys in chunks @@ -78,6 +78,30 @@ async function handleKeygen(event) { console.log("BSK chunks generation completed.") + // Generate keyswitch keys in chunks + for (const ksk of keysetInfoJson.lwe_keyswitch_keys) { + const chunk_size = parseInt(document.getElementById('chunk-size').value, 10) || 8; + var chunk_count = 0; + const port = { + postMessage: (data) => { + zipFile.add(`ksk_${ksk.id}_chunk_${chunk_count++}`, new zip.Uint8ArrayReader(data)); + }, + close: () => console.log(`Keyswitch key ${ksk.id} generation completed.`) + }; + await cr.chunked_ksk_keygen( + keysetInfoBuffer, + cr.get_lwe_secret_key_from_client_keyset(clientKeysetBuffer, ksk.input_id), + cr.get_lwe_secret_key_from_client_keyset(clientKeysetBuffer, ksk.output_id), + ksk.id, + BigInt(0), + BigInt(0), + chunk_size, + port + ); + } + + console.log("KSK chunks generation completed.") + const url = URL.createObjectURL(await zipFile.close()); downloadLink.href = url;