Skip to content

Commit

Permalink
chore(docs): add strings guides
Browse files Browse the repository at this point in the history
  • Loading branch information
tmontaigu committed Jan 7, 2025
1 parent bc742e9 commit 79523f9
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 1 deletion.
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
94 changes: 94 additions & 0 deletions tfhe/docs/guides/strings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Strings

This document explains the string type provided by the High-level API.

TFHE-RS has supports for **ASCII** strings with the type `FheAsciiString`.

{% hint style="info" %}
You can enable this feature using the flag: `--features=strings` when building **TFHE-rs**.
{% endhint %}

A lot of common operations are supported, to list a few of them:

- comparisons (`eq`, `ne`, `lt`, `le`, `gt`, `ge`, `eq_ignore_case`)
- `to_lowercase` / `to_uppercase`
- `starts_with` / `ends_with`
- `trim_start` / `trim_end` / `trim`
- `strip_prefix` / `strip_suffix`
- `contains` / `find` / `rfind`


At encryption, strings can be encrypted with some *padding*.
The null character (b'\0') is used as the padding character, the *padding* mechanism
can be used to hide the actual length of the string.

```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
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

0 comments on commit 79523f9

Please sign in to comment.