1use crate::digest::sha256_digest;
9use serde::de::DeserializeOwned;
10use serde::{Deserialize, Serialize};
11use serde_json::value::RawValue;
12use thiserror::Error;
13
14#[derive(Debug, Deserialize)]
21pub struct EnvelopeProbe {
22 pub kind: String,
24 pub format_version: u32,
26}
27
28#[derive(Debug, Error)]
32#[non_exhaustive]
33pub enum EnvelopeCodecError {
34 #[error("failed to encode envelope payload: {0}")]
36 PayloadEncode(String),
37 #[error("failed to encode envelope document: {0}")]
39 EnvelopeEncode(String),
40 #[error("failed to decode envelope document: {0}")]
42 EnvelopeDecode(String),
43 #[error("failed to decode envelope payload: {0}")]
45 PayloadDecode(String),
46 #[error("failed to compress envelope: {0}")]
48 Compress(String),
49 #[error("failed to decompress envelope: {0}")]
51 Decompress(String),
52 #[error("unknown envelope kind `{found}`")]
54 UnknownKind {
55 found: String,
57 },
58 #[error("envelope kind mismatch: expected `{expected}`, found `{found}`")]
60 KindMismatch {
61 expected: String,
63 found: String,
65 },
66 #[error(
68 "unsupported `{kind}` envelope format version `{found}`: \
69 this build supports `{supported}`"
70 )]
71 UnsupportedFormatVersion {
72 kind: String,
74 found: u32,
76 supported: u32,
78 },
79 #[error("envelope payload checksum mismatch: expected `{expected}`, actual `{actual}`")]
81 ChecksumMismatch {
82 expected: String,
84 actual: String,
86 },
87 #[error(
89 "envelope checksum `{checksum}` does not match its payload `{actual}`: \
90 rebuild the envelope from its payload"
91 )]
92 StalePayloadChecksum {
93 checksum: String,
95 actual: String,
97 },
98}
99
100pub fn verify_kind(expected: &str, found: &str) -> Result<(), EnvelopeCodecError> {
102 if found != expected {
103 return Err(EnvelopeCodecError::KindMismatch {
104 expected: expected.to_owned(),
105 found: found.to_owned(),
106 });
107 }
108 Ok(())
109}
110
111pub fn verify_version(kind: &str, found: u32, supported: u32) -> Result<(), EnvelopeCodecError> {
114 if found != supported {
115 return Err(EnvelopeCodecError::UnsupportedFormatVersion {
116 kind: kind.to_owned(),
117 found,
118 supported,
119 });
120 }
121 Ok(())
122}
123
124pub fn verify_payload_checksum(
126 expected: &str,
127 payload_bytes: &[u8],
128) -> Result<(), EnvelopeCodecError> {
129 let actual = sha256_digest(payload_bytes);
130 if actual != expected {
131 return Err(EnvelopeCodecError::ChecksumMismatch {
132 expected: expected.to_owned(),
133 actual,
134 });
135 }
136 Ok(())
137}
138
139pub fn verify_checksum_fresh(
143 checksum: &str,
144 payload_bytes: &[u8],
145) -> Result<(), EnvelopeCodecError> {
146 let actual = sha256_digest(payload_bytes);
147 if actual != checksum {
148 return Err(EnvelopeCodecError::StalePayloadChecksum {
149 checksum: checksum.to_owned(),
150 actual,
151 });
152 }
153 Ok(())
154}
155
156#[derive(Serialize, Deserialize)]
161struct JsonEnvelopeDocument {
162 kind: String,
163 format_version: u32,
164 payload_checksum: String,
165 payload: Box<RawValue>,
166}
167
168#[derive(Deserialize)]
169#[serde(deny_unknown_fields)]
170struct StrictJsonEnvelopeDocument {
171 kind: String,
172 format_version: u32,
173 payload_checksum: String,
174 payload: Box<RawValue>,
175}
176
177impl From<StrictJsonEnvelopeDocument> for JsonEnvelopeDocument {
178 fn from(document: StrictJsonEnvelopeDocument) -> Self {
179 Self {
180 kind: document.kind,
181 format_version: document.format_version,
182 payload_checksum: document.payload_checksum,
183 payload: document.payload,
184 }
185 }
186}
187
188pub fn json_payload_checksum<T: Serialize>(payload: &T) -> Result<String, EnvelopeCodecError> {
191 let bytes = serde_json::to_vec(payload)
192 .map_err(|err| EnvelopeCodecError::PayloadEncode(err.to_string()))?;
193 Ok(sha256_digest(&bytes))
194}
195
196pub fn encode_json_envelope<T: Serialize>(
200 kind: &str,
201 format_version: u32,
202 supported_version: u32,
203 payload_checksum: &str,
204 payload: &T,
205) -> Result<Vec<u8>, EnvelopeCodecError> {
206 verify_version(kind, format_version, supported_version)?;
207 let payload_json = serde_json::to_string(payload)
208 .map_err(|err| EnvelopeCodecError::PayloadEncode(err.to_string()))?;
209 verify_checksum_fresh(payload_checksum, payload_json.as_bytes())?;
210 let document = JsonEnvelopeDocument {
211 kind: kind.to_owned(),
212 format_version,
213 payload_checksum: payload_checksum.to_owned(),
214 payload: RawValue::from_string(payload_json)
215 .map_err(|err| EnvelopeCodecError::PayloadEncode(err.to_string()))?,
216 };
217 serde_json::to_vec(&document).map_err(|err| EnvelopeCodecError::EnvelopeEncode(err.to_string()))
218}
219
220pub struct DecodedJsonEnvelope<T> {
222 pub format_version: u32,
224 pub payload_checksum: String,
226 pub payload: T,
228}
229
230pub fn decode_json_envelope<T: DeserializeOwned>(
238 bytes: &[u8],
239 supported_version: u32,
240 classify_kind: impl FnOnce(&str) -> Result<(), EnvelopeCodecError>,
241) -> Result<DecodedJsonEnvelope<T>, EnvelopeCodecError> {
242 decode_json_envelope_probe(bytes, supported_version, classify_kind)?;
243 let document: JsonEnvelopeDocument = serde_json::from_slice(bytes)
244 .map_err(|err| EnvelopeCodecError::EnvelopeDecode(err.to_string()))?;
245 decode_json_envelope_payload(document)
246}
247
248pub fn decode_strict_json_envelope<T: DeserializeOwned>(
252 bytes: &[u8],
253 supported_version: u32,
254 classify_kind: impl FnOnce(&str) -> Result<(), EnvelopeCodecError>,
255) -> Result<DecodedJsonEnvelope<T>, EnvelopeCodecError> {
256 decode_json_envelope_probe(bytes, supported_version, classify_kind)?;
257 let document: StrictJsonEnvelopeDocument = serde_json::from_slice(bytes)
258 .map_err(|err| EnvelopeCodecError::EnvelopeDecode(err.to_string()))?;
259 decode_json_envelope_payload(document.into())
260}
261
262fn decode_json_envelope_probe(
263 bytes: &[u8],
264 supported_version: u32,
265 classify_kind: impl FnOnce(&str) -> Result<(), EnvelopeCodecError>,
266) -> Result<(), EnvelopeCodecError> {
267 let probe: EnvelopeProbe = serde_json::from_slice(bytes)
268 .map_err(|err| EnvelopeCodecError::EnvelopeDecode(err.to_string()))?;
269 classify_kind(&probe.kind)?;
270 verify_version(&probe.kind, probe.format_version, supported_version)?;
271 Ok(())
272}
273
274fn decode_json_envelope_payload<T: DeserializeOwned>(
275 document: JsonEnvelopeDocument,
276) -> Result<DecodedJsonEnvelope<T>, EnvelopeCodecError> {
277 verify_payload_checksum(
278 &document.payload_checksum,
279 document.payload.get().as_bytes(),
280 )?;
281 let payload: T = serde_json::from_str(document.payload.get())
282 .map_err(|err| EnvelopeCodecError::PayloadDecode(err.to_string()))?;
283
284 Ok(DecodedJsonEnvelope {
285 format_version: document.format_version,
286 payload_checksum: document.payload_checksum,
287 payload,
288 })
289}