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)]
41pub enum EnvelopeCodecError {
42 #[error("failed to encode envelope payload: {0}")]
44 PayloadEncode(String),
45 #[error("failed to encode envelope document: {0}")]
47 EnvelopeEncode(String),
48 #[error("failed to decode envelope document: {0}")]
50 EnvelopeDecode(String),
51 #[error("failed to decode envelope payload: {0}")]
53 PayloadDecode(String),
54 #[error("failed to compress envelope: {0}")]
56 Compress(String),
57 #[error("failed to decompress envelope: {0}")]
59 Decompress(String),
60 #[error("unknown envelope kind `{found}`")]
62 UnknownKind {
63 found: String,
65 },
66 #[error("envelope kind mismatch: expected `{expected}`, found `{found}`")]
68 KindMismatch {
69 expected: String,
71 found: String,
73 },
74 #[error(
76 "unsupported `{kind}` envelope format version `{found}`: \
77 this build supports `{supported}`"
78 )]
79 UnsupportedFormatVersion {
80 kind: String,
82 found: u32,
84 supported: u32,
86 },
87 #[error("envelope payload checksum mismatch: expected `{expected}`, actual `{actual}`")]
89 ChecksumMismatch {
90 expected: String,
92 actual: String,
94 },
95 #[error(
97 "envelope checksum `{checksum}` does not match its payload `{actual}`: \
98 rebuild the envelope from its payload"
99 )]
100 StalePayloadChecksum {
101 checksum: String,
103 actual: String,
105 },
106}
107
108pub fn verify_kind(expected: &str, found: &str) -> Result<(), EnvelopeCodecError> {
110 if found != expected {
111 return Err(EnvelopeCodecError::KindMismatch {
112 expected: expected.to_owned(),
113 found: found.to_owned(),
114 });
115 }
116 Ok(())
117}
118
119pub fn verify_version(kind: &str, found: u32, supported: u32) -> Result<(), EnvelopeCodecError> {
122 if found != supported {
123 return Err(EnvelopeCodecError::UnsupportedFormatVersion {
124 kind: kind.to_owned(),
125 found,
126 supported,
127 });
128 }
129 Ok(())
130}
131
132pub fn verify_payload_checksum(
134 expected: &str,
135 payload_bytes: &[u8],
136) -> Result<(), EnvelopeCodecError> {
137 let actual = sha256_digest(payload_bytes);
138 if actual != expected {
139 return Err(EnvelopeCodecError::ChecksumMismatch {
140 expected: expected.to_owned(),
141 actual,
142 });
143 }
144 Ok(())
145}
146
147pub fn verify_checksum_fresh(
151 checksum: &str,
152 payload_bytes: &[u8],
153) -> Result<(), EnvelopeCodecError> {
154 let actual = sha256_digest(payload_bytes);
155 if actual != checksum {
156 return Err(EnvelopeCodecError::StalePayloadChecksum {
157 checksum: checksum.to_owned(),
158 actual,
159 });
160 }
161 Ok(())
162}
163
164#[derive(Serialize, Deserialize)]
169struct JsonEnvelopeDocument {
170 kind: String,
171 format_version: u32,
172 payload_checksum: String,
173 payload: Box<RawValue>,
174}
175
176#[derive(Deserialize)]
177#[serde(deny_unknown_fields)]
178struct StrictJsonEnvelopeDocument {
179 kind: String,
180 format_version: u32,
181 payload_checksum: String,
182 payload: Box<RawValue>,
183}
184
185impl From<StrictJsonEnvelopeDocument> for JsonEnvelopeDocument {
186 fn from(document: StrictJsonEnvelopeDocument) -> Self {
187 Self {
188 kind: document.kind,
189 format_version: document.format_version,
190 payload_checksum: document.payload_checksum,
191 payload: document.payload,
192 }
193 }
194}
195
196pub fn json_payload_checksum<T: Serialize>(payload: &T) -> Result<String, EnvelopeCodecError> {
199 let bytes = serde_json::to_vec(payload)
200 .map_err(|err| EnvelopeCodecError::PayloadEncode(err.to_string()))?;
201 Ok(sha256_digest(&bytes))
202}
203
204pub fn encode_json_envelope<T: Serialize>(
208 kind: &str,
209 format_version: u32,
210 supported_version: u32,
211 payload_checksum: &str,
212 payload: &T,
213) -> Result<Vec<u8>, EnvelopeCodecError> {
214 verify_version(kind, format_version, supported_version)?;
215 let payload_json = serde_json::to_string(payload)
216 .map_err(|err| EnvelopeCodecError::PayloadEncode(err.to_string()))?;
217 verify_checksum_fresh(payload_checksum, payload_json.as_bytes())?;
218 let document = JsonEnvelopeDocument {
219 kind: kind.to_owned(),
220 format_version,
221 payload_checksum: payload_checksum.to_owned(),
222 payload: RawValue::from_string(payload_json)
223 .map_err(|err| EnvelopeCodecError::PayloadEncode(err.to_string()))?,
224 };
225 serde_json::to_vec(&document).map_err(|err| EnvelopeCodecError::EnvelopeEncode(err.to_string()))
226}
227
228pub struct DecodedJsonEnvelope<T> {
230 pub format_version: u32,
232 pub payload_checksum: String,
234 pub payload: T,
236}
237
238pub fn decode_json_envelope<T: DeserializeOwned>(
246 bytes: &[u8],
247 supported_version: u32,
248 classify_kind: impl FnOnce(&str) -> Result<(), EnvelopeCodecError>,
249) -> Result<DecodedJsonEnvelope<T>, EnvelopeCodecError> {
250 decode_json_envelope_probe(bytes, supported_version, classify_kind)?;
251 let document: JsonEnvelopeDocument = serde_json::from_slice(bytes)
252 .map_err(|err| EnvelopeCodecError::EnvelopeDecode(err.to_string()))?;
253 decode_json_envelope_payload(document)
254}
255
256pub fn decode_strict_json_envelope<T: DeserializeOwned>(
260 bytes: &[u8],
261 supported_version: u32,
262 classify_kind: impl FnOnce(&str) -> Result<(), EnvelopeCodecError>,
263) -> Result<DecodedJsonEnvelope<T>, EnvelopeCodecError> {
264 decode_json_envelope_probe(bytes, supported_version, classify_kind)?;
265 let document: StrictJsonEnvelopeDocument = serde_json::from_slice(bytes)
266 .map_err(|err| EnvelopeCodecError::EnvelopeDecode(err.to_string()))?;
267 decode_json_envelope_payload(document.into())
268}
269
270fn decode_json_envelope_probe(
271 bytes: &[u8],
272 supported_version: u32,
273 classify_kind: impl FnOnce(&str) -> Result<(), EnvelopeCodecError>,
274) -> Result<(), EnvelopeCodecError> {
275 let probe: EnvelopeProbe = serde_json::from_slice(bytes)
276 .map_err(|err| EnvelopeCodecError::EnvelopeDecode(err.to_string()))?;
277 classify_kind(&probe.kind)?;
278 verify_version(&probe.kind, probe.format_version, supported_version)?;
279 Ok(())
280}
281
282fn decode_json_envelope_payload<T: DeserializeOwned>(
283 document: JsonEnvelopeDocument,
284) -> Result<DecodedJsonEnvelope<T>, EnvelopeCodecError> {
285 verify_payload_checksum(
286 &document.payload_checksum,
287 document.payload.get().as_bytes(),
288 )?;
289 let payload: T = serde_json::from_str(document.payload.get())
290 .map_err(|err| EnvelopeCodecError::PayloadDecode(err.to_string()))?;
291
292 Ok(DecodedJsonEnvelope {
293 format_version: document.format_version,
294 payload_checksum: document.payload_checksum,
295 payload,
296 })
297}