Skip to main content

voprf_vx/
serialization.rs

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