From aa9fbde75e0739f9ba632333c6a48441a58a4ede Mon Sep 17 00:00:00 2001 From: tmontaigu Date: Wed, 8 Jan 2025 14:54:37 +0100 Subject: [PATCH] chore(docs): add strings guides --- Makefile | 2 +- tfhe/docs/SUMMARY.md | 1 + tfhe/docs/guides/strings.md | 97 +++++++++++++++++++++++++++++++++++++ tfhe/src/test_user_docs.rs | 1 + 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 tfhe/docs/guides/strings.md diff --git a/Makefile b/Makefile index bb1c55c91f..4c85ac43c0 100644 --- a/Makefile +++ b/Makefile @@ -824,7 +824,7 @@ test_strings: install_rs_build_toolchain .PHONY: test_user_doc # Run tests from the .md documentation test_user_doc: install_rs_build_toolchain RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) --doc \ - --features=boolean,shortint,integer,internal-keycache,pbs-stats,zk-pok \ + --features=boolean,shortint,integer,internal-keycache,pbs-stats,zk-pok,strings \ -p $(TFHE_SPEC) \ -- test_user_docs:: diff --git a/tfhe/docs/SUMMARY.md b/tfhe/docs/SUMMARY.md index 399133cd1d..92a92ddb08 100644 --- a/tfhe/docs/SUMMARY.md +++ b/tfhe/docs/SUMMARY.md @@ -42,6 +42,7 @@ * [Trivial ciphertexts](guides/trivial\_ciphertext.md) * [PBS statistics](guides/pbs-stats.md) * [Array](guides/array.md) +* [Strings](guides/strings.md) ## Tutorials diff --git a/tfhe/docs/guides/strings.md b/tfhe/docs/guides/strings.md new file mode 100644 index 0000000000..1b0bc8a880 --- /dev/null +++ b/tfhe/docs/guides/strings.md @@ -0,0 +1,97 @@ +# Strings + +This document explains the FheAsciiString type for handling encrypted strings in TFHE-rs. + +TFHE-rs has supports for ASCII strings with the type FheAsciiString. +You can enable this feature using the flag: --features=strings + +{% hint style="warning" %} +Strings are not yet compatible with `CompactCiphertextList` and `CompressedCiphertextList` +{% endhint %} + +## Supported Operations + +A variety of common operations are supported for `FheAsciiString`. These include: + +- **Comparisons**: `eq`, `ne`, `lt`, `le`, `gt`, `ge`, `eq_ignore_case` +- **Case conversion**: `to_lowercase` / `to_uppercase` +- **String checks**: `starts_with` / `ends_with` / `contains` +- **Trimming**: `trim_start` / `trim_end` / `trim` +- **Prefix/suffix operations**: `strip_prefix` / `strip_suffix` +- **Search**: `find` / `rfind` + + +When encrypting strings, you can add padding to hide the actual length of strings. +The null character (b'\0') is used as the padding. +Here is an example: + +```toml +# Cargo.toml + +[dependencies] +tfhe = { version = "0.11.0", features = ["integer", "strings"] } +``` + +```rust +use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheAsciiString, FheStringLen, ClearString}; +use tfhe::prelude::*; +use tfhe::safe_serialization::safe_serialize; + + +fn main() { + let config = ConfigBuilder::default().build(); + let (cks, sks) = generate_keys(config); + + set_server_key(sks); + + let r = FheAsciiString::try_encrypt("café is french for coffee", &cks); + // As the input string is not strictly ASCII, it is not compatible + assert!(r.is_err()); + + let string = FheAsciiString::try_encrypt("tfhe-rs", &cks).unwrap(); + // This adds 3 chars of padding to the chars of the input string + let padded_string = FheAsciiString::try_encrypt_with_padding("tfhe-rs", 3, &cks).unwrap(); + // This makes it so the string has 10 chars (adds padding or truncates input as necessary) + let other_string = FheAsciiString::try_encrypt_with_fixed_sized("tfhe", 10, &cks).unwrap(); + + let mut buffer1 = vec![]; + safe_serialize(&padded_string, &mut buffer1, 1 << 30).unwrap(); + let mut buffer2 = vec![]; + safe_serialize(&other_string, &mut buffer2, 1 << 30).unwrap(); + // The two strings created with padding, have the same + // memory/disk footprint, even though the lengths are not the same + assert_eq!(buffer1.len(), buffer2.len()); + + // When a string has no padding, its length is known in clear + let len = string.len(); + assert!(matches!(len, FheStringLen::NoPadding(7))); + // When a string has padding, its length is only known as an encrypted value + let FheStringLen::Padding(encrypted_len) = padded_string.len() else { + panic!("Expected len to be encrypted"); + }; + let padded_string_len: u16 = encrypted_len.decrypt(&cks); + assert_eq!(padded_string_len, 7); // Note padding chars are not counted + // The enum resulting of a len() / is_empty() call can be transformed + // to a FheUint16 using `into_ciphertext` + assert!(string.len().into_ciphertext().is_trivial()); + assert!(!padded_string.len().into_ciphertext().is_trivial()); + let other_string_len: u16 = other_string.len().into_ciphertext().decrypt(&cks); + assert_eq!(other_string_len, 4); + + // Padded and un-padded strings are equal if the content is + assert!(padded_string.eq(&string).decrypt(&cks)); + + let prefix = ClearString::new("tfhe".to_string()); + let (stripped_string, has_been_stripped) = string.strip_prefix(&prefix); + // Notice that stripping, makes the string as being considered as padded + // as it is not possible to homomorphically remove chars + let FheStringLen::Padding(encrypted_len) = stripped_string.len() else { + panic!("Expected len to be encrypted"); + }; + let stripped_string_len: u16 = encrypted_len.decrypt(&cks); + assert_eq!(stripped_string_len, 3); + let decrypted = stripped_string.decrypt(&cks); + assert_eq!(decrypted, "-rs"); + assert!(has_been_stripped.decrypt(&cks)); +} +``` diff --git a/tfhe/src/test_user_docs.rs b/tfhe/src/test_user_docs.rs index 0226a31160..257f98b163 100644 --- a/tfhe/src/test_user_docs.rs +++ b/tfhe/src/test_user_docs.rs @@ -57,6 +57,7 @@ mod test_cpu_doc { doctest!("../docs/guides/pbs-stats.md", guides_pbs_stats); doctest!("../docs/guides/public_key.md", guides_public_key); doctest!("../docs/guides/rayon_crate.md", guides_rayon_crate); + doctest!("../docs/guides/strings.md", guides_strings); doctest!("../docs/guides/trait_bounds.md", guides_trait_bounds); doctest!( "../docs/guides/trivial_ciphertext.md",