1use crate::digest::sha256_digest;
18use serde::de::DeserializeOwned;
19use serde::{Deserialize, Serialize};
20use serde_json::value::RawValue;
21use thiserror::Error;
22
23#[derive(Debug, Deserialize)]
30pub struct EnvelopeProbe {
31 pub kind: String,
33 pub format_version: u32,
35}
36
37#[derive(Debug, Error)]
41#[non_exhaustive]
42pub enum EnvelopeCodecError {
43 #[error("failed to encode envelope payload: {0}")]
45 PayloadEncode(String),
46 #[error("failed to encode envelope document: {0}")]
48 EnvelopeEncode(String),
49 #[error("failed to decode envelope document: {0}")]
51 EnvelopeDecode(String),
52 #[error("failed to decode envelope payload: {0}")]
54 PayloadDecode(String),
55 #[error("failed to compress envelope: {0}")]
57 Compress(String),
58 #[error("failed to decompress envelope: {0}")]
60 Decompress(String),
61 #[error("unknown envelope kind `{found}`")]
63 UnknownKind {
64 found: String,
66 },
67 #[error("envelope kind mismatch: expected `{expected}`, found `{found}`")]
69 KindMismatch {
70 expected: String,
72 found: String,
74 },
75 #[error(
77 "unsupported `{kind}` envelope format version `{found}`: \
78 this build supports `{supported}`"
79 )]
80 UnsupportedFormatVersion {
81 kind: String,
83 found: u32,
85 supported: u32,
87 },
88 #[error("envelope payload checksum mismatch: expected `{expected}`, actual `{actual}`")]
90 ChecksumMismatch {
91 expected: String,
93 actual: String,
95 },
96 #[error(
98 "envelope checksum `{checksum}` does not match its payload `{actual}`: \
99 rebuild the envelope from its payload"
100 )]
101 StalePayloadChecksum {
102 checksum: String,
104 actual: String,
106 },
107}
108
109pub fn verify_kind(expected: &str, found: &str) -> Result<(), EnvelopeCodecError> {
111 if found != expected {
112 return Err(EnvelopeCodecError::KindMismatch {
113 expected: expected.to_owned(),
114 found: found.to_owned(),
115 });
116 }
117 Ok(())
118}
119
120pub fn verify_version(kind: &str, found: u32, supported: u32) -> Result<(), EnvelopeCodecError> {
123 if found != supported {
124 return Err(EnvelopeCodecError::UnsupportedFormatVersion {
125 kind: kind.to_owned(),
126 found,
127 supported,
128 });
129 }
130 Ok(())
131}
132
133pub fn verify_payload_checksum(
135 expected: &str,
136 payload_bytes: &[u8],
137) -> Result<(), EnvelopeCodecError> {
138 let actual = sha256_digest(payload_bytes);
139 if actual != expected {
140 return Err(EnvelopeCodecError::ChecksumMismatch {
141 expected: expected.to_owned(),
142 actual,
143 });
144 }
145 Ok(())
146}
147
148pub fn verify_checksum_fresh(
152 checksum: &str,
153 payload_bytes: &[u8],
154) -> Result<(), EnvelopeCodecError> {
155 let actual = sha256_digest(payload_bytes);
156 if actual != checksum {
157 return Err(EnvelopeCodecError::StalePayloadChecksum {
158 checksum: checksum.to_owned(),
159 actual,
160 });
161 }
162 Ok(())
163}
164
165#[derive(Serialize, Deserialize)]
170struct JsonEnvelopeDocument {
171 kind: String,
172 format_version: u32,
173 payload_checksum: String,
174 payload: Box<RawValue>,
175}
176
177#[derive(Deserialize)]
178#[serde(deny_unknown_fields)]
179struct StrictJsonEnvelopeDocument {
180 kind: String,
181 format_version: u32,
182 payload_checksum: String,
183 payload: Box<RawValue>,
184}
185
186impl From<StrictJsonEnvelopeDocument> for JsonEnvelopeDocument {
187 fn from(document: StrictJsonEnvelopeDocument) -> Self {
188 Self {
189 kind: document.kind,
190 format_version: document.format_version,
191 payload_checksum: document.payload_checksum,
192 payload: document.payload,
193 }
194 }
195}
196
197pub fn json_payload_checksum<T: Serialize>(payload: &T) -> Result<String, EnvelopeCodecError> {
200 let bytes = serde_json::to_vec(payload)
201 .map_err(|err| EnvelopeCodecError::PayloadEncode(err.to_string()))?;
202 Ok(sha256_digest(&bytes))
203}
204
205pub fn encode_json_envelope<T: Serialize>(
209 kind: &str,
210 format_version: u32,
211 supported_version: u32,
212 payload_checksum: &str,
213 payload: &T,
214) -> Result<Vec<u8>, EnvelopeCodecError> {
215 verify_version(kind, format_version, supported_version)?;
216 let payload_json = serde_json::to_string(payload)
217 .map_err(|err| EnvelopeCodecError::PayloadEncode(err.to_string()))?;
218 verify_checksum_fresh(payload_checksum, payload_json.as_bytes())?;
219 let document = JsonEnvelopeDocument {
220 kind: kind.to_owned(),
221 format_version,
222 payload_checksum: payload_checksum.to_owned(),
223 payload: RawValue::from_string(payload_json)
224 .map_err(|err| EnvelopeCodecError::PayloadEncode(err.to_string()))?,
225 };
226 serde_json::to_vec(&document).map_err(|err| EnvelopeCodecError::EnvelopeEncode(err.to_string()))
227}
228
229pub struct DecodedJsonEnvelope<T> {
231 pub format_version: u32,
233 pub payload_checksum: String,
235 pub payload: T,
237}
238
239pub fn decode_json_envelope<T: DeserializeOwned>(
247 bytes: &[u8],
248 supported_version: u32,
249 classify_kind: impl FnOnce(&str) -> Result<(), EnvelopeCodecError>,
250) -> Result<DecodedJsonEnvelope<T>, EnvelopeCodecError> {
251 decode_json_envelope_probe(bytes, supported_version, classify_kind)?;
252 let document: JsonEnvelopeDocument = serde_json::from_slice(bytes)
253 .map_err(|err| EnvelopeCodecError::EnvelopeDecode(err.to_string()))?;
254 decode_json_envelope_payload(document)
255}
256
257pub fn decode_strict_json_envelope<T: DeserializeOwned>(
261 bytes: &[u8],
262 supported_version: u32,
263 classify_kind: impl FnOnce(&str) -> Result<(), EnvelopeCodecError>,
264) -> Result<DecodedJsonEnvelope<T>, EnvelopeCodecError> {
265 decode_json_envelope_probe(bytes, supported_version, classify_kind)?;
266 let document: StrictJsonEnvelopeDocument = serde_json::from_slice(bytes)
267 .map_err(|err| EnvelopeCodecError::EnvelopeDecode(err.to_string()))?;
268 decode_json_envelope_payload(document.into())
269}
270
271fn decode_json_envelope_probe(
272 bytes: &[u8],
273 supported_version: u32,
274 classify_kind: impl FnOnce(&str) -> Result<(), EnvelopeCodecError>,
275) -> Result<(), EnvelopeCodecError> {
276 let probe: EnvelopeProbe = serde_json::from_slice(bytes)
277 .map_err(|err| EnvelopeCodecError::EnvelopeDecode(err.to_string()))?;
278 classify_kind(&probe.kind)?;
279 verify_version(&probe.kind, probe.format_version, supported_version)?;
280 Ok(())
281}
282
283fn decode_json_envelope_payload<T: DeserializeOwned>(
284 document: JsonEnvelopeDocument,
285) -> Result<DecodedJsonEnvelope<T>, EnvelopeCodecError> {
286 verify_payload_checksum(
287 &document.payload_checksum,
288 document.payload.get().as_bytes(),
289 )?;
290 let payload: T = serde_json::from_str(document.payload.get())
291 .map_err(|err| EnvelopeCodecError::PayloadDecode(err.to_string()))?;
292
293 Ok(DecodedJsonEnvelope {
294 format_version: document.format_version,
295 payload_checksum: document.payload_checksum,
296 payload,
297 })
298}