Last active
May 4, 2022 14:01
-
-
Save FlorentCLMichel/9fbaf668b030e263d3d9614e585bb9ed to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// encrypt the string `plaintext` into a vector of ciphertexts | |
pub fn str_to_encrypted_seq(client_key: &ClientKey, plaintext: &str) | |
-> Vec<Ciphertext> | |
{ | |
// convert the string to a sequence of bytes | |
let plaintext_bytes = plaintext.as_bytes(); | |
// create a vector of ciphertexts | |
let mut result = Vec::<Ciphertext>::new(); | |
// loop over the bytes of the plaintext | |
for x in plaintext_bytes { | |
// loop over the bits of the current byte | |
for i in 0..N_BITS_PER_CHAR { | |
// if the bit is 1, push an encryption of true to the results | |
// otherwise, push an encryption of false | |
if *x & (1 << i) != 0 { | |
result.push(client_key.encrypt(true)) | |
} else { | |
result.push(client_key.encrypt(false)) | |
} | |
} | |
} | |
result | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// a custom error for FHE operations | |
#[derive(Debug, Clone)] | |
pub struct FHEError { | |
message: String | |
} | |
impl FHEError { | |
/// creates a new [`FHEError`] | |
pub fn new(message: String) -> FHEError { | |
FHEError { message } | |
} | |
} | |
// implement the `Display` trait to be able to print the error | |
impl std::fmt::Display for FHEError { | |
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
write!(f, "FHEError: {}", self.message) | |
} | |
} | |
// implement the `Error` trait | |
impl std::error::Error for FHEError {} | |
// convert Concrete's [`CryptoAPIError`] to an `FHEError` | |
impl std::convert::From<CryptoAPIError> for FHEError { | |
fn from (err: CryptoAPIError) -> Self { | |
FHEError::new(format!("CryptoAPIError: {:}", err)) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub use concrete_boolean::{ gen_keys, | |
server_key::ServerKey, | |
ciphertext::Ciphertext, | |
client_key::ClientKey }; | |
pub use concrete::CryptoAPIError; | |
pub const N_BITS_PER_CHAR: usize = 8; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub use concrete_boolean::{ gen_keys, | |
server_key::ServerKey, | |
ciphertext::Ciphertext, | |
client_key::ClientKey }; | |
pub use concrete::CryptoAPIError; | |
/// Return a ciphertext that decrypts to `true` if `a` and `b` encrypt the same bit and `false` | |
/// otherwise. | |
pub fn bits_are_equal(server_key: &ServerKey, a: &Ciphertext, b: &Ciphertext) | |
-> Ciphertext | |
{ | |
server_key.xnor(&a, &b) | |
} | |
/// If `a` is not empty and `a` and `b` have the same length, return `Ok(c)` where `c` is ciphertext | |
/// that decrypts to `true` if `a` and `b` encrypt the same sequence of bits and `false`otherwise. | |
/// Return an `FHEError` if `a` is empty or if `a` and `b` have different lengths. | |
pub fn are_equal(server_key: &ServerKey, a: &[Ciphertext], b: &[Ciphertext]) | |
-> Result<Ciphertext, FHEError> | |
{ | |
// check that a is not empty | |
if a.len() == 0 { | |
return Err(FHEError::new( | |
"Error checking the equality between two elements: the first element is empty" | |
.to_string(), | |
)); | |
} | |
// check that the two inputs have the same size | |
if a.len() != b.len() { | |
return Err(FHEError::new(format!( | |
"Error checking the equality between two elements: the elements of different lengths ({} and {})", | |
a.len(), b.len() | |
))); | |
} | |
// check the equality of the first elements | |
let mut are_equal = bits_are_equal(server_key, &a[0], &b[0]); | |
// check the equality of the other elements | |
for i in 1..a.len() { | |
are_equal = server_key.and(&are_equal, &bits_are_equal(server_key, &a[i], &b[i])); | |
} | |
Ok(are_equal) | |
} | |
/// encrypt an str | |
pub fn str_to_encrypted_seq(client_key: &ClientKey, plaintext: &str) | |
-> Vec<Ciphertext> | |
{ | |
let plaintext_bytes = plaintext.as_bytes(); | |
let mut result = Vec::<Ciphertext>::new(); | |
for x in plaintext_bytes { | |
for i in 0..8 { | |
if *x & (1 << i) != 0 { | |
result.push(client_key.encrypt(true)) | |
} else { | |
result.push(client_key.encrypt(false)) | |
} | |
} | |
} | |
result | |
} | |
/// search an encrypted word in a set of encrypted words | |
pub fn search(server_key: &ServerKey, word: &[Ciphertext], list: &[Vec<Ciphertext>]) | |
-> Result<Ciphertext, FHEError> | |
{ | |
// get an encryption of `false` | |
let mut result_encrypted = server_key.not(&bits_are_equal(server_key, &word[0], &word[0])); | |
// loop over the words in the list | |
for word_from_list in list { | |
// If the word has the right length, check it against `word`. | |
// Otherwise, do nothing. | |
if word_from_list.len() == word.len() { | |
result_encrypted = server_key.or(&result_encrypted, | |
&are_equal(server_key, &word, &word_from_list)?); | |
} | |
} | |
Ok(result_encrypted) | |
} | |
/// split a string on spaces and pad the words with ` ` | |
pub fn split_and_pad_str(text: &str, length: usize) | |
-> Vec<String> | |
{ | |
text.split(' ').map(|x| { format!("{:*<1$}", x, length) }).collect() | |
} | |
/// pad `word` with ` ` up to length `length` | |
pub fn pad_word(word: &str, length: usize) | |
-> String | |
{ | |
format!("{:*<1$}", word, length) | |
} | |
/// A custom error for FHE operations | |
#[derive(Debug, Clone)] | |
pub struct FHEError { | |
message: String | |
} | |
impl FHEError { | |
pub fn new(message: String) -> FHEError { | |
FHEError { message } | |
} | |
} | |
impl std::fmt::Display for FHEError { | |
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
write!(f, "FHEError: {}", self.message) | |
} | |
} | |
impl std::error::Error for FHEError {} | |
impl std::convert::From<CryptoAPIError> for FHEError { | |
fn from (err: CryptoAPIError) -> Self { | |
FHEError::new(format!("CryptoAPIError: {:}", err)) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn main() { | |
// define the client and server keys | |
let (client_key, server_key) = gen_keys(); | |
// words to be searched for | |
let word_1 = "like"; | |
let word_2 = "hate"; | |
// text to search the words in | |
let text = "I like making FHE easy!"; | |
// encrypt the words and text | |
let word_1_enc = str_to_encrypted_seq(&client_key, &word_1); | |
let word_2_enc = str_to_encrypted_seq(&client_key, &word_2); | |
let text_enc = str_to_encrypted_seq(&client_key, &text); | |
// search for the words in the text | |
let word_1_found_enc = search_word(&server_key, &word_1_enc, &text_enc).unwrap(); | |
let word_2_found_enc = search_word(&server_key, &word_2_enc, &text_enc).unwrap(); | |
// decrypt the results | |
let words_found = [client_key.decrypt(&word_1_found_enc), | |
client_key.decrypt(&word_2_found_enc)]; | |
// print the result | |
for (i,word) in [word_1, word_2].iter().enumerate() { | |
if words_found[i] { | |
println!("‘{}’ found!", word); | |
} else { | |
println!("‘{}’ not found", word); | |
} | |
} | |
println!(""); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use test_concrete::*; | |
fn main() { | |
// maximum length of a word | |
let max_length = 6; | |
// words to be searched for | |
let words = ["like", "hate"]; | |
// text to search the words in | |
let text = "I like making FHE easy"; | |
// split text into words and pad them | |
let list_words = split_and_pad_str(text, max_length); | |
// define the client and server keys | |
let (client_key, server_key) = gen_keys(); | |
// encrypt the words and list | |
let words_enc = words.iter().map(|x| str_to_encrypted_seq(&client_key, | |
&pad_word(x, max_length))) | |
.collect::<Vec<Vec<Ciphertext>>>(); | |
let list_enc: Vec<Vec<Ciphertext>> = | |
list_words.iter().map(|word| str_to_encrypted_seq(&client_key, &word)).collect(); | |
// search for the words in the list | |
let words_found_enc = words_enc.iter() | |
.map(|x| search(&server_key, x, &list_enc).unwrap()) | |
.collect::<Vec<Ciphertext>>(); | |
// decrypt the results | |
let words_found = words_found_enc.iter() | |
.map(|x| client_key.decrypt(x)) | |
.collect::<Vec<bool>>(); | |
// print the result | |
for (i,word) in words.iter().enumerate() { | |
if words_found[i] { | |
println!("‘{}’ found!", word); | |
} else { | |
println!("‘{}’ not found", word); | |
} | |
} | |
println!(""); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
‘like’ found! | |
‘hate’ not found |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// If `a` is not empty and `b` is no smaller than `a`, return `Ok(c)` where `c` is a cipher that | |
/// decrypts to `true` if the plaintext of `a` is in the plaintext of `b` and to `false` otherwise. | |
/// Return an `FHEError` if `a` is empty or if `b` is smaller than `a`. | |
pub fn search_word(server_key: &ServerKey, a: &[Ciphertext], b: &[Ciphertext]) | |
-> Result<Ciphertext, FHEError> | |
{ | |
// check that a is not empty | |
if a.len() == 0 { | |
return Err(FHEError::new( | |
"Error checking the equality between two elements: the first element is empty" | |
.to_string(), | |
)); | |
} | |
// if b is smaller than a, return an encryption of false | |
if b.len() < a.len() { | |
return Ok(server_key.and(&a[0], &server_key.not(&a[0]))); | |
} | |
// check if the start of b is a | |
let mut is_found = are_equal(server_key, a, &b[..a.len()])?; | |
// check the next positions | |
for i in 1..=(b.len()-a.len())/N_BITS_PER_CHAR { | |
is_found = server_key.or(&is_found, | |
&are_equal(server_key, a, &b[i*N_BITS_PER_CHAR..a.len()+i*N_BITS_PER_CHAR])?); | |
} | |
Ok(is_found) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Return a ciphertext that decrypts to `true` if `a` and `b` encrypt the same bit and `false` | |
/// otherwise. | |
pub fn bits_are_equal(server_key: &ServerKey, a: &Ciphertext, b: &Ciphertext) | |
-> Ciphertext | |
{ | |
server_key.xnor(&a, &b) | |
} | |
/// If `a` is not empty and `a` and `b` have the same length, return `Ok(c)` where `c` is ciphertext | |
/// that decrypts to `true` if `a` and `b` encrypt the same sequence of bits and `false`otherwise. | |
/// Return an `FHEError` if `a` is empty or if `a` and `b` have different lengths. | |
pub fn are_equal(server_key: &ServerKey, a: &[Ciphertext], b: &[Ciphertext]) | |
-> Result<Ciphertext, FHEError> | |
{ | |
// check that a is not empty | |
if a.len() == 0 { | |
return Err(FHEError::new( | |
"Error checking the equality between two elements: the first element is empty" | |
.to_string(), | |
)); | |
} | |
// check that the two inputs have the same size | |
if a.len() != b.len() { | |
return Err(FHEError::new(format!( | |
"Error checking the equality between two elements: the elements have different lengths ({} and {})", | |
a.len(), b.len() | |
))); | |
} | |
// check the equality of the first elements | |
let mut are_equal = bits_are_equal(server_key, &a[0], &b[0]); | |
// check the equality of the other elements | |
for i in 1..a.len() { | |
are_equal = server_key.and(&are_equal, &bits_are_equal(server_key, &a[i], &b[i])); | |
} | |
Ok(are_equal) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment