cretrit/
cipher.rs

1//! Where the `Cipher` lives.
2//!
3
4use core::fmt::Debug;
5use rand::{Rng, SeedableRng};
6use std::cell::RefCell;
7use std::marker::PhantomData;
8
9use crate::ciphersuite::CipherSuite;
10use crate::ciphertext::CipherText;
11use crate::cmp::Comparator;
12use crate::kbkdf::{KBKDFInit, KBKDF};
13use crate::plaintext::PlainText;
14use crate::prf::{PseudoRandomFunction, PseudoRandomFunctionInit};
15use crate::prp::{PseudoRandomPermutation, PseudoRandomPermutationInit};
16use crate::Error;
17
18/// Something capable of turning [`PlainText`s](crate::PlainText) into comparable
19/// [`CipherText`s](crate::CipherText) by means of encryption.
20///
21/// A Cipher consists of a key, a ciphersuite (which is, itself, a whole bunch of possibilities
22/// jammed into one neat little package), a comparator (which defines how ciphertexts can be
23/// compared while encrypted), as well as parameters for the number of blocks (`N`), the "width" of
24/// each block (the total number of values that each block can represent, `W`), and the number of
25/// values that the comparator is able to use to represent comparison values (`M`).
26///
27/// Since all of this is a heck of a lot to have to specify everytime you want to encrypt
28/// something, it's not recommended that you try and use this type directly.  Instead, use the
29/// Cipher types provided by the comparison-specific modules defined by each available
30/// ciphersuites.  At the moment, those are:
31///
32/// * [`aes128v1`](crate::aes128v1) -- ciphersuite using AES128 as the primary cryptographic
33/// primitive, which provides
34///   * [`ere::Cipher`](crate::aes128v1::ere::Cipher) for equality comparisons (`==`, `!=`), and
35///   * [`ore::Cipher`](crate::aes128v1::ore::Cipher) for ordering comparisons (`<`, `>`, `<=`,
36///   `>=`, `==`, `!=`).
37///
38///
39/// These more-contrained Cipher types only require you to specify the block count and width (`N`
40/// and `W`) and the key to use for encryption, which is far more tractable.
41///
42#[derive(Clone)]
43pub struct Cipher<
44    S: CipherSuite<W, M>,
45    CMP: Comparator<M>,
46    const N: usize,
47    const W: u16,
48    const M: u8,
49> {
50    /// The CSPRNG we're using for our random numbers
51    rng: RefCell<S::RNG>,
52
53    /// The instance of the PRF in use
54    prf: S::PRF,
55
56    /// The instance of the PRP in use
57    prp: S::PRP,
58
59    /// Bumf to keep the compiler happy
60    _ffs: PhantomData<CMP>,
61}
62
63impl<S: CipherSuite<W, M>, CMP: Comparator<M>, const N: usize, const W: u16, const M: u8> Debug
64    for Cipher<S, CMP, N, W, M>
65{
66    fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
67        Ok(())
68    }
69}
70
71impl<S: CipherSuite<W, M>, CMP: Comparator<M>, const N: usize, const W: u16, const M: u8>
72    Cipher<S, CMP, N, W, M>
73{
74    /// Create a new Cipher.
75    ///
76    /// All ciphertexts produced with the same key (and all other parameters) can be compared
77    /// against each other.  As such, it is just as important that the key used for these
78    /// encryptions is as secure and secret as any other cryptographic key.
79    ///
80    /// # Errors
81    ///
82    /// Can return an error if any of the underlying cryptographic operations can't complete, or if
83    /// there's a bug somewhere.
84    ///
85    pub fn new(key: &[u8; 32]) -> Result<Self, Error>
86    where
87        <S as CipherSuite<W, M>>::PRF: PseudoRandomFunctionInit,
88        <S as CipherSuite<W, M>>::PRP: PseudoRandomPermutationInit<W>,
89        <S as CipherSuite<W, M>>::KBKDF: 'static,
90    {
91        #![allow(clippy::similar_names)] // I think we can keep things clear in here, prf/prp is totes different
92        let kbkdf: Box<dyn KBKDF> = S::KBKDF::new(key)
93            .map_err(|e| Error::KeyError(format!("failed to create KBKDF instance: {e}")))?;
94
95        let prf: S::PRF = PseudoRandomFunctionInit::new(&*kbkdf)?;
96        let prp: S::PRP = PseudoRandomPermutationInit::new(&*kbkdf)?;
97        let rng: S::RNG = SeedableRng::from_entropy();
98
99        Ok(Cipher {
100            rng: RefCell::new(rng),
101            prf,
102            prp,
103            _ffs: PhantomData,
104        })
105    }
106
107    /// Encrypt a value and produce a ciphertext that contains both "left" and "right" parts
108    ///
109    /// For details on ciphertexts and their components, see the struct-level documentation for
110    /// [`CipherText`](crate::CipherText).
111    ///
112    /// # Errors
113    ///
114    /// Can return an error if any of the underlying cryptographic operations can't complete, or if
115    /// there's a bug somewhere.
116    ///
117    pub fn full_encrypt(
118        &self,
119        value: &PlainText<N, W>,
120    ) -> Result<CipherText<S, CMP, N, W, M>, Error> {
121        CipherText::<S, CMP, N, W, M>::new(self, value)
122    }
123
124    /// Encrypt a value and produce a ciphertext that contains only a "right" part
125    ///
126    /// For details on ciphertexts and their components, see the struct-level documentation for
127    /// [`CipherText`](crate::CipherText).
128    ///
129    /// # Errors
130    ///
131    /// Can return an error if any of the underlying cryptographic operations can't complete, or if
132    /// there's a bug somewhere.
133    ///
134    pub fn right_encrypt(
135        &self,
136        value: &PlainText<N, W>,
137    ) -> Result<CipherText<S, CMP, N, W, M>, Error> {
138        CipherText::<S, CMP, N, W, M>::new_right(self, value)
139    }
140
141    /// Write a random value into the given slice
142    ///
143    /// # Errors
144    ///
145    /// Can return an error if any of the underlying cryptographic operations can't complete, or if
146    /// there's a bug somewhere.
147    ///
148    pub(crate) fn fill_nonce(&self, nonce: &mut [u8]) -> Result<(), Error> {
149        self.rng
150            .borrow_mut()
151            .try_fill(nonce)
152            .map_err(|e| Error::CryptoError(format!("RNG failed to fill random bytes ({e})")))?;
153
154        Ok(())
155    }
156
157    /// Calculate the pseudo-random block corresponding to the given value
158    ///
159    /// Writes the result into the given block, rather than return by value, because the data can
160    /// be of non-trivial size, and the caller has already allocated the space anyway.
161    ///
162    pub(crate) fn pseudorandomise(
163        &self,
164        value: u16,
165        block: &mut <<S as CipherSuite<W, M>>::PRF as PseudoRandomFunction>::BlockType,
166    ) {
167        self.prf.randomise(value, block);
168    }
169
170    /// Return the value->permutation mapping for the given value
171    ///
172    /// # Errors
173    ///
174    /// Can return an error if any of the underlying cryptographic operations can't complete, or if
175    /// there's a bug somewhere.
176    ///
177    pub(crate) fn permuted_value(&self, value: u16) -> Result<u16, Error> {
178        if value >= W {
179            return Err(Error::RangeError(format!(
180                "permuted_value received value={value} greater than block width W={W}"
181            )));
182        }
183        self.prp.value(value)
184    }
185
186    /// Return the permutation->value mapping
187    ///
188    /// # Errors
189    ///
190    /// Can return an error if any of the underlying cryptographic operations can't complete, or if
191    /// there's a bug somewhere.
192    ///
193    pub(crate) fn inverse_permuted_value(&self, permutation: u16) -> Result<u16, Error> {
194        if permutation >= W {
195            return Err(Error::RangeError(format!("inverse_permuted_value received permutation={permutation} greater than block width W={W}")));
196        }
197        self.prp.inverse(permutation)
198    }
199}