Skip to main content

ore_rs/
ciphertext.rs

1use thiserror::Error;
2
3use crate::primitives::NONCE_SIZE;
4pub use crate::OreCipher;
5
6/// The trait of any encryption output (either Left, Right or combined).
7pub trait OreOutput: Sized {
8    /// The size (in bytes) of this encrypted value
9    fn size() -> usize;
10
11    /// Convert to bytes
12    fn to_bytes(&self) -> Vec<u8>;
13
14    /// Try to deserialize from a slice
15    fn from_slice(data: &[u8]) -> Result<Self, ParseError>;
16
17    /// Deserialise from a byte slice.
18    #[deprecated(since = "0.8.0", note = "please use `from_slice` instead")]
19    fn from_bytes(data: &[u8]) -> Result<Self, ParseError> {
20        Self::from_slice(data)
21    }
22}
23
24/// The Left half of a fixed-N BlockORE ciphertext: the per-block PRF₁ tags
25/// `f` and the per-block PRP outputs `xt`. Sufficient on its own to act as
26/// the "query" side of the comparator.
27#[derive(Debug, Copy, Clone)]
28pub struct Left<S: OreCipher, const N: usize> {
29    /// Per-block PRF₁ tag binding `(prefix ‖ xt[i] ‖ block_index)`.
30    pub f: [S::LeftBlockType; N],
31
32    /// Per-block PRP output `xt[i] = π_i(x[i])`.
33    pub xt: [u8; N],
34}
35
36/// The Right half of a fixed-N BlockORE ciphertext: a per-ciphertext nonce
37/// and per-block masked truth-table rows. Combined with a Left from another
38/// ciphertext this drives the comparator.
39#[derive(Debug, Copy, Clone)]
40pub struct Right<S: OreCipher, const N: usize> {
41    /// 16-byte random nonce, shared across all blocks of this ciphertext.
42    pub nonce: [u8; NONCE_SIZE],
43    /// Per-block masked truth tables.
44    pub data: [S::RightBlockType; N],
45}
46
47/// A complete fixed-N BlockORE ciphertext: Left + Right.
48#[derive(Debug, Copy, Clone)]
49pub struct CipherText<S: OreCipher, const N: usize> {
50    /// Left half (PRF tags + permuted plaintext bytes).
51    pub left: Left<S, N>,
52    /// Right half (nonce + masked truth tables).
53    pub right: Right<S, N>,
54}
55
56/// Trait implemented by per-block ciphertext components (Left and Right
57/// blocks). Provides a fixed serialised size and byte conversions used by
58/// [`OreOutput`].
59pub trait CipherTextBlock: Default + Copy + std::fmt::Debug {
60    /// Serialised size of one block in bytes.
61    const BLOCK_SIZE: usize;
62
63    /// Serialise this block to bytes.
64    fn to_bytes(self) -> Vec<u8>;
65
66    /// Deserialise a block from a byte slice. Returns [`ParseError`] if the
67    /// slice is malformed or the wrong length.
68    fn from_bytes(data: &[u8]) -> Result<Self, ParseError>;
69
70    /// Reset this block to its default value in place.
71    fn default_in_place(&mut self);
72}
73
74/// Error returned when a serialised ciphertext can't be parsed (wrong
75/// length, malformed block, etc.).
76#[derive(Debug, Error)]
77#[error("Unable to parse ORE Ciphertext")]
78pub struct ParseError;
79
80impl<S: OreCipher, const N: usize> Left<S, N> {
81    pub(crate) fn init() -> Self {
82        Self {
83            xt: [0; N],
84            f: [S::LeftBlockType::default(); N],
85        }
86    }
87}
88
89impl<S: OreCipher, const N: usize> OreOutput for Left<S, N> {
90    fn size() -> usize {
91        N * (S::LeftBlockType::BLOCK_SIZE + 1)
92    }
93
94    fn to_bytes(&self) -> Vec<u8> {
95        let mut vec = Vec::with_capacity(N * S::LeftBlockType::BLOCK_SIZE);
96        self.f
97            .iter()
98            .for_each(|&block| vec.append(&mut block.to_bytes()));
99
100        [self.xt.to_vec(), vec].concat()
101    }
102
103    fn from_slice(data: &[u8]) -> Result<Self, ParseError> {
104        let mut out = Self::init();
105        out.xt.copy_from_slice(&data[0..N]);
106        for i in 0..N {
107            let block_start_index = N + (i * S::LeftBlockType::BLOCK_SIZE);
108            out.f[i] = S::LeftBlockType::from_bytes(
109                &data[block_start_index..(block_start_index + S::LeftBlockType::BLOCK_SIZE)],
110            )?;
111        }
112
113        Ok(out)
114    }
115}
116
117impl<S: OreCipher, const N: usize> Right<S, N> {
118    pub(crate) fn init() -> Self {
119        Self {
120            nonce: Default::default(),
121            data: [Default::default(); N],
122        }
123    }
124}
125
126impl<S: OreCipher, const N: usize> OreOutput for Right<S, N> {
127    fn size() -> usize {
128        (N * S::RightBlockType::BLOCK_SIZE) + NONCE_SIZE
129    }
130
131    fn to_bytes(&self) -> Vec<u8> {
132        let mut vec = Vec::with_capacity(N * S::RightBlockType::BLOCK_SIZE);
133        self.data
134            .iter()
135            .for_each(|&block| vec.append(&mut block.to_bytes()));
136
137        [self.nonce.to_vec(), vec].concat()
138    }
139
140    fn from_slice(data: &[u8]) -> Result<Self, ParseError> {
141        let mut out = Self::init();
142        out.nonce.copy_from_slice(&data[0..NONCE_SIZE]);
143        for i in 0..N {
144            let block_start_index = NONCE_SIZE + (i * S::RightBlockType::BLOCK_SIZE);
145            out.data[i] = S::RightBlockType::from_bytes(
146                &data[block_start_index..(block_start_index + S::RightBlockType::BLOCK_SIZE)],
147            )?;
148        }
149        Ok(out)
150    }
151}
152
153impl<S: OreCipher, const N: usize> OreOutput for CipherText<S, N> {
154    fn size() -> usize {
155        Left::<S, N>::size() + Right::<S, N>::size()
156    }
157
158    /// Serialize the ciphertext into a vector of bytes
159    fn to_bytes(&self) -> Vec<u8> {
160        [self.left.to_bytes(), self.right.to_bytes()].concat()
161    }
162
163    /// Deserialize from a slice of bytes
164    fn from_slice(data: &[u8]) -> Result<Self, ParseError> {
165        if data.len() != (Left::<S, N>::size() + Right::<S, N>::size()) {
166            return Err(ParseError);
167        }
168        let (left, right) = data.split_at(Left::<S, N>::size());
169        let left = Left::<S, N>::from_slice(left)?;
170        let right = Right::<S, N>::from_slice(right)?;
171
172        Ok(Self { left, right })
173    }
174}