mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 14:47:56 -05:00
feat(tfhe): add possibility to expand a ciphertext without verifying it
This commit is contained in:
@@ -208,6 +208,21 @@ pub unsafe extern "C" fn proven_compact_ciphertext_list_verify_and_expand(
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "zk-pok")]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn proven_compact_ciphertext_list_expand_without_verification(
|
||||
compact_list: *const ProvenCompactCiphertextList,
|
||||
expander: *mut *mut CompactCiphertextListExpander,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let list = get_ref_checked(compact_list).unwrap();
|
||||
|
||||
let inner = list.0.expand_without_verification().unwrap();
|
||||
|
||||
*expander = Box::into_raw(Box::new(CompactCiphertextListExpander(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn compact_ciphertext_list_expander_len(
|
||||
expander: *mut CompactCiphertextListExpander,
|
||||
|
||||
@@ -292,6 +292,60 @@ mod zk {
|
||||
Some(_) => Err(crate::Error::new("Expected a CPU server key".to_string())),
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// This function allows to expand a ciphertext without verifying the associated proof.
|
||||
///
|
||||
/// If you are here you were probably looking for it: use at your own risks.
|
||||
pub fn expand_without_verification(&self) -> crate::Result<CompactCiphertextListExpander> {
|
||||
// For WASM
|
||||
if !self.inner.is_packed() && !self.inner.needs_casting() {
|
||||
// No ServerKey required, short circuit to avoid the global state call
|
||||
return Ok(CompactCiphertextListExpander {
|
||||
inner: self.inner.expand_without_verification(
|
||||
IntegerCompactCiphertextListUnpackingMode::NoUnpacking,
|
||||
IntegerCompactCiphertextListCastingMode::NoCasting,
|
||||
)?,
|
||||
tag: self.tag.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
global_state::try_with_internal_keys(|maybe_keys| match maybe_keys {
|
||||
None => Err(crate::high_level_api::errors::UninitializedServerKey.into()),
|
||||
Some(InternalServerKey::Cpu(cpu_key)) => {
|
||||
let unpacking_mode = if self.inner.is_packed() {
|
||||
IntegerCompactCiphertextListUnpackingMode::UnpackIfNecessary(
|
||||
cpu_key.pbs_key(),
|
||||
)
|
||||
} else {
|
||||
IntegerCompactCiphertextListUnpackingMode::NoUnpacking
|
||||
};
|
||||
|
||||
let casting_mode = if self.inner.needs_casting() {
|
||||
IntegerCompactCiphertextListCastingMode::CastIfNecessary(
|
||||
cpu_key.cpk_casting_key().ok_or_else(|| {
|
||||
crate::Error::new(
|
||||
"No casting key found in ServerKey, \
|
||||
required to expand this CompactCiphertextList"
|
||||
.to_string(),
|
||||
)
|
||||
})?,
|
||||
)
|
||||
} else {
|
||||
IntegerCompactCiphertextListCastingMode::NoCasting
|
||||
};
|
||||
|
||||
self.inner
|
||||
.expand_without_verification(unpacking_mode, casting_mode)
|
||||
.map(|expander| CompactCiphertextListExpander {
|
||||
inner: expander,
|
||||
tag: self.tag.clone(),
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "gpu")]
|
||||
Some(_) => Err(crate::Error::new("Expected a CPU server key".to_string())),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,6 +601,26 @@ mod tests {
|
||||
// Correct type but wrong number of bits
|
||||
assert!(expander.get::<FheUint16>(0).unwrap().is_err());
|
||||
}
|
||||
|
||||
let unverified_expander = compact_list.expand_without_verification().unwrap();
|
||||
|
||||
{
|
||||
let a: FheUint32 = unverified_expander.get(0).unwrap().unwrap();
|
||||
let b: FheInt64 = unverified_expander.get(1).unwrap().unwrap();
|
||||
let c: FheBool = unverified_expander.get(2).unwrap().unwrap();
|
||||
let d: FheUint2 = unverified_expander.get(3).unwrap().unwrap();
|
||||
|
||||
let a: u32 = a.decrypt(&ck);
|
||||
assert_eq!(a, 17);
|
||||
let b: i64 = b.decrypt(&ck);
|
||||
assert_eq!(b, -1);
|
||||
let c = c.decrypt(&ck);
|
||||
assert!(!c);
|
||||
let d: u8 = d.decrypt(&ck);
|
||||
assert_eq!(d, 3);
|
||||
|
||||
assert!(unverified_expander.get::<FheBool>(4).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zk-pok")]
|
||||
@@ -616,5 +690,25 @@ mod tests {
|
||||
// Correct type but wrong number of bits
|
||||
assert!(expander.get::<FheUint16>(0).unwrap().is_err());
|
||||
}
|
||||
|
||||
let unverified_expander = compact_list.expand_without_verification().unwrap();
|
||||
|
||||
{
|
||||
let a: FheUint32 = unverified_expander.get(0).unwrap().unwrap();
|
||||
let b: FheInt64 = unverified_expander.get(1).unwrap().unwrap();
|
||||
let c: FheBool = unverified_expander.get(2).unwrap().unwrap();
|
||||
let d: FheUint2 = unverified_expander.get(3).unwrap().unwrap();
|
||||
|
||||
let a: u32 = a.decrypt(&ck);
|
||||
assert_eq!(a, 17);
|
||||
let b: i64 = b.decrypt(&ck);
|
||||
assert_eq!(b, -1);
|
||||
let c = c.decrypt(&ck);
|
||||
assert!(!c);
|
||||
let d: u8 = d.decrypt(&ck);
|
||||
assert_eq!(d, 3);
|
||||
|
||||
assert!(unverified_expander.get::<FheBool>(4).is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,38 @@ fn test_tag_propagation_zk_pok() {
|
||||
let cbool = abool & bbool;
|
||||
assert_eq!(cbool.tag(), cks.tag());
|
||||
}
|
||||
|
||||
let unverified_expander = list_packed.expand_without_verification().unwrap();
|
||||
|
||||
{
|
||||
let au32: FheUint32 = unverified_expander.get(0).unwrap().unwrap();
|
||||
let bu32: FheUint32 = unverified_expander.get(1).unwrap().unwrap();
|
||||
assert_eq!(au32.tag(), cks.tag());
|
||||
assert_eq!(bu32.tag(), cks.tag());
|
||||
|
||||
let cu32 = au32 + bu32;
|
||||
assert_eq!(cu32.tag(), cks.tag());
|
||||
}
|
||||
|
||||
{
|
||||
let ai64: FheInt64 = unverified_expander.get(2).unwrap().unwrap();
|
||||
let bi64: FheInt64 = unverified_expander.get(3).unwrap().unwrap();
|
||||
assert_eq!(ai64.tag(), cks.tag());
|
||||
assert_eq!(bi64.tag(), cks.tag());
|
||||
|
||||
let ci64 = ai64 + bi64;
|
||||
assert_eq!(ci64.tag(), cks.tag());
|
||||
}
|
||||
|
||||
{
|
||||
let abool: FheBool = unverified_expander.get(4).unwrap().unwrap();
|
||||
let bbool: FheBool = unverified_expander.get(5).unwrap().unwrap();
|
||||
assert_eq!(abool.tag(), cks.tag());
|
||||
assert_eq!(bbool.tag(), cks.tag());
|
||||
|
||||
let cbool = abool & bbool;
|
||||
assert_eq!(cbool.tag(), cks.tag());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -636,6 +636,62 @@ impl ProvenCompactCiphertextList {
|
||||
))
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// This function allows to expand a ciphertext without verifying the associated proof.
|
||||
///
|
||||
/// If you are here you were probably looking for it: use at your own risks.
|
||||
pub fn expand_without_verification(
|
||||
&self,
|
||||
unpacking_mode: IntegerCompactCiphertextListUnpackingMode<'_>,
|
||||
casting_mode: IntegerCompactCiphertextListCastingMode<'_>,
|
||||
) -> crate::Result<CompactCiphertextListExpander> {
|
||||
let is_packed = self.is_packed();
|
||||
|
||||
if is_packed
|
||||
&& matches!(
|
||||
unpacking_mode,
|
||||
IntegerCompactCiphertextListUnpackingMode::NoUnpacking
|
||||
)
|
||||
{
|
||||
return Err(crate::Error::new(String::from(
|
||||
WRONG_UNPACKING_MODE_ERR_MSG,
|
||||
)));
|
||||
}
|
||||
|
||||
let expanded_blocks = self
|
||||
.ct_list
|
||||
.expand_without_verification(casting_mode.into())?;
|
||||
|
||||
let expanded_blocks = if is_packed {
|
||||
match unpacking_mode {
|
||||
IntegerCompactCiphertextListUnpackingMode::UnpackIfNecessary(sks) => {
|
||||
let degree = self.ct_list.proved_lists[0].0.degree;
|
||||
let mut conformance_params = sks.key.conformance_params();
|
||||
conformance_params.degree = degree;
|
||||
|
||||
for ct in expanded_blocks.iter() {
|
||||
if !ct.is_conformant(&conformance_params) {
|
||||
return Err(crate::Error::new(
|
||||
"This compact list is not conformant with the given server key"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
extract_message_and_carries(expanded_blocks, sks)
|
||||
}
|
||||
IntegerCompactCiphertextListUnpackingMode::NoUnpacking => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
expanded_blocks
|
||||
};
|
||||
|
||||
Ok(CompactCiphertextListExpander::new(
|
||||
expanded_blocks,
|
||||
self.info.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn is_packed(&self) -> bool {
|
||||
self.ct_list.proved_lists[0].0.degree.get()
|
||||
> self.ct_list.proved_lists[0]
|
||||
@@ -731,5 +787,21 @@ mod tests {
|
||||
let decrypted = cks.decrypt_radix::<u64>(&expanded);
|
||||
assert_eq!(msg, decrypted);
|
||||
}
|
||||
|
||||
let unverified_expander = proven_ct
|
||||
.expand_without_verification(
|
||||
IntegerCompactCiphertextListUnpackingMode::UnpackIfNecessary(&sk),
|
||||
IntegerCompactCiphertextListCastingMode::CastIfNecessary(ksk.as_view()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for (idx, msg) in msgs.iter().copied().enumerate() {
|
||||
let expanded = unverified_expander
|
||||
.get::<RadixCiphertext>(idx)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let decrypted = cks.decrypt_radix::<u64>(&expanded);
|
||||
assert_eq!(msg, decrypted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -792,6 +792,17 @@ impl ProvenCompactCiphertextList {
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn expand_without_verification(&self) -> Result<CompactCiphertextListExpander, JsError> {
|
||||
catch_panic_result(|| {
|
||||
let inner = self
|
||||
.0
|
||||
.expand_without_verification()
|
||||
.map_err(into_js_error)?;
|
||||
Ok(CompactCiphertextListExpander(inner))
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn serialize(&self) -> Result<Vec<u8>, JsError> {
|
||||
catch_panic_result(|| bincode::serialize(&self.0).map_err(into_js_error))
|
||||
|
||||
@@ -78,6 +78,18 @@ impl ProvenCompactCiphertextList {
|
||||
return Err(crate::ErrorKind::InvalidZkProof.into());
|
||||
}
|
||||
|
||||
// We can call the function as we have verified the proofs
|
||||
self.expand_without_verification(casting_mode)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// This function allows to expand a ciphertext without verifying the associated proof.
|
||||
///
|
||||
/// If you are here you were probably looking for it: use at your own risks.
|
||||
pub fn expand_without_verification(
|
||||
&self,
|
||||
casting_mode: ShortintCompactCiphertextListCastingMode<'_>,
|
||||
) -> crate::Result<Vec<Ciphertext>> {
|
||||
let expanded = self
|
||||
.proved_lists
|
||||
.iter()
|
||||
@@ -155,6 +167,17 @@ mod tests {
|
||||
encryption_modulus,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let unproven_ct = proven_ct
|
||||
.expand_without_verification(ShortintCompactCiphertextListCastingMode::NoCasting);
|
||||
assert!(unproven_ct.is_ok());
|
||||
let unproven_ct = unproven_ct.unwrap();
|
||||
|
||||
let decrypted = cks.decrypt(&unproven_ct[0]);
|
||||
assert_eq!(msg, decrypted);
|
||||
}
|
||||
|
||||
let proven_ct = proven_ct.verify_and_expand(
|
||||
crs.public_params(),
|
||||
&pk,
|
||||
|
||||
@@ -411,6 +411,10 @@ async function compactPublicKeyZeroKnowledge() {
|
||||
);
|
||||
|
||||
assert_eq(expander.get_uint64(0).decrypt(clientKey), input);
|
||||
|
||||
let unverified_expander = deserialized.expand_without_verification();
|
||||
|
||||
assert_eq(unverified_expander.get_uint64(0).decrypt(clientKey), input);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -450,6 +454,16 @@ async function compactPublicKeyZeroKnowledge() {
|
||||
assert_eq(expander.get_uint64(2).decrypt(clientKey), inputs[2]);
|
||||
|
||||
assert_eq(expander.get_uint64(3).decrypt(clientKey), inputs[3]);
|
||||
|
||||
let unverified_expander = encrypted.expand_without_verification();
|
||||
|
||||
assert_eq(unverified_expander.get_uint64(0).decrypt(clientKey), inputs[0]);
|
||||
|
||||
assert_eq(unverified_expander.get_uint64(1).decrypt(clientKey), inputs[1]);
|
||||
|
||||
assert_eq(unverified_expander.get_uint64(2).decrypt(clientKey), inputs[2]);
|
||||
|
||||
assert_eq(unverified_expander.get_uint64(3).decrypt(clientKey), inputs[3]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user