use crate::ciphertext::Ciphertext;
use crate::parameters::BooleanParameters;
use crate::{ClientKey, PLAINTEXT_FALSE, PLAINTEXT_TRUE};
use bootstrapping::{BooleanServerKey, Bootstrapper, CpuBootstrapper};
use concrete_core::prelude::*;
use std::cell::RefCell;
pub mod bootstrapping;
use crate::engine::bootstrapping::CpuBootstrapKey;
#[cfg(feature = "cuda")]
use bootstrapping::{CudaBootstrapKey, CudaBootstrapper};
pub(crate) trait BinaryGatesEngine<L, R, K> {
fn and(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
fn nand(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
fn nor(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
fn or(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
fn xor(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
fn xnor(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
}
pub(crate) trait WithThreadLocalEngine {
fn with_thread_local_mut<R, F>(func: F) -> R
where
F: FnOnce(&mut Self) -> R;
}
pub(crate) type CpuBooleanEngine = BooleanEngine<CpuBootstrapper>;
#[cfg(feature = "cuda")]
pub(crate) type CudaBooleanEngine = BooleanEngine<CudaBootstrapper>;
thread_local! {
static CPU_ENGINE: RefCell<BooleanEngine<CpuBootstrapper>> = RefCell::new(BooleanEngine::<_>::new());
#[cfg(feature = "cuda")]
static CUDA_ENGINE: RefCell<BooleanEngine<CudaBootstrapper>> = RefCell::new(BooleanEngine::<_>::new());
}
impl WithThreadLocalEngine for CpuBooleanEngine {
fn with_thread_local_mut<R, F>(func: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
CPU_ENGINE.with(|engine_cell| func(&mut engine_cell.borrow_mut()))
}
}
#[cfg(feature = "cuda")]
impl WithThreadLocalEngine for CudaBooleanEngine {
fn with_thread_local_mut<R, F>(func: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
CUDA_ENGINE.with(|engine_cell| func(&mut engine_cell.borrow_mut()))
}
}
pub(crate) struct BooleanEngine<B> {
pub(crate) engine: DefaultEngine,
bootstrapper: B,
}
impl BooleanEngine<CpuBootstrapper> {
pub fn create_server_key(&mut self, cks: &ClientKey) -> CpuBootstrapKey {
let server_key = self.bootstrapper.new_server_key(cks).unwrap();
server_key
}
}
#[cfg(feature = "cuda")]
impl BooleanEngine<CudaBootstrapper> {
pub fn create_server_key(&mut self, cpu_key: &CpuBootstrapKey) -> CudaBootstrapKey {
let server_key = self.bootstrapper.new_serverk_key(cpu_key).unwrap();
server_key
}
}
impl<B> BooleanEngine<B> {
pub fn create_client_key(&mut self, parameters: BooleanParameters) -> ClientKey {
let lwe_secret_key: LweSecretKey32 = self
.engine
.generate_new_lwe_secret_key(parameters.lwe_dimension)
.unwrap();
let glwe_secret_key: GlweSecretKey32 = self
.engine
.generate_new_glwe_secret_key(parameters.glwe_dimension, parameters.polynomial_size)
.unwrap();
ClientKey {
lwe_secret_key,
glwe_secret_key,
parameters,
}
}
pub fn trivial_encrypt(&mut self, message: bool) -> Ciphertext {
Ciphertext::Trivial(message)
}
pub fn encrypt(&mut self, message: bool, cks: &ClientKey) -> Ciphertext {
let plain: Plaintext32 = if message {
self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap()
} else {
self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap()
};
let var = Variance(cks.parameters.lwe_modular_std_dev.get_variance());
let ct = self
.engine
.encrypt_lwe_ciphertext(&cks.lwe_secret_key, &plain, var)
.unwrap();
Ciphertext::Encrypted(ct)
}
pub fn decrypt(&mut self, ct: &Ciphertext, cks: &ClientKey) -> bool {
match ct {
Ciphertext::Trivial(b) => *b,
Ciphertext::Encrypted(ciphertext) => {
let decrypted = self
.engine
.decrypt_lwe_ciphertext(&cks.lwe_secret_key, ciphertext)
.unwrap();
let mut decrypted_u32: u32 = 0;
self.engine
.discard_retrieve_plaintext(&mut decrypted_u32, &decrypted)
.unwrap();
decrypted_u32 < (1 << 31)
}
}
}
pub fn not(&mut self, ct: &Ciphertext) -> Ciphertext {
match ct {
Ciphertext::Trivial(message) => Ciphertext::Trivial(!*message),
Ciphertext::Encrypted(ct_ct) => {
let mut ct_res = ct_ct.clone();
self.engine.fuse_opp_lwe_ciphertext(&mut ct_res).unwrap();
Ciphertext::Encrypted(ct_res)
}
}
}
}
fn new_seeder() -> Box<dyn Seeder> {
let seeder: Box<dyn Seeder>;
#[cfg(target_arch = "x86_64")]
{
if RdseedSeeder::is_available() {
seeder = Box::new(RdseedSeeder);
} else {
seeder = Box::new(UnixSeeder::new(0));
}
}
#[cfg(not(target_arch = "x86_64"))]
{
seeder = Box::new(UnixSeeder::new(0));
}
seeder
}
impl<B> BooleanEngine<B>
where
B: Bootstrapper,
{
pub fn new() -> Self {
let engine =
DefaultEngine::new(new_seeder()).expect("Unexpectedly failed to create a core engine");
Self {
engine,
bootstrapper: Default::default(),
}
}
fn convert_into_lwe_ciphertext_32(
&mut self,
ct: &Ciphertext,
server_key: &B::ServerKey,
) -> LweCiphertext32 {
match ct {
Ciphertext::Encrypted(ct_ct) => ct_ct.clone(),
Ciphertext::Trivial(message) => {
let plain: Plaintext32 = if *message {
self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap()
} else {
self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap()
};
self.engine
.trivially_encrypt_lwe_ciphertext(server_key.lwe_size(), &plain)
.unwrap()
}
}
}
pub fn mux(
&mut self,
ct_condition: &Ciphertext,
ct_then: &Ciphertext,
ct_else: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match ct_condition {
Ciphertext::Trivial(message_condition) => {
if *message_condition {
ct_then.clone()
} else {
ct_else.clone()
}
}
Ciphertext::Encrypted(ct_condition_ct) => {
if let Ciphertext::Trivial(message_then) = ct_then {
return if *message_then {
self.or(ct_condition, ct_else, server_key)
} else {
let ct_not_condition = self.not(ct_condition);
self.and(&ct_not_condition, ct_else, server_key)
};
}
if let Ciphertext::Trivial(message_else) = ct_else {
return if *message_else {
let ct_not_condition = self.not(ct_condition);
self.or(ct_then, &ct_not_condition, server_key)
} else {
self.and(ct_condition, ct_then, server_key)
};
}
let ct_then_ct = self.convert_into_lwe_ciphertext_32(ct_then, server_key);
let ct_else_ct = self.convert_into_lwe_ciphertext_32(ct_else, server_key);
let mut buffer_lwe_before_pbs_o = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let buffer_lwe_before_pbs = &mut buffer_lwe_before_pbs_o;
let bootstrapper = &mut self.bootstrapper;
self.engine
.discard_add_lwe_ciphertext(buffer_lwe_before_pbs, ct_condition_ct, &ct_then_ct)
.unwrap(); let cst = self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(buffer_lwe_before_pbs, &cst)
.unwrap();
let mut ct_temp_2 = ct_condition_ct.clone(); self.engine.fuse_opp_lwe_ciphertext(&mut ct_temp_2).unwrap(); self.engine
.fuse_add_lwe_ciphertext(&mut ct_temp_2, &ct_else_ct)
.unwrap(); let cst = self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut ct_temp_2, &cst)
.unwrap();
let mut ct_pbs_1 = bootstrapper
.bootstrap(buffer_lwe_before_pbs, server_key)
.unwrap();
let ct_pbs_2 = bootstrapper.bootstrap(&ct_temp_2, server_key).unwrap();
self.engine
.fuse_add_lwe_ciphertext(&mut ct_pbs_1, &ct_pbs_2)
.unwrap(); let cst = self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut ct_pbs_1, &cst)
.unwrap();
let ct_ks = bootstrapper.keyswitch(&ct_pbs_1, server_key).unwrap();
Ciphertext::Encrypted(ct_ks)
}
}
}
}
impl<B> BinaryGatesEngine<&Ciphertext, &Ciphertext, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
fn and(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(*message_left && *message_right)
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.and(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
self.and(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let bootstrapper = &mut self.bootstrapper;
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); let cst = self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst)
.unwrap();
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
fn nand(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(!(*message_left && *message_right))
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.nand(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
self.nand(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let bootstrapper = &mut self.bootstrapper;
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); self.engine
.fuse_opp_lwe_ciphertext(&mut buffer_lwe_before_pbs)
.unwrap(); let cst = self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst)
.unwrap();
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
fn nor(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(!(*message_left || *message_right))
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.nor(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
self.nor(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let bootstrapper = &mut self.bootstrapper;
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); self.engine
.fuse_opp_lwe_ciphertext(&mut buffer_lwe_before_pbs)
.unwrap(); let cst = self.engine.create_plaintext_from(&PLAINTEXT_FALSE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst)
.unwrap();
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
fn or(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(*message_left || *message_right)
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.or(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
self.or(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let bootstrapper = &mut self.bootstrapper;
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); let cst = self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst)
.unwrap();
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
fn xor(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(*message_left ^ *message_right)
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.xor(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
self.xor(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let bootstrapper = &mut self.bootstrapper;
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); let cst_add = self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst_add)
.unwrap(); let cst_mul = self.engine.create_cleartext_from(&2u32).unwrap();
self.engine
.fuse_mul_lwe_ciphertext_cleartext(&mut buffer_lwe_before_pbs, &cst_mul)
.unwrap();
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
fn xnor(
&mut self,
ct_left: &Ciphertext,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
match (ct_left, ct_right) {
(Ciphertext::Trivial(message_left), Ciphertext::Trivial(message_right)) => {
Ciphertext::Trivial(!(*message_left ^ *message_right))
}
(Ciphertext::Encrypted(_), Ciphertext::Trivial(message_right)) => {
self.xnor(ct_left, *message_right, server_key)
}
(Ciphertext::Trivial(message_left), Ciphertext::Encrypted(_)) => {
self.xnor(*message_left, ct_right, server_key)
}
(Ciphertext::Encrypted(ct_left_ct), Ciphertext::Encrypted(ct_right_ct)) => {
let mut buffer_lwe_before_pbs = self
.engine
.create_lwe_ciphertext_from(vec![0u32; server_key.lwe_size().0])
.unwrap();
let bootstrapper = &mut self.bootstrapper;
self.engine
.discard_add_lwe_ciphertext(&mut buffer_lwe_before_pbs, ct_left_ct, ct_right_ct)
.unwrap(); let cst_add = self.engine.create_plaintext_from(&PLAINTEXT_TRUE).unwrap();
self.engine
.fuse_add_lwe_ciphertext_plaintext(&mut buffer_lwe_before_pbs, &cst_add)
.unwrap(); self.engine
.fuse_opp_lwe_ciphertext(&mut buffer_lwe_before_pbs)
.unwrap(); let cst_mul = self.engine.create_cleartext_from(&2u32).unwrap();
self.engine
.fuse_mul_lwe_ciphertext_cleartext(&mut buffer_lwe_before_pbs, &cst_mul)
.unwrap();
bootstrapper
.bootstrap_keyswitch(buffer_lwe_before_pbs, server_key)
.unwrap()
}
}
}
}
impl<B> BinaryGatesEngine<&Ciphertext, bool, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
fn and(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
ct_left.clone()
} else {
self.trivial_encrypt(false)
}
}
fn nand(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
self.not(ct_left)
} else {
self.trivial_encrypt(true)
}
}
fn nor(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
self.trivial_encrypt(false)
} else {
self.not(ct_left)
}
}
fn or(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
self.trivial_encrypt(true)
} else {
ct_left.clone()
}
}
fn xor(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
self.not(ct_left)
} else {
ct_left.clone()
}
}
fn xnor(
&mut self,
ct_left: &Ciphertext,
ct_right: bool,
_server_key: &B::ServerKey,
) -> Ciphertext {
if ct_right {
ct_left.clone()
} else {
self.not(ct_left)
}
}
}
impl<B> BinaryGatesEngine<bool, &Ciphertext, B::ServerKey> for BooleanEngine<B>
where
B: Bootstrapper,
{
fn and(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
self.and(ct_right, ct_left, server_key)
}
fn nand(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
self.nand(ct_right, ct_left, server_key)
}
fn nor(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
self.nor(ct_right, ct_left, server_key)
}
fn or(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
self.or(ct_right, ct_left, server_key)
}
fn xor(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
self.xor(ct_right, ct_left, server_key)
}
fn xnor(
&mut self,
ct_left: bool,
ct_right: &Ciphertext,
server_key: &B::ServerKey,
) -> Ciphertext {
self.xnor(ct_right, ct_left, server_key)
}
}