s2n_quic_core/frame/
crypto.rs1use crate::{
5 frame::{FitError, Tag},
6 varint::VarInt,
7};
8use core::{convert::TryFrom, mem::size_of};
9use s2n_codec::{
10 decoder_parameterized_value, DecoderBuffer, DecoderBufferMut, Encoder, EncoderValue,
11};
12
13macro_rules! crypto_tag {
18 () => {
19 0x06u8
20 };
21}
22
23#[derive(Debug, PartialEq, Eq)]
43pub struct Crypto<Data> {
44 pub offset: VarInt,
47
48 pub data: Data,
50}
51
52impl<Data> Crypto<Data> {
53 #[inline]
54 pub const fn tag(&self) -> u8 {
55 crypto_tag!()
56 }
57
58 #[inline]
60 pub fn map_data<F: FnOnce(Data) -> Out, Out>(self, map: F) -> Crypto<Out> {
61 Crypto {
62 offset: self.offset,
63 data: map(self.data),
64 }
65 }
66}
67
68impl<Data: EncoderValue> Crypto<Data> {
69 #[inline]
74 pub fn try_fit(&self, capacity: usize) -> Result<usize, FitError> {
75 let mut fixed_len = 0;
76 fixed_len += size_of::<Tag>();
77 fixed_len += self.offset.encoding_size();
78
79 let remaining_capacity = capacity.checked_sub(fixed_len).ok_or(FitError)?;
80
81 let data_len = self.data.encoding_size();
82 let max_data_len = remaining_capacity.min(data_len);
83
84 let len_prefix_size = VarInt::try_from(max_data_len)
85 .map_err(|_| FitError)?
86 .encoding_size();
87
88 let prefixed_data_len = remaining_capacity
89 .checked_sub(len_prefix_size)
90 .ok_or(FitError)?;
91 let data_len = prefixed_data_len.min(data_len);
92
93 Ok(data_len)
94 }
95}
96
97pub type CryptoRef<'a> = Crypto<&'a [u8]>;
98pub type CryptoMut<'a> = Crypto<&'a mut [u8]>;
99
100decoder_parameterized_value!(
101 impl<'a, Data> Crypto<Data> {
102 fn decode(_tag: Tag, buffer: Buffer) -> Result<Self> {
103 let (offset, buffer) = buffer.decode()?;
104 let (data, buffer) = buffer.decode_with_len_prefix::<VarInt, Data>()?;
105
106 let frame = Crypto { offset, data };
107
108 Ok((frame, buffer))
109 }
110 }
111);
112
113impl<Data: EncoderValue> EncoderValue for Crypto<Data> {
114 #[inline]
115 fn encode<E: Encoder>(&self, buffer: &mut E) {
116 buffer.encode(&self.tag());
117 buffer.encode(&self.offset);
118 buffer.encode_with_len_prefix::<VarInt, _>(&self.data);
119 }
120}
121
122impl<'a> From<Crypto<DecoderBuffer<'a>>> for CryptoRef<'a> {
123 #[inline]
124 fn from(s: Crypto<DecoderBuffer<'a>>) -> Self {
125 s.map_data(|data| data.into_less_safe_slice())
126 }
127}
128
129impl<'a> From<Crypto<DecoderBufferMut<'a>>> for CryptoRef<'a> {
130 #[inline]
131 fn from(s: Crypto<DecoderBufferMut<'a>>) -> Self {
132 s.map_data(|data| &*data.into_less_safe_slice())
133 }
134}
135
136impl<'a> From<Crypto<DecoderBufferMut<'a>>> for CryptoMut<'a> {
137 #[inline]
138 fn from(s: Crypto<DecoderBufferMut<'a>>) -> Self {
139 s.map_data(|data| data.into_less_safe_slice())
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use crate::frame::Padding;
147 use bolero::check;
148 use core::convert::TryInto;
149
150 fn model(offset: VarInt, length: VarInt, capacity: usize) {
151 let length = if let Ok(length) = VarInt::try_into(length) {
152 length
153 } else {
154 return;
156 };
157
158 let mut frame = Crypto {
159 offset,
160 data: Padding { length },
161 };
162
163 if let Ok(new_length) = frame.try_fit(capacity) {
164 frame.data = Padding { length: new_length };
165
166 assert!(
167 frame.encoding_size() <= capacity,
168 "the encoding_size should not exceed capacity {frame:#?}"
169 );
170
171 if new_length < length {
172 let tolerance = VarInt::try_from(new_length).unwrap().encoding_size();
180
181 assert!(
182 capacity - frame.encoding_size() <= tolerance,
183 "should fit capacity tolerance: expected {}, got {}; {:#?}",
184 tolerance,
185 capacity - frame.encoding_size(),
186 frame,
187 );
188 }
189 } else {
190 assert!(
191 frame.encoding_size() > capacity,
192 "rejection should only occur when encoding size > capacity {frame:#?}"
193 );
194 }
195 }
196
197 #[test]
198 #[cfg_attr(kani, kani::proof, kani::unwind(1), kani::solver(kissat))]
199 fn try_fit_test() {
200 check!()
201 .with_type()
202 .cloned()
203 .for_each(|(offset, length, capacity)| {
204 model(offset, length, capacity);
205 });
206 }
207}