Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(docs): add strings guides #1944

Merged
merged 1 commit into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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::

Expand Down
1 change: 1 addition & 0 deletions tfhe/docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
97 changes: 97 additions & 0 deletions tfhe/docs/guides/strings.md
Original file line number Diff line number Diff line change
@@ -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));
}
```
1 change: 1 addition & 0 deletions tfhe/src/test_user_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading