vest/
properties.rs

1pub use crate::errors::*;
2pub use crate::utils::*;
3use vstd::prelude::*;
4use vstd::*;
5
6verus! {
7
8/// Specification for parser and serializer [`Combinator`]s. All Vest combinators must implement this
9/// trait.
10pub trait SpecCombinator {
11    /// The view of [`Combinator::Result`].
12    type SpecResult;
13
14    /// The specification of [`Combinator::parse`].
15    spec fn spec_parse(&self, s: Seq<u8>) -> Result<(usize, Self::SpecResult), ()>;
16
17    /// The specification of [`Combinator::serialize`].
18    spec fn spec_serialize(&self, v: Self::SpecResult) -> Result<Seq<u8>, ()>;
19
20    /// A helper fact to ensure that the result of parsing is within the input bounds.
21    proof fn spec_parse_wf(&self, s: Seq<u8>)
22        ensures
23            self.spec_parse(s).is_ok() ==> self.spec_parse(s).unwrap().0 <= s.len(),
24    ;
25}
26
27/// Theorems and lemmas that must be proven for a combinator to be considered correct and secure.
28pub trait SecureSpecCombinator: SpecCombinator {
29    /// Like an associated constant, denotes whether the combinator is prefix-secure.
30    spec fn is_prefix_secure() -> bool;
31
32    /// One of the top-level roundtrip properties
33    proof fn theorem_serialize_parse_roundtrip(&self, v: Self::SpecResult)
34        ensures
35            self.spec_serialize(v) matches Ok(b) ==> self.spec_parse(b) == Ok::<_, ()>(
36                (b.len() as usize, v),
37            ),
38    ;
39
40    /// One of the top-level roundtrip properties
41    proof fn theorem_parse_serialize_roundtrip(&self, buf: Seq<u8>)
42        requires
43            buf.len() <= usize::MAX,
44        ensures
45            self.spec_parse(buf) matches Ok((n, v)) ==> self.spec_serialize(v) == Ok::<_, ()>(
46                buf.subrange(0, n as int),
47            ),
48    ;
49
50    /// This prefix-secure lemma is used in the proof of the roundtrip properties for sequencing
51    /// combinators.
52    proof fn lemma_prefix_secure(&self, s1: Seq<u8>, s2: Seq<u8>)
53        requires
54            s1.len() + s2.len() <= usize::MAX,
55        ensures
56            Self::is_prefix_secure() ==> self.spec_parse(s1).is_ok() ==> self.spec_parse(s1.add(s2))
57                == self.spec_parse(s1),
58    ;
59}
60
61/// Implementation for parser and serializer combinators. A combinator's view must be a
62/// [`SecureSpecCombinator`].
63pub trait Combinator: View where
64    Self::V: SecureSpecCombinator<SpecResult = <Self::Owned as View>::V>,
65 {
66    /// The result type of parsing and the input type of serialization.
67    type Result<'a>: View<V = <Self::Owned as View>::V>;
68
69    /// The owned parsed type. This is currently a hack to avoid lifetime bindings in [`SpecCombinator::SpecResult`]
70    /// , but it can be useful if we want to have functions that return owned values (e.g. [`Vec<T>`]).
71    type Owned: View;
72
73    /// Spec version of [`Self::length`].
74    spec fn spec_length(&self) -> Option<usize>;
75
76    /// The length of the output buffer, if known.
77    /// This can be used to optimize serialization by pre-allocating the buffer.
78    fn length(&self) -> (res: Option<usize>)
79        ensures
80            res == self.spec_length(),
81    ;
82
83    /// Pre-condition for parsing.
84    open spec fn parse_requires(&self) -> bool {
85        true
86    }
87
88    /// The parsing function.
89    fn parse<'a>(&self, s: &'a [u8]) -> (res: Result<(usize, Self::Result<'a>), ParseError>)
90        requires
91            self.parse_requires(),
92        ensures
93            res matches Ok((n, v)) ==> {
94                &&& self@.spec_parse(s@) is Ok
95                &&& self@.spec_parse(s@) matches Ok((m, w)) ==> n == m && v@ == w && n <= s@.len()
96            },
97            res is Err ==> self@.spec_parse(s@) is Err,
98    ;
99
100    /// Pre-condition for serialization.
101    open spec fn serialize_requires(&self) -> bool {
102        true
103    }
104
105    /// The serialization function.
106    fn serialize(&self, v: Self::Result<'_>, data: &mut Vec<u8>, pos: usize) -> (res: Result<
107        usize,
108        SerializeError,
109    >)
110        requires
111            self.serialize_requires(),
112        ensures
113            data@.len() == old(data)@.len(),
114            res matches Ok(n) ==> {
115                &&& self@.spec_serialize(v@) is Ok
116                &&& self@.spec_serialize(v@) matches Ok(b) ==> {
117                    n == b.len() && data@ == seq_splice(old(data)@, pos, b)
118                }
119            },
120    ;
121}
122
123impl<C: SpecCombinator> SpecCombinator for &C {
124    type SpecResult = C::SpecResult;
125
126    open spec fn spec_parse(&self, s: Seq<u8>) -> Result<(usize, Self::SpecResult), ()> {
127        (*self).spec_parse(s)
128    }
129
130    open spec fn spec_serialize(&self, v: Self::SpecResult) -> Result<Seq<u8>, ()> {
131        (*self).spec_serialize(v)
132    }
133
134    proof fn spec_parse_wf(&self, s: Seq<u8>) {
135        (*self).spec_parse_wf(s)
136    }
137}
138
139impl<C: SecureSpecCombinator> SecureSpecCombinator for &C {
140    open spec fn is_prefix_secure() -> bool {
141        C::is_prefix_secure()
142    }
143
144    proof fn theorem_serialize_parse_roundtrip(&self, v: Self::SpecResult) {
145        (*self).theorem_serialize_parse_roundtrip(v)
146    }
147
148    proof fn theorem_parse_serialize_roundtrip(&self, buf: Seq<u8>) {
149        (*self).theorem_parse_serialize_roundtrip(buf)
150    }
151
152    proof fn lemma_prefix_secure(&self, s1: Seq<u8>, s2: Seq<u8>) {
153        (*self).lemma_prefix_secure(s1, s2)
154    }
155}
156
157impl<C: Combinator> Combinator for &C where
158    C::V: SecureSpecCombinator<SpecResult = <C::Owned as View>::V>,
159 {
160    type Result<'a> = C::Result<'a>;
161
162    type Owned = C::Owned;
163
164    open spec fn spec_length(&self) -> Option<usize> {
165        (*self).spec_length()
166    }
167
168    fn length(&self) -> Option<usize> {
169        (*self).length()
170    }
171
172    open spec fn parse_requires(&self) -> bool {
173        (*self).parse_requires()
174    }
175
176    fn parse<'a>(&self, s: &'a [u8]) -> (res: Result<(usize, Self::Result<'a>), ParseError>) {
177        (*self).parse(s)
178    }
179
180    open spec fn serialize_requires(&self) -> bool {
181        (*self).serialize_requires()
182    }
183
184    fn serialize(&self, v: Self::Result<'_>, data: &mut Vec<u8>, pos: usize) -> (res: Result<
185        usize,
186        SerializeError,
187    >) {
188        (*self).serialize(v, data, pos)
189    }
190}
191
192// The following is an attempt to support `Fn`s as combinators.
193// I started implementing it because Verus doesn't support existential types (impl Trait) yet,
194// which is required to be able to put `Depend` combinator in the function's return type,
195// but more generally, I think it's probably also good to have a way in our framework to define
196// combinators as a group of (parsing and serializing) functions.
197//
198// Overall this is doable, but it's a bit tricky to implement because of the current limitations of Verus:
199// - Verus doesn't have general support for struct invariants yet, which means we have to add
200// pre-conditions to the security properties (i.e. the struct containing a pair of parsing and
201// serializing functions must be "well-formed" in some sense).
202// - Verus doesn't have general support for `&mut` types yet, which means for serialization we
203// cannot freely pass around the `&mut Vec<u8>`
204//
205// In theory, we could factor out all the lifetime parameters in `Combinator` trait and use generic
206// type parameters instead for both parsing and serialization, but that would require another
207// entire-codebase refactoring, which I think is not worth it at this point.
208//
209// #[verifier::reject_recursive_types(SpecResult)]
210// pub struct SpecCombinatorFn<SpecResult, const Prefix: u8> {
211//     pub parse: spec_fn(Seq<u8>) -> Result<(usize, SpecResult), ()>,
212//     pub serialize: spec_fn(SpecResult) -> Result<Seq<u8>, ()>,
213// }
214//
215// impl<SpecResult, const Prefix: u8> SpecCombinatorFn<SpecResult, Prefix> {
216//     pub open spec fn new(
217//         parse: spec_fn(Seq<u8>) -> Result<(usize, SpecResult), ()>,
218//         serialize: spec_fn(SpecResult) -> Result<Seq<u8>, ()>,
219//     ) -> (o: Self)
220//         recommends
221//             forall|v| Self::theorem_serialize_parse_roundtrip(parse, serialize, v),
222//             forall|buf| Self::theorem_parse_serialize_roundtrip(parse, serialize, buf),
223//             forall|s1, s2| Self::lemma_prefix_secure(parse, s1, s2),
224//     {
225//         Self { parse, serialize }
226//     }
227//
228//     pub open spec fn theorem_serialize_parse_roundtrip(
229//         parse: spec_fn(Seq<u8>) -> Result<(usize, SpecResult), ()>,
230//         serialize: spec_fn(SpecResult) -> Result<Seq<u8>, ()>,
231//         v: SpecResult,
232//     ) -> bool {
233//         serialize(v) is Ok ==> parse(serialize(v).unwrap()) == Ok::<_, ()>(
234//             (serialize(v).unwrap().len() as usize, v),
235//         )
236//     }
237//
238//     pub open spec fn theorem_parse_serialize_roundtrip(
239//         parse: spec_fn(Seq<u8>) -> Result<(usize, SpecResult), ()>,
240//         serialize: spec_fn(SpecResult) -> Result<Seq<u8>, ()>,
241//         buf: Seq<u8>,
242//     ) -> bool {
243//         buf.len() <= usize::MAX ==> parse(buf) is Ok ==> serialize(parse(buf).unwrap().1) == Ok::<
244//             _,
245//             (),
246//         >(buf.subrange(0, parse(buf).unwrap().0 as int))
247//     }
248//
249//     pub open spec fn lemma_prefix_secure(
250//         parse: spec_fn(Seq<u8>) -> Result<(usize, SpecResult), ()>,
251//         s1: Seq<u8>,
252//         s2: Seq<u8>,
253//     ) -> bool {
254//         if s1.len() + s2.len() <= usize::MAX {
255//             (Prefix == 1) ==> parse(s1) is Ok ==> parse(s1.add(s2)) == parse(s1)
256//         } else {
257//             true
258//         }
259//     }
260// }
261//
262//
263// impl<SpecResult, const Prefix: u8> SpecCombinator for SpecCombinatorFn<SpecResult, Prefix> {
264//     type SpecResult = SpecResult;
265//
266//     open spec fn spec_parse(&self, s: Seq<u8>) -> Result<(usize, Self::SpecResult), ()> {
267//         if let Ok((n, v)) = (self.parse)(s) {
268//             if n <= s.len() {
269//                 Ok((n, v))
270//             } else {
271//                 Err(())
272//             }
273//         } else {
274//             Err(())
275//         }
276//     }
277//
278//     open spec fn spec_serialize(&self, v: Self::SpecResult) -> Result<Seq<u8>, ()> {
279//         (self.serialize)(v)
280//     }
281//
282//     proof fn spec_parse_wf(&self, s: Seq<u8>) {
283//     }
284// }
285//
286// impl<SpecResult, const Prefix: u8> SecureSpecCombinator for SpecCombinatorFn<SpecResult, Prefix> {
287//     open spec fn spec_is_prefix_secure() -> bool {
288//         Prefix == 1
289//     }
290//
291//     proof fn theorem_serialize_parse_roundtrip(&self, v: Self::SpecResult) {
292//         assume(Self::theorem_serialize_parse_roundtrip(self.parse, self.serialize, v));
293//     }
294//
295//     proof fn theorem_parse_serialize_roundtrip(&self, buf: Seq<u8>) {
296//         assume(Self::theorem_parse_serialize_roundtrip(self.parse, self.serialize, buf));
297//     }
298//
299//     proof fn lemma_prefix_secure(&self, s1: Seq<u8>, s2: Seq<u8>) {
300//         assume(Self::lemma_prefix_secure(self.parse, s1, s2));
301//     }
302// }
303//
304// pub struct CombinatorFn<'a, 'b, R, P, S, const Prefix: u8> where
305//     R: View,
306//     P: Fn(&'a [u8]) -> Result<(usize, R), ()>,
307//     S: Fn(R, &'b mut Vec<u8>, usize) -> Result<usize, ()>,
308// {
309//     pub parse: P,
310//     pub serialize: S,
311//     pub spec_parse: Ghost<spec_fn(Seq<u8>) -> Result<(usize, R::V), ()>>,
312//     pub spec_serialize: Ghost<spec_fn(R::V) -> Result<Seq<u8>, ()>>,
313//     pub _phantom: std::marker::PhantomData<&'b &'a R>,
314// }
315//
316// impl<'a, 'b, R, P, S, const Prefix: u8> View for CombinatorFn<'a, 'b, R, P, S, Prefix> where
317//     R: View,
318//     P: Fn(&'a [u8]) -> Result<(usize, R), ()>,
319//     S: Fn(R, &'b mut Vec<u8>, usize) -> Result<usize, ()>,
320// {
321//     type V = SpecCombinatorFn<R::V, Prefix>;
322//
323//     open spec fn view(&self) -> Self::V {
324//         let Ghost(parse) = self.spec_parse;
325//         let Ghost(serialize) = self.spec_serialize;
326//         SpecCombinatorFn {
327//             parse,
328//             serialize,
329//         }
330//     }
331// }
332//
333// impl<'a, 'b, R, P, S, const Prefix: u8> Combinator for CombinatorFn<'a, 'b, R, P, S, Prefix> where
334//     R: View,
335//     P: Fn(&'a [u8]) -> Result<(usize, R), ()>,
336//     S: Fn(R, &'b mut Vec<u8>, usize) -> Result<usize, ()>,
337//     Self::V: SecureSpecCombinator<SpecResult = R::V>,
338//  {
339//     type Result<'c> = R;
340//
341//     type Owned = R;
342//
343//     open spec fn spec_length(&self) -> Option<usize> {
344//         None
345//     }
346//
347//     fn length(&self) -> Option<usize> {
348//         None
349//     }
350//
351//     fn exec_is_prefix_secure() -> bool {
352//         Prefix == 1
353//     }
354//
355//     fn parse<'c>(&self, s: &'c [u8]) -> Result<(usize, Self::Result<'c>), ()> where 'c: 'a {
356//         (self.parse)(s)
357//     }
358//
359//     fn serialize(&self, v: Self::Result<'_>, data: &mut Vec<u8>, pos: usize) -> Result<usize, ()> {
360//         (self.serialize)(v, data, pos)
361//     }
362// }
363} // verus!