pub mod atomic_pattern;
pub(crate) mod secret_encryption_key;
use atomic_pattern::{
AtomicPatternClientKey, EncryptionAtomicPattern, StandardAtomicPatternClientKey,
};
use tfhe_versionable::Versionize;
use super::parameters::ShortintKeySwitchingParameters;
use super::server_key::UnsupportedOperation;
use super::{AtomicPatternParameters, PaddingBit, ShortintEncoding};
use crate::core_crypto::entities::*;
use crate::core_crypto::prelude::decrypt_lwe_ciphertext;
use crate::shortint::backward_compatibility::client_key::GenericClientKeyVersions;
use crate::shortint::ciphertext::{Ciphertext, CompressedCiphertext};
use crate::shortint::engine::ShortintEngine;
use crate::shortint::parameters::{DynamicDistribution, MessageModulus, ShortintParameterSet};
use crate::shortint::CarryModulus;
use secret_encryption_key::SecretEncryptionKeyView;
use serde::{Deserialize, Serialize};
use std::fmt::{Debug, Display};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Versionize)]
#[versionize(GenericClientKeyVersions)]
pub struct GenericClientKey<AP> {
pub atomic_pattern: AP,
}
pub type ClientKey = GenericClientKey<AtomicPatternClientKey>;
pub type StandardClientKey = GenericClientKey<StandardAtomicPatternClientKey>;
pub type ClientKeyView<'key> = GenericClientKey<&'key AtomicPatternClientKey>;
pub type StandardClientKeyView<'key> = GenericClientKey<&'key StandardAtomicPatternClientKey>;
impl<'cks> From<&'cks ClientKey> for SecretEncryptionKeyView<'cks> {
fn from(value: &'cks ClientKey) -> Self {
Self {
lwe_secret_key: value.encryption_key(),
message_modulus: value.parameters().message_modulus(),
carry_modulus: value.parameters().carry_modulus(),
}
}
}
impl<'key> TryFrom<ClientKeyView<'key>> for StandardClientKeyView<'key> {
type Error = UnsupportedOperation;
fn try_from(value: ClientKeyView<'key>) -> Result<Self, Self::Error> {
let AtomicPatternClientKey::Standard(atomic_pattern) = value.atomic_pattern else {
return Err(UnsupportedOperation);
};
Ok(Self { atomic_pattern })
}
}
impl<AP> GenericClientKey<AP> {
pub fn as_view(&self) -> GenericClientKey<&AP> {
GenericClientKey {
atomic_pattern: &self.atomic_pattern,
}
}
}
impl ClientKey {
pub fn new<P>(parameters: P) -> Self
where
P: TryInto<ShortintParameterSet>,
<P as TryInto<ShortintParameterSet>>::Error: Debug,
{
ShortintEngine::with_thread_local_mut(|engine| engine.new_client_key(parameters))
}
pub fn try_from_lwe_encryption_key<P>(
encryption_key: LweSecretKeyOwned<u64>,
parameters: P,
) -> crate::Result<Self>
where
P: TryInto<AtomicPatternParameters>,
<P as TryInto<AtomicPatternParameters>>::Error: Display,
{
let parameters = parameters
.try_into()
.map_err(|err| crate::Error::new(format!("{err}")))?;
let atomic_pattern =
AtomicPatternClientKey::try_from_lwe_encryption_key(encryption_key, parameters)?;
Ok(Self { atomic_pattern })
}
}
impl<AP: EncryptionAtomicPattern> GenericClientKey<AP> {
pub fn parameters(&self) -> ShortintParameterSet {
self.atomic_pattern.parameters()
}
pub fn encryption_key(&self) -> LweSecretKeyView<'_, u64> {
self.atomic_pattern.encryption_key()
}
pub fn encryption_key_and_noise(
&self,
) -> (LweSecretKeyView<'_, u64>, DynamicDistribution<u64>) {
self.atomic_pattern.encryption_key_and_noise()
}
#[cfg(test)]
pub fn create_trivial(&self, value: u64) -> Ciphertext {
let modular_value = value % self.parameters().message_modulus().0;
self.unchecked_create_trivial(modular_value)
}
#[cfg(test)]
pub fn unchecked_create_trivial(&self, value: u64) -> Ciphertext {
let params = self.parameters();
let lwe_size = params.encryption_lwe_dimension().to_lwe_size();
super::ciphertext::unchecked_create_trivial_with_lwe_size(
Cleartext(value),
lwe_size,
params.message_modulus(),
params.carry_modulus(),
params.atomic_pattern(),
params.ciphertext_modulus(),
)
}
pub fn encrypt(&self, message: u64) -> Ciphertext {
ShortintEngine::with_thread_local_mut(|engine| engine.encrypt(self, message))
}
pub fn encrypt_compressed(&self, message: u64) -> CompressedCiphertext {
ShortintEngine::with_thread_local_mut(|engine| engine.encrypt_compressed(self, message))
}
pub fn encrypt_with_message_modulus(
&self,
message: u64,
message_modulus: MessageModulus,
) -> Ciphertext {
ShortintEngine::with_thread_local_mut(|engine| {
engine.encrypt_with_message_modulus(self, message, message_modulus)
})
}
pub fn encrypt_with_message_and_carry_modulus(
&self,
message: u64,
message_modulus: MessageModulus,
carry_modulus: CarryModulus,
) -> Ciphertext {
ShortintEngine::with_thread_local_mut(|engine| {
engine.encrypt_with_message_and_carry_modulus(
self,
message,
message_modulus,
carry_modulus,
)
})
}
pub fn encrypt_with_message_modulus_compressed(
&self,
message: u64,
message_modulus: MessageModulus,
) -> CompressedCiphertext {
ShortintEngine::with_thread_local_mut(|engine| {
engine.encrypt_with_message_modulus_compressed(self, message, message_modulus)
})
}
pub fn unchecked_encrypt(&self, message: u64) -> Ciphertext {
ShortintEngine::with_thread_local_mut(|engine| engine.unchecked_encrypt(self, message))
}
pub fn decrypt_message_and_carry(&self, ct: &Ciphertext) -> u64 {
let decrypted_u64 = self.decrypt_no_decode(ct);
ShortintEncoding::from_parameters(self.parameters(), PaddingBit::Yes)
.decode(decrypted_u64)
.0
}
pub fn decrypt(&self, ct: &Ciphertext) -> u64 {
self.decrypt_message_and_carry(ct) % ct.message_modulus.0
}
pub fn decrypt_no_decode(&self, ct: &Ciphertext) -> Plaintext<u64> {
let lwe_decryption_key = self.encryption_key();
decrypt_lwe_ciphertext(&lwe_decryption_key, &ct.ct)
}
pub fn encrypt_without_padding(&self, message: u64) -> Ciphertext {
ShortintEngine::with_thread_local_mut(|engine| {
engine.encrypt_without_padding(self, message)
})
}
pub fn encrypt_without_padding_compressed(&self, message: u64) -> CompressedCiphertext {
ShortintEngine::with_thread_local_mut(|engine| {
engine.encrypt_without_padding_compressed(self, message)
})
}
pub fn decrypt_message_and_carry_without_padding(&self, ct: &Ciphertext) -> u64 {
let decrypted_u64 = self.decrypt_no_decode(ct);
ShortintEncoding::from_parameters(self.parameters(), PaddingBit::No)
.decode(decrypted_u64)
.0
}
pub fn decrypt_without_padding(&self, ct: &Ciphertext) -> u64 {
self.decrypt_message_and_carry_without_padding(ct) % ct.message_modulus.0
}
pub fn encrypt_native_crt(&self, message: u64, message_modulus: MessageModulus) -> Ciphertext {
ShortintEngine::with_thread_local_mut(|engine| {
engine.encrypt_native_crt(self, message, message_modulus)
})
}
pub fn encrypt_native_crt_compressed(
&self,
message: u64,
message_modulus: MessageModulus,
) -> CompressedCiphertext {
ShortintEngine::with_thread_local_mut(|engine| {
engine.encrypt_native_crt_compressed(self, message, message_modulus)
})
}
pub fn decrypt_message_native_crt(
&self,
ct: &Ciphertext,
message_modulus: MessageModulus,
) -> u64 {
let basis = message_modulus.0;
let decrypted_u64: u64 = self.decrypt_no_decode(ct).0;
let mut result = decrypted_u64 as u128 * basis as u128;
result = result.wrapping_add((result & (1 << 63)) << 1) / (1 << 64);
result as u64 % basis
}
}
impl StandardClientKeyView<'_> {
pub fn keyswitch_encryption_key_and_noise(
&self,
params: ShortintKeySwitchingParameters,
) -> (LweSecretKeyView<'_, u64>, DynamicDistribution<u64>) {
self.atomic_pattern
.keyswitch_encryption_key_and_noise(params)
}
}