Skip to main content

voprf_ng/
serialization.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Handles the serialization of each of the components used in the VOPRF
4//! protocol
5
6use generic_array::sequence::Concat;
7use generic_array::typenum::{Sum, Unsigned};
8use generic_array::GenericArray;
9
10use crate::{
11    BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer,
12    PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer,
13};
14
15//////////////////////////////////////////////////////////
16// Serialization and Deserialization for High-Level API //
17// ==================================================== //
18//////////////////////////////////////////////////////////
19
20/// Length of [`OprfClient`] in bytes for serialization.
21pub type OprfClientLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
22
23impl<CS: CipherSuite> OprfClient<CS> {
24    /// Serialization into bytes
25    pub fn serialize(&self) -> GenericArray<u8, OprfClientLen<CS>> {
26        CS::Group::serialize_scalar(self.blind)
27    }
28
29    /// Deserialization from bytes
30    ///
31    /// # Errors
32    /// [`Error::Deserialization`] if failed to deserialize `input`.
33    pub fn deserialize(mut input: &[u8]) -> Result<Self> {
34        let blind = deserialize_scalar::<CS::Group>(&mut input)?;
35        check_consumed(input)?;
36
37        Ok(Self { blind })
38    }
39}
40
41/// Length of [`VoprfClient`] in bytes for serialization.
42pub type VoprfClientLen<CS> = Sum<
43    <<CS as CipherSuite>::Group as Group>::ScalarLen,
44    <<CS as CipherSuite>::Group as Group>::ElemLen,
45>;
46
47impl<CS: CipherSuite> VoprfClient<CS> {
48    /// Serialization into bytes
49    pub fn serialize(&self) -> GenericArray<u8, VoprfClientLen<CS>> {
50        <CS::Group as Group>::serialize_scalar(self.blind)
51            .concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
52    }
53
54    /// Deserialization from bytes
55    ///
56    /// # Errors
57    /// [`Error::Deserialization`] if failed to deserialize `input`.
58    pub fn deserialize(mut input: &[u8]) -> Result<Self> {
59        let blind = deserialize_scalar::<CS::Group>(&mut input)?;
60        let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
61        check_consumed(input)?;
62
63        Ok(Self {
64            blind,
65            blinded_element,
66        })
67    }
68}
69
70/// Length of [`PoprfClient`] in bytes for serialization.
71pub type PoprfClientLen<CS> = Sum<
72    <<CS as CipherSuite>::Group as Group>::ScalarLen,
73    <<CS as CipherSuite>::Group as Group>::ElemLen,
74>;
75
76impl<CS: CipherSuite> PoprfClient<CS> {
77    /// Serialization into bytes
78    pub fn serialize(&self) -> GenericArray<u8, PoprfClientLen<CS>> {
79        <CS::Group as Group>::serialize_scalar(self.blind)
80            .concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
81    }
82
83    /// Deserialization from bytes
84    ///
85    /// # Errors
86    /// [`Error::Deserialization`] if failed to deserialize `input`.
87    pub fn deserialize(mut input: &[u8]) -> Result<Self> {
88        let blind = deserialize_scalar::<CS::Group>(&mut input)?;
89        let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
90        check_consumed(input)?;
91
92        Ok(Self {
93            blind,
94            blinded_element,
95        })
96    }
97}
98
99/// Length of [`OprfServer`] in bytes for serialization.
100pub type OprfServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
101
102impl<CS: CipherSuite> OprfServer<CS> {
103    /// Serialization into bytes
104    pub fn serialize(&self) -> GenericArray<u8, OprfServerLen<CS>> {
105        CS::Group::serialize_scalar(self.sk)
106    }
107
108    /// Deserialization from bytes
109    ///
110    /// # Errors
111    /// [`Error::Deserialization`] if failed to deserialize `input`.
112    pub fn deserialize(mut input: &[u8]) -> Result<Self> {
113        let sk = deserialize_scalar::<CS::Group>(&mut input)?;
114        check_consumed(input)?;
115
116        Ok(Self { sk })
117    }
118}
119
120/// Length of [`VoprfServer`] in bytes for serialization.
121pub type VoprfServerLen<CS> = Sum<
122    <<CS as CipherSuite>::Group as Group>::ScalarLen,
123    <<CS as CipherSuite>::Group as Group>::ElemLen,
124>;
125
126impl<CS: CipherSuite> VoprfServer<CS> {
127    /// Serialization into bytes
128    pub fn serialize(&self) -> GenericArray<u8, VoprfServerLen<CS>> {
129        CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
130    }
131
132    /// Deserialization from bytes
133    ///
134    /// # Errors
135    /// [`Error::Deserialization`] if failed to deserialize `input`.
136    pub fn deserialize(mut input: &[u8]) -> Result<Self> {
137        let sk = deserialize_scalar::<CS::Group>(&mut input)?;
138        let pk = deserialize_elem::<CS::Group>(&mut input)?;
139        check_consumed(input)?;
140
141        Ok(Self { sk, pk })
142    }
143}
144
145/// Length of [`PoprfServer`] in bytes for serialization.
146pub type PoprfServerLen<CS> = Sum<
147    <<CS as CipherSuite>::Group as Group>::ScalarLen,
148    <<CS as CipherSuite>::Group as Group>::ElemLen,
149>;
150
151impl<CS: CipherSuite> PoprfServer<CS> {
152    /// Serialization into bytes
153    pub fn serialize(&self) -> GenericArray<u8, PoprfServerLen<CS>> {
154        CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
155    }
156
157    /// Deserialization from bytes
158    ///
159    /// # Errors
160    /// [`Error::Deserialization`] if failed to deserialize `input`.
161    pub fn deserialize(mut input: &[u8]) -> Result<Self> {
162        let sk = deserialize_scalar::<CS::Group>(&mut input)?;
163        let pk = deserialize_elem::<CS::Group>(&mut input)?;
164        check_consumed(input)?;
165
166        Ok(Self { sk, pk })
167    }
168}
169
170/// Length of [`Proof`] in bytes for serialization.
171pub type ProofLen<CS> = Sum<
172    <<CS as CipherSuite>::Group as Group>::ScalarLen,
173    <<CS as CipherSuite>::Group as Group>::ScalarLen,
174>;
175
176impl<CS: CipherSuite> Proof<CS> {
177    /// Serialization into bytes
178    pub fn serialize(&self) -> GenericArray<u8, ProofLen<CS>> {
179        CS::Group::serialize_scalar(self.c_scalar)
180            .concat(CS::Group::serialize_scalar(self.s_scalar))
181    }
182
183    /// Deserialization from bytes
184    ///
185    /// # Errors
186    /// [`Error::Deserialization`] if failed to deserialize `input`.
187    pub fn deserialize(mut input: &[u8]) -> Result<Self> {
188        let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
189        let s_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
190        check_consumed(input)?;
191
192        Ok(Proof { c_scalar, s_scalar })
193    }
194}
195
196/// Length of [`BlindedElement`] in bytes for serialization.
197pub type BlindedElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
198
199impl<CS: CipherSuite> BlindedElement<CS> {
200    /// Serialization into bytes
201    pub fn serialize(&self) -> GenericArray<u8, BlindedElementLen<CS>> {
202        CS::Group::serialize_elem(self.0)
203    }
204
205    /// Deserialization from bytes
206    ///
207    /// # Errors
208    /// [`Error::Deserialization`] if failed to deserialize `input`.
209    pub fn deserialize(mut input: &[u8]) -> Result<Self> {
210        let value = deserialize_elem::<CS::Group>(&mut input)?;
211        check_consumed(input)?;
212
213        Ok(Self(value))
214    }
215}
216
217/// Length of [`EvaluationElement`] in bytes for serialization.
218pub type EvaluationElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
219
220impl<CS: CipherSuite> EvaluationElement<CS> {
221    /// Serialization into bytes
222    pub fn serialize(&self) -> GenericArray<u8, EvaluationElementLen<CS>> {
223        CS::Group::serialize_elem(self.0)
224    }
225
226    /// Deserialization from bytes
227    ///
228    /// # Errors
229    /// [`Error::Deserialization`] if failed to deserialize `input`.
230    pub fn deserialize(mut input: &[u8]) -> Result<Self> {
231        let value = deserialize_elem::<CS::Group>(&mut input)?;
232        check_consumed(input)?;
233
234        Ok(Self(value))
235    }
236}
237
238/// Rejects trailing bytes after a fixed-length object has been fully read. The
239/// VOPRF wire formats are fixed-length, so any remaining input is a
240/// non-canonical encoding.
241fn check_consumed(input: &[u8]) -> Result<()> {
242    if input.is_empty() {
243        Ok(())
244    } else {
245        Err(Error::Deserialization)
246    }
247}
248
249fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> {
250    let input = input
251        .take_ext(G::ElemLen::USIZE)
252        .ok_or(Error::Deserialization)?;
253    G::deserialize_elem(input)
254}
255
256fn deserialize_scalar<G: Group>(input: &mut &[u8]) -> Result<G::Scalar> {
257    let input = input
258        .take_ext(G::ScalarLen::USIZE)
259        .ok_or(Error::Deserialization)?;
260    G::deserialize_scalar(input)
261}
262
263trait SliceExt {
264    fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self>;
265}
266
267impl<T> SliceExt for [T] {
268    fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self> {
269        if take > self.len() {
270            return None;
271        }
272
273        let (front, back) = self.split_at(take);
274        *self = back;
275        Some(front)
276    }
277}
278
279#[cfg(feature = "serde")]
280pub(crate) mod serde {
281    use core::marker::PhantomData;
282
283    use generic_array::GenericArray;
284    use serde::de::{Deserializer, Error};
285    use serde::ser::Serializer;
286    use serde::{Deserialize, Serialize};
287
288    use crate::Group;
289
290    pub(crate) struct Element<G: Group>(PhantomData<G>);
291
292    impl<'de, G: Group> Element<G> {
293        pub(crate) fn deserialize<D>(deserializer: D) -> Result<G::Elem, D::Error>
294        where
295            D: Deserializer<'de>,
296        {
297            GenericArray::<_, G::ElemLen>::deserialize(deserializer)
298                .and_then(|bytes| G::deserialize_elem(&bytes).map_err(D::Error::custom))
299        }
300
301        pub(crate) fn serialize<S>(self_: &G::Elem, serializer: S) -> Result<S::Ok, S::Error>
302        where
303            S: Serializer,
304        {
305            G::serialize_elem(*self_).serialize(serializer)
306        }
307    }
308
309    pub(crate) struct Scalar<G: Group>(PhantomData<G>);
310
311    impl<'de, G: Group> Scalar<G> {
312        pub(crate) fn deserialize<D>(deserializer: D) -> Result<G::Scalar, D::Error>
313        where
314            D: Deserializer<'de>,
315        {
316            GenericArray::<_, G::ScalarLen>::deserialize(deserializer)
317                .and_then(|bytes| G::deserialize_scalar(&bytes).map_err(D::Error::custom))
318        }
319
320        pub(crate) fn serialize<S>(self_: &G::Scalar, serializer: S) -> Result<S::Ok, S::Error>
321        where
322            S: Serializer,
323        {
324            G::serialize_scalar(*self_).serialize(serializer)
325        }
326    }
327}
328
329#[cfg(test)]
330mod test {
331    use proptest::collection::vec;
332    use proptest::prelude::*;
333
334    use crate::{
335        BlindedElement, EvaluationElement, OprfClient, OprfServer, PoprfClient, PoprfServer, Proof,
336        VoprfClient, VoprfServer,
337    };
338
339    macro_rules! test_deserialize {
340        ($item:ident, $bytes:ident) => {
341            #[cfg(feature = "ristretto255")]
342            {
343                let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
344            }
345
346            let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]);
347            let _ = $item::<p384::NistP384>::deserialize(&$bytes[..]);
348            let _ = $item::<p521::NistP521>::deserialize(&$bytes[..]);
349        };
350    }
351
352    proptest! {
353        #[test]
354        fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
355            test_deserialize!(OprfClient, bytes);
356        }
357
358        #[test]
359        fn test_nocrash_voprf_client(bytes in vec(any::<u8>(), 0..200)) {
360            test_deserialize!(VoprfClient, bytes);
361        }
362
363        #[test]
364        fn test_nocrash_poprf_client(bytes in vec(any::<u8>(), 0..200)) {
365            test_deserialize!(PoprfClient, bytes);
366        }
367
368        #[test]
369        fn test_nocrash_oprf_server(bytes in vec(any::<u8>(), 0..200)) {
370            test_deserialize!(OprfServer, bytes);
371        }
372
373        #[test]
374        fn test_nocrash_voprf_server(bytes in vec(any::<u8>(), 0..200)) {
375            test_deserialize!(VoprfServer, bytes);
376        }
377
378        #[test]
379        fn test_nocrash_poprf_server(bytes in vec(any::<u8>(), 0..200)) {
380            test_deserialize!(PoprfServer, bytes);
381        }
382
383
384        #[test]
385        fn test_nocrash_blinded_element(bytes in vec(any::<u8>(), 0..200)) {
386            test_deserialize!(BlindedElement, bytes);
387        }
388
389        #[test]
390        fn test_nocrash_evaluation_element(bytes in vec(any::<u8>(), 0..200)) {
391            test_deserialize!(EvaluationElement, bytes);
392        }
393
394        #[test]
395        fn test_nocrash_proof(bytes in vec(any::<u8>(), 0..200)) {
396            test_deserialize!(Proof, bytes);
397        }
398    }
399
400    use ::alloc::vec::Vec;
401    use rand::rngs::SysRng;
402
403    use crate::{CipherSuite, Error};
404
405    fn trailing_bytes_rejected<CS: CipherSuite>() {
406        let mut rng = SysRng;
407        let server = VoprfServer::<CS>::new(&mut rng).unwrap();
408        let blind_result = VoprfClient::<CS>::blind(b"input", &mut rng).unwrap();
409
410        // An exact-length encoding round-trips, a single trailing byte is
411        // rejected as a non-canonical encoding.
412        let server_bytes = server.serialize().to_vec();
413        VoprfServer::<CS>::deserialize(&server_bytes).unwrap();
414        assert!(matches!(
415            VoprfServer::<CS>::deserialize(&extend(&server_bytes)),
416            Err(Error::Deserialization)
417        ));
418
419        let client_bytes = blind_result.state.serialize().to_vec();
420        VoprfClient::<CS>::deserialize(&client_bytes).unwrap();
421        assert!(matches!(
422            VoprfClient::<CS>::deserialize(&extend(&client_bytes)),
423            Err(Error::Deserialization)
424        ));
425
426        let element_bytes = blind_result.message.serialize().to_vec();
427        BlindedElement::<CS>::deserialize(&element_bytes).unwrap();
428        assert!(matches!(
429            BlindedElement::<CS>::deserialize(&extend(&element_bytes)),
430            Err(Error::Deserialization)
431        ));
432    }
433
434    fn extend(bytes: &[u8]) -> Vec<u8> {
435        let mut extended = bytes.to_vec();
436        extended.push(0);
437        extended
438    }
439
440    #[test]
441    fn test_trailing_bytes_rejected() {
442        #[cfg(feature = "ristretto255")]
443        trailing_bytes_rejected::<crate::Ristretto255>();
444
445        trailing_bytes_rejected::<p256::NistP256>();
446        trailing_bytes_rejected::<p384::NistP384>();
447        trailing_bytes_rejected::<p521::NistP521>();
448    }
449}