Skip to content

Commit

Permalink
feat(tfhe): add possibility to expand a ciphertext without verifying it
Browse files Browse the repository at this point in the history
  • Loading branch information
IceTDrinker committed Sep 12, 2024
1 parent b95b07e commit 7b5cd2f
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 0 deletions.
15 changes: 15 additions & 0 deletions tfhe/src/c_api/high_level_api/compact_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
94 changes: 94 additions & 0 deletions tfhe/src/high_level_api/compact_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())),
})
}
}
}

Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -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());
}
}
}
32 changes: 32 additions & 0 deletions tfhe/src/high_level_api/tests/tags_on_entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
72 changes: 72 additions & 0 deletions tfhe/src/integer/ciphertext/compact_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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);
}
}
}
11 changes: 11 additions & 0 deletions tfhe/src/js_on_wasm_api/js_high_level_api/integers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
31 changes: 31 additions & 0 deletions tfhe/src/shortint/ciphertext/zk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,26 @@ impl ProvenCompactCiphertextList {
Ok(expanded)
}

#[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()
.map(|(ct_list, _proof)| ct_list.expand(casting_mode))
.collect::<Result<Vec<Vec<_>>, _>>()?
.into_iter()
.flatten()
.collect();

Ok(expanded)
}

pub fn verify(
&self,
public_params: &CompactPkePublicParams,
Expand Down Expand Up @@ -155,6 +175,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,
Expand Down
14 changes: 14 additions & 0 deletions tfhe/web_wasm_parallel_tests/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

{
Expand Down Expand Up @@ -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 = deserialized.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]);
}
}

Expand Down

0 comments on commit 7b5cd2f

Please sign in to comment.