Skip to main content

tfhe/high_level_api/strings/ascii/
mod.rs

1mod comp;
2mod contains;
3mod find;
4mod no_pattern;
5mod replace;
6mod strip;
7mod trim;
8
9pub use crate::high_level_api::backward_compatibility::strings::FheAsciiStringVersions;
10use crate::high_level_api::compressed_ciphertext_list::ToBeCompressed;
11use crate::high_level_api::details::MaybeCloned;
12use crate::high_level_api::errors::UninitializedServerKey;
13use crate::high_level_api::global_state;
14use crate::high_level_api::keys::InternalServerKey;
15use crate::high_level_api::re_randomization::ReRandomizationMetadata;
16use crate::integer::ciphertext::{Compressible, DataKind, Expandable};
17use crate::named::Named;
18use crate::prelude::{FheDecrypt, FheTryEncrypt, FheTryTrivialEncrypt, Tagged};
19use crate::shortint::ciphertext::NotTrivialCiphertextError;
20use crate::strings::ciphertext::FheString;
21use crate::{ClientKey, HlExpandable, Tag};
22pub use no_pattern::{FheStringIsEmpty, FheStringLen};
23use serde::{Deserialize, Deserializer, Serialize, Serializer};
24use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned};
25
26pub enum EncryptableString<'a> {
27    NoPadding(&'a str),
28    WithPadding { str: &'a str, padding: u32 },
29}
30
31impl EncryptableString<'_> {
32    fn str_and_padding(&self) -> (&str, Option<u32>) {
33        match self {
34            EncryptableString::NoPadding(str) => (str, None),
35            EncryptableString::WithPadding { str, padding } => (str, Some(*padding)),
36        }
37    }
38}
39
40pub(crate) enum AsciiDevice {
41    Cpu(FheString),
42}
43
44impl From<FheString> for AsciiDevice {
45    fn from(value: FheString) -> Self {
46        Self::Cpu(value)
47    }
48}
49
50impl AsciiDevice {
51    pub fn on_cpu(&self) -> MaybeCloned<'_, FheString> {
52        match self {
53            Self::Cpu(cpu_string) => MaybeCloned::Borrowed(cpu_string),
54        }
55    }
56}
57
58impl Clone for AsciiDevice {
59    fn clone(&self) -> Self {
60        match self {
61            Self::Cpu(s) => Self::Cpu(s.clone()),
62        }
63    }
64}
65
66impl serde::Serialize for AsciiDevice {
67    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
68    where
69        S: Serializer,
70    {
71        self.on_cpu().serialize(serializer)
72    }
73}
74
75impl<'de> serde::Deserialize<'de> for AsciiDevice {
76    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
77    where
78        D: Deserializer<'de>,
79    {
80        let deserialized = Self::Cpu(FheString::deserialize(deserializer)?);
81        Ok(deserialized)
82    }
83}
84
85// Only CPU data are serialized so we only versionize the CPU type.
86#[derive(serde::Serialize, serde::Deserialize)]
87#[cfg_attr(dylint_lib = "tfhe_lints", allow(serialize_without_versionize))]
88pub(crate) struct AsciiDeviceVersionOwned(<FheString as VersionizeOwned>::VersionedOwned);
89
90#[derive(Serialize, Deserialize)]
91#[cfg_attr(dylint_lib = "tfhe_lints", allow(serialize_without_versionize))]
92pub(crate) enum AsciiDeviceVersionedOwned {
93    V0(AsciiDeviceVersionOwned),
94}
95
96impl Versionize for AsciiDevice {
97    type Versioned<'vers> = AsciiDeviceVersionedOwned;
98
99    fn versionize(&self) -> Self::Versioned<'_> {
100        let data = self.on_cpu();
101        let versioned = data.into_owned().versionize_owned();
102        AsciiDeviceVersionedOwned::V0(AsciiDeviceVersionOwned(versioned))
103    }
104}
105
106impl VersionizeOwned for AsciiDevice {
107    type VersionedOwned = AsciiDeviceVersionedOwned;
108
109    fn versionize_owned(self) -> Self::VersionedOwned {
110        let cpu_data = self.on_cpu();
111        AsciiDeviceVersionedOwned::V0(AsciiDeviceVersionOwned(
112            cpu_data.into_owned().versionize_owned(),
113        ))
114    }
115}
116
117impl Unversionize for AsciiDevice {
118    fn unversionize(versioned: Self::VersionedOwned) -> Result<Self, UnversionizeError> {
119        match versioned {
120            AsciiDeviceVersionedOwned::V0(v0) => {
121                let unversioned = Self::Cpu(FheString::unversionize(v0.0)?);
122                Ok(unversioned)
123            }
124        }
125    }
126}
127
128#[derive(Serialize, Deserialize, Versionize, Clone)]
129#[versionize(FheAsciiStringVersions)]
130pub struct FheAsciiString {
131    pub(crate) inner: AsciiDevice,
132    pub(crate) tag: Tag,
133    pub(crate) re_randomization_metadata: ReRandomizationMetadata,
134}
135
136impl Named for FheAsciiString {
137    const NAME: &'static str = "high_level_api::FheAsciiString";
138}
139
140impl Tagged for FheAsciiString {
141    fn tag(&self) -> &Tag {
142        &self.tag
143    }
144
145    fn tag_mut(&mut self) -> &mut Tag {
146        &mut self.tag
147    }
148}
149
150impl FheAsciiString {
151    pub(crate) fn new(
152        inner: impl Into<AsciiDevice>,
153        tag: Tag,
154        re_randomization_metadata: ReRandomizationMetadata,
155    ) -> Self {
156        Self {
157            inner: inner.into(),
158            tag,
159            re_randomization_metadata,
160        }
161    }
162
163    /// Encrypts the string `str` and adds `padding` blocks of padding (encryption of zero)
164    pub fn try_encrypt_with_padding(
165        str: impl AsRef<str>,
166        padding: u32,
167        client_key: &ClientKey,
168    ) -> crate::Result<Self> {
169        Self::try_encrypt(
170            EncryptableString::WithPadding {
171                str: str.as_ref(),
172                padding,
173            },
174            client_key,
175        )
176    }
177
178    /// Encrypts the string `str` with a fixed size `size`
179    ///
180    /// * If the input str is shorter than size, it will be padded with encryptions of 0
181    /// * If the input str is longer than size, it will be truncated
182    ///
183    /// # Example
184    ///
185    /// ```
186    /// use tfhe::prelude::*;
187    /// use tfhe::safe_serialization::safe_serialize;
188    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheAsciiString, FheStringLen};
189    ///
190    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
191    /// set_server_key(server_key);
192    ///
193    /// // The input string is shorter
194    /// let string1 = FheAsciiString::try_encrypt_with_fixed_sized("tfhe", 5, &client_key).unwrap();
195    /// match string1.len() {
196    ///     FheStringLen::NoPadding(_) => {
197    ///         panic!("Expected padding")
198    ///     }
199    ///     FheStringLen::Padding(len) => {
200    ///         let len: u16 = len.decrypt(&client_key);
201    ///         // The padding is not part of the len
202    ///         assert_eq!(len, 4);
203    ///     }
204    /// }
205    /// assert_eq!(string1.decrypt(&client_key), "tfhe".to_string());
206    ///
207    /// // The input string is longer
208    /// let string2 = FheAsciiString::try_encrypt_with_fixed_sized("tfhe-rs", 5, &client_key).unwrap();
209    /// match string2.len() {
210    ///     FheStringLen::NoPadding(len) => {
211    ///         assert_eq!(len, 5);
212    ///     }
213    ///     FheStringLen::Padding(len) => {
214    ///         panic!("Unexpected padding");
215    ///     }
216    /// }
217    /// assert_eq!(string2.decrypt(&client_key), "tfhe-".to_string());
218    ///
219    /// let mut buffer1 = vec![];
220    /// safe_serialize(&string1, &mut buffer1, 1 << 30).unwrap();
221    /// let mut buffer2 = vec![];
222    /// safe_serialize(&string2, &mut buffer2, 1 << 30).unwrap();
223    /// // But they have the same 'size'
224    /// assert_eq!(buffer1.len(), buffer2.len())
225    /// ```
226    pub fn try_encrypt_with_fixed_sized(
227        str: impl AsRef<str>,
228        size: usize,
229        client_key: &ClientKey,
230    ) -> crate::Result<Self> {
231        let str = str.as_ref();
232        let (sliced, padding) = if str.len() >= size {
233            (&str[..size], 0)
234        } else {
235            (str, (size - str.len()) as u32)
236        };
237
238        Self::try_encrypt(
239            EncryptableString::WithPadding {
240                str: sliced,
241                padding,
242            },
243            client_key,
244        )
245    }
246
247    /// Trivially encrypts the string `str` and adds `padding` blocks of padding (encryption of
248    /// zero)
249    /// # Example
250    ///
251    /// ```
252    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheAsciiString};
253    ///
254    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
255    /// set_server_key(server_key);
256    ///
257    /// // The input string is shorter
258    /// let string1 = FheAsciiString::try_encrypt_trivial_with_padding("tfhe", 5).unwrap();
259    /// assert!(string1.is_trivial());
260    /// assert_eq!(string1.try_decrypt_trivial(), Ok("tfhe".to_string()));
261    /// ```
262    pub fn try_encrypt_trivial_with_padding(
263        str: impl AsRef<str>,
264        padding: u32,
265    ) -> crate::Result<Self> {
266        Self::try_encrypt_trivial(EncryptableString::WithPadding {
267            str: str.as_ref(),
268            padding,
269        })
270    }
271
272    /// Trivially encrypts the string `str` with a fixed size `size`
273    ///
274    /// * If the input str is shorter than size, it will be padded with encryptions of 0
275    /// * If the input str is longer than size, it will be truncated
276    ///
277    /// # Example
278    ///
279    /// ```
280    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheAsciiString};
281    ///
282    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
283    /// set_server_key(server_key);
284    ///
285    /// // The input string is shorter
286    /// let string1 = FheAsciiString::try_encrypt_trivial_with_fixed_sized("tfhe", 5).unwrap();
287    /// assert!(string1.is_trivial());
288    /// assert_eq!(string1.try_decrypt_trivial(), Ok("tfhe".to_string()));
289    ///
290    /// // The input string is longer
291    /// let string2 = FheAsciiString::try_encrypt_trivial_with_fixed_sized("tfhe-rs", 5).unwrap();
292    /// assert!(string2.is_trivial());
293    /// assert_eq!(string2.try_decrypt_trivial(), Ok("tfhe-".to_string()));
294    /// ```
295    pub fn try_encrypt_trivial_with_fixed_sized(
296        str: impl AsRef<str>,
297        size: usize,
298    ) -> crate::Result<Self> {
299        let str = str.as_ref();
300        let (sliced, padding) = if str.len() >= size {
301            (&str[..size], 0)
302        } else {
303            (str, (size - str.len()) as u32)
304        };
305
306        Self::try_encrypt_trivial(EncryptableString::WithPadding {
307            str: sliced,
308            padding,
309        })
310    }
311
312    pub fn try_decrypt_trivial(&self) -> Result<String, NotTrivialCiphertextError> {
313        self.inner.on_cpu().decrypt_trivial()
314    }
315
316    pub fn is_trivial(&self) -> bool {
317        self.inner.on_cpu().is_trivial()
318    }
319
320    pub fn re_randomization_metadata(&self) -> &ReRandomizationMetadata {
321        &self.re_randomization_metadata
322    }
323
324    pub fn re_randomization_metadata_mut(&mut self) -> &mut ReRandomizationMetadata {
325        &mut self.re_randomization_metadata
326    }
327}
328
329impl<'a> FheTryEncrypt<EncryptableString<'a>, ClientKey> for FheAsciiString {
330    type Error = crate::Error;
331
332    fn try_encrypt(value: EncryptableString<'a>, key: &ClientKey) -> Result<Self, Self::Error> {
333        let (str, padding) = value.str_and_padding();
334        if !str.is_ascii() || str.contains('\0') {
335            return Err(crate::Error::new(
336                "Input is not an ASCII string".to_string(),
337            ));
338        }
339
340        let inner = crate::strings::ClientKey::new(&key.key.key).encrypt_ascii(str, padding);
341        Ok(Self {
342            inner: inner.into(),
343            tag: key.tag.clone(),
344            re_randomization_metadata: ReRandomizationMetadata::default(),
345        })
346    }
347}
348
349impl FheTryEncrypt<&str, ClientKey> for FheAsciiString {
350    type Error = crate::Error;
351
352    fn try_encrypt(value: &str, key: &ClientKey) -> Result<Self, Self::Error> {
353        Self::try_encrypt(EncryptableString::NoPadding(value), key)
354    }
355}
356
357impl FheTryEncrypt<&String, ClientKey> for FheAsciiString {
358    type Error = crate::Error;
359
360    fn try_encrypt(value: &String, key: &ClientKey) -> Result<Self, Self::Error> {
361        Self::try_encrypt(EncryptableString::NoPadding(value), key)
362    }
363}
364
365impl<'a> FheTryTrivialEncrypt<EncryptableString<'a>> for FheAsciiString {
366    type Error = crate::Error;
367
368    fn try_encrypt_trivial(value: EncryptableString<'a>) -> Result<Self, Self::Error> {
369        let (str, padding) = value.str_and_padding();
370
371        if !str.is_ascii() || str.contains('\0') {
372            return Err(crate::Error::new(
373                "Input is not an ASCII string".to_string(),
374            ));
375        }
376
377        global_state::try_with_internal_keys(|keys| match keys {
378            Some(InternalServerKey::Cpu(cpu_key)) => {
379                let inner = cpu_key.string_key().trivial_encrypt_ascii(str, padding);
380                Ok(Self::new(
381                    inner,
382                    cpu_key.tag.clone(),
383                    ReRandomizationMetadata::default(),
384                ))
385            }
386            #[cfg(feature = "gpu")]
387            Some(InternalServerKey::Cuda(_)) => Err(crate::error!("CUDA does not support string")),
388            #[cfg(feature = "hpu")]
389            Some(InternalServerKey::Hpu(_)) => Err(crate::error!("Hpu does not support string")),
390            None => Err(UninitializedServerKey.into()),
391        })
392    }
393}
394
395impl FheTryTrivialEncrypt<&str> for FheAsciiString {
396    type Error = crate::Error;
397
398    fn try_encrypt_trivial(value: &str) -> Result<Self, Self::Error> {
399        Self::try_encrypt_trivial(EncryptableString::NoPadding(value))
400    }
401}
402
403impl FheTryTrivialEncrypt<&String> for FheAsciiString {
404    type Error = crate::Error;
405
406    fn try_encrypt_trivial(value: &String) -> Result<Self, Self::Error> {
407        Self::try_encrypt_trivial(EncryptableString::NoPadding(value.as_str()))
408    }
409}
410
411impl FheDecrypt<String> for FheAsciiString {
412    fn decrypt(&self, key: &ClientKey) -> String {
413        crate::strings::ClientKey::new(&key.key.key).decrypt_ascii(&self.inner.on_cpu())
414    }
415}
416
417impl Expandable for FheAsciiString {
418    fn from_expanded_blocks(
419        blocks: Vec<crate::shortint::Ciphertext>,
420        kind: DataKind,
421    ) -> crate::Result<Self> {
422        FheString::from_expanded_blocks(blocks, kind).map(|cpu_string| {
423            Self::new(
424                cpu_string,
425                Tag::default(),
426                ReRandomizationMetadata::default(),
427            )
428        })
429    }
430}
431
432impl crate::HlCompactable for &crate::ClearString {}
433
434#[cfg(feature = "gpu")]
435impl crate::integer::gpu::ciphertext::compressed_ciphertext_list::CudaExpandable
436    for FheAsciiString
437{
438    fn from_expanded_blocks(
439        blocks: crate::integer::gpu::ciphertext::CudaRadixCiphertext,
440        kind: DataKind,
441    ) -> crate::Result<Self> {
442        let _ = (blocks, kind);
443        Err(crate::error!("GPU does not supports strings yet"))
444    }
445}
446
447impl crate::HlCompressible for FheAsciiString {
448    fn compress_into(self, messages: &mut Vec<(ToBeCompressed, DataKind)>) {
449        match self.inner {
450            AsciiDevice::Cpu(fhe_string) => {
451                let mut blocks = vec![];
452                let data_kind = fhe_string.compress_into(&mut blocks);
453                if let Some(data_kind) = data_kind {
454                    messages.push((ToBeCompressed::Cpu(blocks), data_kind));
455                }
456            }
457        }
458    }
459
460    fn get_re_randomization_metadata(&self) -> ReRandomizationMetadata {
461        self.re_randomization_metadata.clone()
462    }
463}
464
465impl HlExpandable for FheAsciiString {
466    fn set_re_randomization_metadata(&mut self, meta: ReRandomizationMetadata) {
467        self.re_randomization_metadata = meta;
468    }
469}