From a600a4e7cf580295c2e8c5202dd20d6b974102b4 Mon Sep 17 00:00:00 2001 From: prathyuk9 Date: Thu, 21 Nov 2024 23:43:30 -0500 Subject: [PATCH 1/5] Add integration tests folder --- Cargo.lock | 10 +++ Cargo.toml | 5 +- tests/integration_tests.rs | 139 +++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 tests/integration_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 2d72d4c..16fba0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -45,6 +54,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "ckks-engine" version = "0.3.0" dependencies = [ + "approx", "env_logger", "ff", "log", diff --git a/Cargo.toml b/Cargo.toml index 596fbec..10ded33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,7 @@ ff = "0.9" # Finite field arithmetic rand = "0.8" # Random number generation num-bigint = "0.4" # For large integer support (could be useful for mod operations) log = "0.4" -env_logger = "0.10" \ No newline at end of file +env_logger = "0.10" + +[dev-dependencies] +approx = "0.5.1" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs new file mode 100644 index 0000000..10ddf4a --- /dev/null +++ b/tests/integration_tests.rs @@ -0,0 +1,139 @@ +use ckks_engine::*; +use approx::AbsDiffEq; + +fn run_homomorphic_arithmetic_tests(poly1: &[f64], poly2: Option<&[f64]>, epsilon: f64) { + let keygen = KeyGenerator::new(); + let (public_key, secret_key) = keygen.generate_keys(); + let params = CkksParameters::new(2048, 1000000000000007); + let encryptor = CKKSEncryptor::new(public_key.clone(), params.clone()); + let decryptor = CKKSDecryptor::new(secret_key.clone(), params.clone()); + + // Encrypt inputs + let encrypted_poly1 = encryptor.encrypt_collection(poly1); + let encrypted_poly2 = poly2.map(|p| encryptor.encrypt_collection(p)); + // Test addition + if let Some(encrypted_poly2) = &encrypted_poly2 { + let encrypted_add = encryptor.homomorphic_add(&encrypted_poly1, encrypted_poly2); + let decrypted_add = decryptor.decrypt(&encrypted_add); + let expected_add: Vec = poly1 + .iter() + .zip(poly2.unwrap().iter()) + .map(|(&a, &b)| a + b) + .collect(); + + assert!( + expected_add + .iter() + .zip(decrypted_add.iter()) + .all(|(expected_val, decrypted_val)| { + decrypted_val.abs_diff_eq(expected_val, epsilon) + }), + "Addition failed. Expected: {:?}, Decrypted: {:?}", + expected_add, + decrypted_add + ); + } + // Test subtraction + if let Some(encrypted_poly2) = &encrypted_poly2 { + let encrypted_sub = encryptor.homomorphic_subtract(&encrypted_poly1, encrypted_poly2); + let decrypted_sub = decryptor.decrypt(&encrypted_sub); + let expected_sub: Vec = poly1 + .iter() + .zip(poly2.unwrap().iter()) + .map(|(&a, &b)| a - b) + .collect(); + + assert!( + expected_sub + .iter() + .zip(decrypted_sub.iter()) + .all(|(expected_val, decrypted_val)| { + decrypted_val.abs_diff_eq(expected_val, epsilon) + }), + "Subtraction failed. Expected: {:?}, Decrypted: {:?}", + expected_sub, + decrypted_sub + ); + } + // Test negation + let encrypted_neg = encryptor.homomorphic_negation(&encrypted_poly1); + let decrypted_neg = decryptor.decrypt(&encrypted_neg); + let expected_neg: Vec = poly1.iter().map(|&a| -a).collect(); + + assert!( + expected_neg + .iter() + .zip(decrypted_neg.iter()) + .all(|(expected_val, decrypted_val)| { + decrypted_val.abs_diff_eq(expected_val, epsilon) + }), + "Negation failed. Expected: {:?}, Decrypted: {:?}", + expected_neg, + decrypted_neg + ); +} +#[test] +fn test_homomorphic_arithmetic_with_integers() { + let poly1 = [10.0, 202.0, 304.0]; + let poly2 = [5.0, 150.0, 210.0]; + run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9); +} +#[test] +fn test_homomorphic_arithmetic_with_floats() { + let poly1 = [1.1, 2.2, 3.3]; + let poly2 = [4.4, 5.5, 6.6]; + run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9); +} +#[test] +fn test_homomorphic_arithmetic_with_negatives() { + let poly1 = [-10.0, -20.0, -30.0]; + let poly2 = [5.0, 15.0, 25.0]; + run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9); +} +#[test] +fn test_homomorphic_arithmetic_with_single_element_arrays() { + let poly1 = [42.0]; + let poly2 = [58.0]; + run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9); +} +#[test] +fn test_floating_point_operations() { + let keygen = KeyGenerator::new(); + let (public_key, secret_key) = keygen.generate_keys(); + let params = CkksParameters::new(2048, 1000000000000007); + let encryptor = CKKSEncryptor::new(public_key.clone(), params.clone()); + let decryptor = CKKSDecryptor::new(secret_key.clone(), params.clone()); + + + let float_array: [f64; 3] = [3.6, 5.26, 3.78]; + let encrypted_float_array = encryptor.encrypt_collection(&float_array); + + // Ceil + let encrypted_ceil = encryptor.homomorphic_ceil(&encrypted_float_array); + let decrypted_ceil = decryptor.decrypt(&encrypted_ceil); + let expected_ceil: Vec = float_array.iter().map(|&a| a.ceil()).collect(); + assert_eq!(decrypted_ceil, expected_ceil, "Ceil operation failed."); + + // Floor + let encrypted_floor = encryptor.homomorphic_floor(&encrypted_float_array); + let decrypted_floor = decryptor.decrypt(&encrypted_floor); + let expected_floor: Vec = float_array.iter().map(|&a| a.floor()).collect(); + assert_eq!(decrypted_floor, expected_floor, "Floor operation failed."); + + // Round + let encrypted_round = encryptor.homomorphic_round(&encrypted_float_array); + let decrypted_round = decryptor.decrypt(&encrypted_round); + let expected_round: Vec = float_array.iter().map(|&a| a.round()).collect(); + assert_eq!(decrypted_round, expected_round, "Round operation failed."); + + // Truncate + let encrypted_truncate = encryptor.homomorphic_truncate(&encrypted_float_array); + let decrypted_truncate = decryptor.decrypt_as_int(&encrypted_truncate); + let expected_truncate: Vec = float_array.iter().map(|&a| a as i64).collect(); + assert_eq!( + decrypted_truncate, expected_truncate, + "Truncate operation failed." + ); +} + + From 43fc5bef571ef717a0484d3596e735a636a16a4a Mon Sep 17 00:00:00 2001 From: prathyuk9 Date: Sat, 23 Nov 2024 00:41:52 -0500 Subject: [PATCH 2/5] Add tests for advance arithmetic operations --- src/arithmetic.rs | 2 - tests/integration_tests.rs | 124 ++++++++++++++++++++++++++++++------- 2 files changed, 102 insertions(+), 24 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 83501f1..52073aa 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -208,9 +208,7 @@ impl CKKSEncryptor { // Multiply the result by cipher polynomial let temp = self.homomorphic_multiply(&result, cipher); result = temp; - println!("bro the result is {:?}",result); } - println!("bro the result is {:?}",result); // Perform modular reduction to ensure the result fits within the modulus let reduced_result = mod_reduce(&result, self.params.modulus); info!("Result after homomorphic exponentiation and mod reduction: {:?}", reduced_result); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 10ddf4a..0d410f7 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,7 +1,7 @@ use ckks_engine::*; use approx::AbsDiffEq; -fn run_homomorphic_arithmetic_tests(poly1: &[f64], poly2: Option<&[f64]>, epsilon: f64) { +fn run_homomorphic_arithmetic_tests(poly1: &[f64], poly2: Option<&[f64]>, epsilon: f64, large_epsilon: f64) { let keygen = KeyGenerator::new(); let (public_key, secret_key) = keygen.generate_keys(); let params = CkksParameters::new(2048, 1000000000000007); @@ -11,6 +11,7 @@ fn run_homomorphic_arithmetic_tests(poly1: &[f64], poly2: Option<&[f64]>, epsilo // Encrypt inputs let encrypted_poly1 = encryptor.encrypt_collection(poly1); let encrypted_poly2 = poly2.map(|p| encryptor.encrypt_collection(p)); + // Test addition if let Some(encrypted_poly2) = &encrypted_poly2 { let encrypted_add = encryptor.homomorphic_add(&encrypted_poly1, encrypted_poly2); @@ -18,7 +19,7 @@ fn run_homomorphic_arithmetic_tests(poly1: &[f64], poly2: Option<&[f64]>, epsilo let expected_add: Vec = poly1 .iter() .zip(poly2.unwrap().iter()) - .map(|(&a, &b)| a + b) + .map(|(&a, &b)| ((a + b) * 100.0).round() / 100.0) .collect(); assert!( @@ -33,6 +34,7 @@ fn run_homomorphic_arithmetic_tests(poly1: &[f64], poly2: Option<&[f64]>, epsilo decrypted_add ); } + // Test subtraction if let Some(encrypted_poly2) = &encrypted_poly2 { let encrypted_sub = encryptor.homomorphic_subtract(&encrypted_poly1, encrypted_poly2); @@ -40,7 +42,7 @@ fn run_homomorphic_arithmetic_tests(poly1: &[f64], poly2: Option<&[f64]>, epsilo let expected_sub: Vec = poly1 .iter() .zip(poly2.unwrap().iter()) - .map(|(&a, &b)| a - b) + .map(|(&a, &b)| ((a - b) * 100.0).round() / 100.0) .collect(); assert!( @@ -55,47 +57,125 @@ fn run_homomorphic_arithmetic_tests(poly1: &[f64], poly2: Option<&[f64]>, epsilo decrypted_sub ); } + + // Test multiplication + if let Some(encrypted_poly2) = &encrypted_poly2 { + let encrypted_mul = encryptor.homomorphic_multiply(&encrypted_poly1, encrypted_poly2); + let decrypted_mul = decryptor.decrypt(&encrypted_mul); + let expected_mul: Vec = poly1 + .iter() + .zip(poly2.unwrap().iter()) + .map(|(&a, &b)| ((a * b) * 100.0).round() / 100.0) + .collect(); + + assert!( + expected_mul + .iter() + .zip(decrypted_mul.iter()) + .all(|(expected_val, decrypted_val)| { + decrypted_val.abs_diff_eq(expected_val, large_epsilon) + }), + "Multiplication failed. Expected: {:?}, Decrypted: {:?}", + expected_mul, + decrypted_mul + ); + } + // Test negation - let encrypted_neg = encryptor.homomorphic_negation(&encrypted_poly1); - let decrypted_neg = decryptor.decrypt(&encrypted_neg); - let expected_neg: Vec = poly1.iter().map(|&a| -a).collect(); + if let Some(_) = Some(&encrypted_poly1) { + let encrypted_neg = encryptor.homomorphic_negation(&encrypted_poly1); + let decrypted_neg = decryptor.decrypt(&encrypted_neg); + let expected_neg: Vec = poly1.iter().map(|&a| ((-a) * 100.0).round() / 100.0).collect(); + + assert!( + expected_neg + .iter() + .zip(decrypted_neg.iter()) + .all(|(expected_val, decrypted_val)| { + decrypted_val.abs_diff_eq(expected_val, epsilon) + }), + "Negation failed. Expected: {:?}, Decrypted: {:?}", + expected_neg, + decrypted_neg + ); + } - assert!( - expected_neg + // Test exponentiation + if let Some(_) = Some(&encrypted_poly1) { + let exponent = 2; + let encrypted_exp = encryptor.homomorphic_exponentiation(&encrypted_poly1, exponent); + let decrypted_exp = decryptor.decrypt(&encrypted_exp); + let expected_exp: Vec = poly1 .iter() - .zip(decrypted_neg.iter()) - .all(|(expected_val, decrypted_val)| { - decrypted_val.abs_diff_eq(expected_val, epsilon) - }), - "Negation failed. Expected: {:?}, Decrypted: {:?}", - expected_neg, - decrypted_neg - ); + .map(|&a| ((a.powi(exponent as i32)) * 100.0).round() / 100.0) + .collect(); + + assert!( + expected_exp + .iter() + .zip(decrypted_exp.iter()) + .all(|(expected_val, decrypted_val)| { + decrypted_val.abs_diff_eq(expected_val, large_epsilon) + }), + "Exponentiation failed. Expected: {:?}, Decrypted: {:?}", + expected_exp, + decrypted_exp + ); + } + + // Test division + if let Some(encrypted_poly2) = &encrypted_poly2 { + let encrypted_div = encryptor.homomorphic_divide(&encrypted_poly1, encrypted_poly2); + let decrypted_div = decryptor.decrypt(&encrypted_div); + let expected_div: Vec = poly1 + .iter() + .zip(poly2.unwrap().iter()) + .map(|(&a, &b)| ((a / b) * 100.0).round() / 100.0) + .collect(); + + assert!( + expected_div + .iter() + .zip(decrypted_div.iter()) + .all(|(expected_val, decrypted_val)| { + decrypted_val.abs_diff_eq(expected_val, large_epsilon) + }), + "Division failed. Expected: {:?}, Decrypted: {:?}", + expected_div, + decrypted_div + ); + } + } + #[test] fn test_homomorphic_arithmetic_with_integers() { - let poly1 = [10.0, 202.0, 304.0]; - let poly2 = [5.0, 150.0, 210.0]; - run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9); + let poly1 = [10.0, 20.0, 30.0]; + let poly2 = [5.0, 15.0, 21.0]; + run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9, 10.0); } + #[test] fn test_homomorphic_arithmetic_with_floats() { let poly1 = [1.1, 2.2, 3.3]; let poly2 = [4.4, 5.5, 6.6]; - run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9); + run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9, 10.0); } + #[test] fn test_homomorphic_arithmetic_with_negatives() { let poly1 = [-10.0, -20.0, -30.0]; let poly2 = [5.0, 15.0, 25.0]; - run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9); + run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9, 10.0); } + #[test] fn test_homomorphic_arithmetic_with_single_element_arrays() { let poly1 = [42.0]; let poly2 = [58.0]; - run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9); + run_homomorphic_arithmetic_tests(&poly1, Some(&poly2), 0.9, 10.0); } + #[test] fn test_floating_point_operations() { let keygen = KeyGenerator::new(); From 34f806c3cb083f406f79b5e61d29c1dd369c7a9f Mon Sep 17 00:00:00 2001 From: Santosh Kumar Pottumuthu Date: Sat, 23 Nov 2024 01:50:30 -0500 Subject: [PATCH 3/5] Test Cases for Homomorphic Operations of Strings --- tests/integration_tests.rs | 99 +++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 10ddf4a..58ca4f6 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -135,5 +135,102 @@ fn test_floating_point_operations() { "Truncate operation failed." ); } - + +fn initialize_ckks() -> (CKKSEncryptor, CKKSDecryptor) { //TO-DO remove redundant declarations in arithmetic tests and use this function + let keygen = KeyGenerator::new(); + let (public_key, secret_key) = keygen.generate_keys(); + let params = CkksParameters::new(2048, 1000000000000007); + let encryptor = CKKSEncryptor::new(public_key.clone(), params.clone()); + let decryptor = CKKSDecryptor::new(secret_key.clone(), params.clone()); + (encryptor, decryptor) +} + +fn encode_string(input: &str) -> Vec { + input.chars().map(|c| c as u32 as f64).collect() +} + +fn map_to_nearest_ascii(val: f64) -> char { + let rounded_val = val.round() as u8; + if rounded_val.is_ascii() { + rounded_val as char + } else { + '?' // Fallback for non-ASCII values + } +} + +#[test] +fn test_character_encoding_encrypting_and_decoding() { + + let (encryptor, decryptor) = initialize_ckks(); + let original_string = "Hello Team! I hope project is going well."; + let encoded = encode_string(&original_string); + let encrypted_data = encryptor.encrypt_collection(&encoded); + let decrypted_data = decryptor.decrypt(&encrypted_data); + + let decrypted_string: String = decrypted_data + .iter() + .map(|&val| map_to_nearest_ascii(val)) + .collect(); + + assert_eq!(decrypted_string, original_string, "String encoding or decryption failed"); +} + +#[test] +fn test_homomorphic_length_calculation() { + + let (encryptor, _) = initialize_ckks(); + let original_string = "TestLength"; + let encoded = encode_string(&original_string); + let encrypted_data = encryptor.encrypt_collection(&encoded); + + let length = encryptor.homomorphic_length(&encrypted_data); + + let expected_length = original_string.len(); + assert_eq!(length, expected_length, "Homomorphic length calculation failed"); +} + +#[test] +fn test_homomorphic_concatenation() { + + let (encryptor, decryptor) = initialize_ckks(); + + let string1 = "Hello"; + let string2 = "World"; + let encoded1 = encode_string(&string1); + let encoded2 = encode_string(&string2); + let encrypted_data1 = encryptor.encrypt_collection(&encoded1); + let encrypted_data2 = encryptor.encrypt_collection(&encoded2); + + let concatenated_encrypted = encryptor.concatenate_encrypted_strings(&encrypted_data1, &encrypted_data2); + let decrypted_data = decryptor.decrypt(&concatenated_encrypted); + + let decrypted_string: String = decrypted_data + .iter() + .map(|&val| (val.round() as u8) as char) + .collect(); + + let expected_string = format!("{}{}", string1, string2); + assert_eq!(decrypted_string, expected_string, "Homomorphic concatenation failed"); +} + +#[test] +fn test_homomorphic_substring_extraction() { + let (encryptor, decryptor) = initialize_ckks(); + let original_string = "HelloWorld"; + let encoded = encode_string(&original_string); + let encrypted_data = encryptor.encrypt_collection(&encoded); + + let substring_start = 5; + let substring_end = 10; + let extracted_encrypted = encryptor.extract_encrypted_substring(&encrypted_data, substring_start..substring_end); + + let decrypted_data = decryptor.decrypt(&extracted_encrypted); + let decrypted_string: String = decrypted_data + .iter() + .map(|&val| (val.round() as u8) as char) + .collect(); + + let expected_string = "World"; + assert_eq!(decrypted_string, expected_string, "Substring extraction failed"); +} From 13d91eb29af160c5602b29ab782b7d4112e94827 Mon Sep 17 00:00:00 2001 From: Santosh Kumar Pottumuthu Date: Sat, 23 Nov 2024 02:57:27 -0500 Subject: [PATCH 4/5] Added few more test cases for Unicode and Larger Strings --- tests/integration_tests.rs | 144 ++++++++++++++++++++++++++++++++----- 1 file changed, 128 insertions(+), 16 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 6b1be4a..5c2af11 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -220,7 +220,9 @@ fn test_floating_point_operations() { fn initialize_ckks() -> (CKKSEncryptor, CKKSDecryptor) { //TO-DO remove redundant declarations in arithmetic tests and use this function let keygen = KeyGenerator::new(); let (public_key, secret_key) = keygen.generate_keys(); - let params = CkksParameters::new(2048, 1000000000000007); + // let params = CkksParameters::new(2048, 1000000000000007); + let params = CkksParameters::new(4096, 1000000000000007); // Larger modulus + let encryptor = CKKSEncryptor::new(public_key.clone(), params.clone()); let decryptor = CKKSDecryptor::new(secret_key.clone(), params.clone()); (encryptor, decryptor) @@ -239,18 +241,32 @@ fn map_to_nearest_ascii(val: f64) -> char { } } +fn map_to_nearest_unicode(val: f64) -> char { + let rounded_val = val.round() as u32; + std::char::from_u32(rounded_val).unwrap_or('?') // Use '?' for invalid Unicode values + + + // if let Some(character) = std::char::from_u32(rounded_val) { + // character + // } else { + // '?' // Fallback for non-valid Unicode values + // } + +} + + #[test] fn test_character_encoding_encrypting_and_decoding() { let (encryptor, decryptor) = initialize_ckks(); - let original_string = "Hello Team! I hope project is going well."; + let original_string = " こんにちは Hello Team! I hope project is going well."; let encoded = encode_string(&original_string); let encrypted_data = encryptor.encrypt_collection(&encoded); let decrypted_data = decryptor.decrypt(&encrypted_data); let decrypted_string: String = decrypted_data .iter() - .map(|&val| map_to_nearest_ascii(val)) + .map(|&val| map_to_nearest_unicode(val)) .collect(); assert_eq!(decrypted_string, original_string, "String encoding or decryption failed"); @@ -272,28 +288,34 @@ fn test_homomorphic_length_calculation() { #[test] fn test_homomorphic_concatenation() { - let (encryptor, decryptor) = initialize_ckks(); - let string1 = "Hello"; - let string2 = "World"; + let string1 = "こんにちは"; + let string2 = Some("World"); let encoded1 = encode_string(&string1); - let encoded2 = encode_string(&string2); let encrypted_data1 = encryptor.encrypt_collection(&encoded1); - let encrypted_data2 = encryptor.encrypt_collection(&encoded2); - let concatenated_encrypted = encryptor.concatenate_encrypted_strings(&encrypted_data1, &encrypted_data2); - let decrypted_data = decryptor.decrypt(&concatenated_encrypted); + if let Some(inner_string2) = string2 { - let decrypted_string: String = decrypted_data - .iter() - .map(|&val| (val.round() as u8) as char) - .collect(); + let encoded2 = encode_string(inner_string2); + let encrypted_data2 = encryptor.encrypt_collection(&encoded2); + + let concatenated_encrypted = encryptor.concatenate_encrypted_strings(&encrypted_data1, &encrypted_data2); + let decrypted_data = decryptor.decrypt(&concatenated_encrypted); + + // mapping to nearest unicode instead of ascii + let decrypted_string: String = decrypted_data + .iter() + .map(|&val| map_to_nearest_unicode(val)) + .collect(); - let expected_string = format!("{}{}", string1, string2); - assert_eq!(decrypted_string, expected_string, "Homomorphic concatenation failed"); + let expected_string = format!("{}{}", string1, inner_string2); + assert_eq!(decrypted_string, expected_string, "Homomorphic concatenation failed"); + } } + + #[test] fn test_homomorphic_substring_extraction() { let (encryptor, decryptor) = initialize_ckks(); @@ -314,3 +336,93 @@ fn test_homomorphic_substring_extraction() { let expected_string = "World"; assert_eq!(decrypted_string, expected_string, "Substring extraction failed"); } +fn test_character_encoding_encrypting_and_decoding_with_unicode() { + let (encryptor, decryptor) = initialize_ckks(); + + let original_string = "Hello 😀 こんにちは 你好!"; + let encoded = encode_string(&original_string); + let encrypted_data = encryptor.encrypt_collection(&encoded); + let decrypted_data = decryptor.decrypt(&encrypted_data); + + let decrypted_string: String = decrypted_data + .iter() + .map(|&val| map_to_nearest_unicode(val)) + .collect(); + println!("Decrypted String is : {decrypted_string}"); + assert_eq!(decrypted_string, original_string, "Unicode encoding or decryption failed"); +} + +#[test] +fn test_unicode_edge_cases() { + let (encryptor, decryptor) = initialize_ckks(); + let original_string = "\u{7F}\u{80}\u{FFFF}\u{1F600}"; // ASCII max, Unicode start, max BMP, emoji + let encoded = encode_string(&original_string); + let encrypted_data = encryptor.encrypt_collection(&encoded); + let decrypted_data = decryptor.decrypt(&encrypted_data); + + let decrypted_string: String = decrypted_data + .iter() + .map(|&val| map_to_nearest_unicode(val)) + .collect(); + + assert_eq!(decrypted_string, original_string, "Unicode edge case failed"); +} + +#[test] +fn test_noise_resilience() { + let (encryptor, decryptor) = initialize_ckks(); + let original_string = "NoiseTest😀"; + let encoded = encode_string(&original_string); + + // Introduce noise + let mut noisy_encoded: Vec = encoded.iter().map(|&x| x + 0.001).collect(); + let encrypted_data = encryptor.encrypt_collection(&noisy_encoded); + let decrypted_data = decryptor.decrypt(&encrypted_data); + + let decrypted_string: String = decrypted_data + .iter() + .map(|&val| map_to_nearest_unicode(val)) + .collect(); + + assert_eq!(decrypted_string, original_string, "Noise resilience test failed"); +} + +#[test] +fn test_large_string_handling() { + let (encryptor, decryptor) = initialize_ckks(); + // let original_string = "A".repeat(10000000); // String with 10,000,000 'A's + let original_string = "A".repeat(100000); // String with 100,000 'A's + + // Encode and encrypt in chunks + let chunk_size = 1000; + let chunks: Vec<&str> = original_string + .as_bytes() + .chunks(chunk_size) + .map(std::str::from_utf8) + .collect::, _>>() + .unwrap(); + + let mut encrypted_chunks = vec![]; + for chunk in chunks { + let encoded_chunk = encode_string(&chunk); + let encrypted_chunk = encryptor.encrypt_collection(&encoded_chunk); + encrypted_chunks.push(encrypted_chunk); + } + + // Decrypt and reconstruct the string + let mut decrypted_chunks = vec![]; + for encrypted_chunk in encrypted_chunks { + let decrypted_chunk = decryptor.decrypt(&encrypted_chunk); + let chunk_string: String = decrypted_chunk + .iter() + .map(|&val| map_to_nearest_unicode(val)) + .collect(); + decrypted_chunks.push(chunk_string); + } + + let reconstructed_string = decrypted_chunks.concat(); + assert_eq!( + reconstructed_string, original_string, + "Large string handling failed" + ); +} From cedf721326193117e8fce6ffec802f65ba733344 Mon Sep 17 00:00:00 2001 From: Santosh Kumar Pottumuthu Date: Sat, 23 Nov 2024 20:52:32 -0500 Subject: [PATCH 5/5] Handling large testcases efficiently! Setting a limit for the desired size. --- src/arithmetic.rs | 29 ++++++++++++++++++++++++ tests/integration_tests.rs | 46 ++++++++++++++++++++++++++------------ 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 52073aa..a7c539c 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -216,4 +216,33 @@ impl CKKSEncryptor { reduced_result } + pub fn homomorphic_divide_with_constant(&self, cipher: &Polynomial, constant: i64) -> Polynomial { + // Gracefully handle the case when the constant is zero + if constant == 0 { + info!("Division by zero is not allowed. Returning the original polynomial unchanged."); + return cipher.clone(); // Return the original polynomial as is + } + + // Scale the constant to match the ciphertext's scale + let scaling_factor = 10_000_000; // Assuming 1e7 scaling factor + let scaled_constant = constant * scaling_factor; + + // Compute the reciprocal of the scaled constant + let reciprocal = scaling_factor / scaled_constant; // This is effectively 1/constant in scaled form + + // Multiply the ciphertext by the reciprocal + let scaled_reciprocal_poly = Polynomial::new(vec![reciprocal]); + let result = self.homomorphic_multiply(cipher, &scaled_reciprocal_poly); + + // Perform modular reduction to ensure the result fits within the modulus + let reduced_result = mod_reduce(&result, self.params.modulus); + info!( + "Result after homomorphic division with constant and mod reduction: {:?}", + reduced_result + ); + + reduced_result + } + + } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 5c2af11..875d076 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -387,42 +387,60 @@ fn test_noise_resilience() { assert_eq!(decrypted_string, original_string, "Noise resilience test failed"); } +fn validate_string_size(size: usize) -> bool { + const MAX_ALLOWED_SIZE: usize = 10_000_000; // Define a reasonable maximum size + if size > MAX_ALLOWED_SIZE { + println!("Error: String size exceeds the allowed limit of {} bytes.", MAX_ALLOWED_SIZE); + return false; + } + true +} + #[test] fn test_large_string_handling() { - let (encryptor, decryptor) = initialize_ckks(); - // let original_string = "A".repeat(10000000); // String with 10,000,000 'A's - let original_string = "A".repeat(100000); // String with 100,000 'A's + let (encryptor, decryptor) = initialize_ckks(); // Replace with actual initialization logic + + // Use a test string size + let desired_size = 100000; // Intentionally exceeding the limit for testing + + // Step 2: Validate the string size + if !validate_string_size(desired_size) { + println!("Test aborted due to size validation failure."); + return; // Exit the function safely + } + + // Step 3: Create the original string + let original_string = "A".repeat(desired_size); // Encode and encrypt in chunks let chunk_size = 1000; - let chunks: Vec<&str> = original_string - .as_bytes() - .chunks(chunk_size) - .map(std::str::from_utf8) - .collect::, _>>() - .unwrap(); + let chunks: Vec<&[u8]> = original_string.as_bytes().chunks(chunk_size).collect(); let mut encrypted_chunks = vec![]; for chunk in chunks { - let encoded_chunk = encode_string(&chunk); - let encrypted_chunk = encryptor.encrypt_collection(&encoded_chunk); + // Convert &[u8] to &str + let encoded_chunk = encode_string(std::str::from_utf8(chunk).unwrap()); // Replace with actual encoding logic + let encrypted_chunk = encryptor.encrypt_collection(&encoded_chunk); // Encrypt encrypted_chunks.push(encrypted_chunk); } + // Decrypt and reconstruct the string let mut decrypted_chunks = vec![]; for encrypted_chunk in encrypted_chunks { - let decrypted_chunk = decryptor.decrypt(&encrypted_chunk); + let decrypted_chunk = decryptor.decrypt(&encrypted_chunk); // Decrypt let chunk_string: String = decrypted_chunk .iter() - .map(|&val| map_to_nearest_unicode(val)) + .map(|&val| map_to_nearest_unicode(val)) // Replace with actual mapping logic .collect(); decrypted_chunks.push(chunk_string); } let reconstructed_string = decrypted_chunks.concat(); + + // Assert the reconstructed string matches the original assert_eq!( reconstructed_string, original_string, "Large string handling failed" ); -} +} \ No newline at end of file