Skip to content

Commit 47963fa

Browse files
committed
chore(rust/hermes-ipfs): code refactoring, add tests for invalid inputs
1 parent dce5543 commit 47963fa

File tree

1 file changed

+126
-37
lines changed

1 file changed

+126
-37
lines changed

rust/hermes-ipfs/src/doc_sync/envelope.rs

Lines changed: 126 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,16 @@ impl EnvelopePayload {
136136
///
137137
/// Returns an error if encoding fails (should not happen with `Vec<u8>` writers).
138138
pub fn to_bytes(&self) -> Result<Vec<u8>, minicbor::encode::Error<Infallible>> {
139-
minicbor::to_vec_with(self, &mut CborContext::Tagged)
139+
minicbor::to_vec(self)
140140
}
141141

142142
/// Decodes `[peer, seq, ver, payload]` from the signed payload array.
143-
fn decode_from_signed(
143+
fn decode_from_signed<C>(
144144
decoder: &mut minicbor::Decoder<'_>,
145-
ctx: &mut CborContext,
145+
ctx: &mut C,
146146
) -> Result<Self, minicbor::decode::Error> {
147147
let peer: PublicKey = decoder.decode_with(ctx)?;
148-
let seq: UuidV7 = decoder.decode_with(ctx)?;
148+
let seq: UuidV7 = decoder.decode_with(&mut CborContext::Tagged)?;
149149
let ver = decoder.u64()?;
150150

151151
if ver != PROTOCOL_VERSION {
@@ -178,26 +178,26 @@ impl EnvelopePayload {
178178
}
179179
}
180180

181-
impl Encode<CborContext> for EnvelopePayload {
181+
impl<C> Encode<C> for EnvelopePayload {
182182
fn encode<W: Write>(
183183
&self,
184184
e: &mut Encoder<W>,
185-
ctx: &mut CborContext,
185+
ctx: &mut C,
186186
) -> Result<(), minicbor::encode::Error<W::Error>> {
187187
e.array(4)?;
188-
e.encode(&self.peer)?
189-
.encode_with(self.seq, ctx)?
188+
e.encode_with(&self.peer, ctx)?
189+
.encode_with(self.seq, &mut CborContext::Tagged)?
190190
.u64(self.ver)?;
191191
<W as Write>::write_all(e.writer_mut(), &self.payload)
192192
.map_err(minicbor::encode::Error::write)?;
193193
Ok(())
194194
}
195195
}
196196

197-
impl<'b> Decode<'b, CborContext> for EnvelopePayload {
197+
impl<'b, C> Decode<'b, C> for EnvelopePayload {
198198
fn decode(
199199
d: &mut minicbor::Decoder<'b>,
200-
ctx: &mut CborContext,
200+
ctx: &mut C,
201201
) -> Result<Self, minicbor::decode::Error> {
202202
let len = d.array()?;
203203
match len {
@@ -215,7 +215,7 @@ impl<'b> Decode<'b, CborContext> for EnvelopePayload {
215215
}
216216

217217
let peer: PublicKey = d.decode_with(ctx)?;
218-
let seq: UuidV7 = d.decode_with(ctx)?;
218+
let seq: UuidV7 = d.decode_with(&mut CborContext::Tagged)?;
219219
let ver = d.u64()?;
220220

221221
if ver != PROTOCOL_VERSION {
@@ -248,6 +248,32 @@ impl<'b> Decode<'b, CborContext> for EnvelopePayload {
248248
}
249249
}
250250

251+
/// Helper struct for encoding the inner [peer, seq, ver, payload, sig] array.
252+
struct SignedPayloadView<'a> {
253+
/// Reference to the envelope payload (providing peer, seq, ver, and body).
254+
payload: &'a EnvelopePayload,
255+
/// Reference to the signature verifying the payload.
256+
signature: &'a Signature,
257+
}
258+
259+
impl<C> Encode<C> for SignedPayloadView<'_> {
260+
fn encode<W: Write>(
261+
&self,
262+
e: &mut Encoder<W>,
263+
_ctx: &mut C,
264+
) -> Result<(), minicbor::encode::Error<W::Error>> {
265+
e.array(5)?;
266+
e.encode(&self.payload.peer)?;
267+
e.encode_with(self.payload.seq, &mut CborContext::Tagged)?;
268+
e.u64(self.payload.ver)?;
269+
e.writer_mut()
270+
.write_all(&self.payload.payload)
271+
.map_err(minicbor::encode::Error::write)?;
272+
e.encode(self.signature)?;
273+
Ok(())
274+
}
275+
}
276+
251277
/// The final outer message structure.
252278
///
253279
/// `Envelope` owns both the `[peer, seq, ver, payload, signature]` array (which
@@ -312,46 +338,33 @@ impl Envelope {
312338
///
313339
/// Returns an error if encoding fails (should not happen with `Vec<u8>` writers).
314340
pub fn to_bytes(&self) -> Result<Vec<u8>, minicbor::encode::Error<Infallible>> {
315-
let mut encoder = Encoder::new(Vec::new());
316-
encoder.bytes(&self.signed_payload_bytes()?)?;
317-
Ok(encoder.into_writer())
318-
}
319-
320-
/// Encodes the `[peer, seq, ver, payload, signature]` array.
321-
fn signed_payload_bytes(&self) -> Result<Vec<u8>, minicbor::encode::Error<Infallible>> {
322-
let mut encoder = Encoder::new(Vec::new());
323-
encoder.array(5)?;
324-
encoder.encode(&self.payload.peer)?;
325-
encoder.encode_with(self.payload.seq, &mut CborContext::Tagged)?;
326-
encoder.u64(self.payload.ver)?;
327-
encoder
328-
.writer_mut()
329-
.write_all(&self.payload.payload)
330-
.map_err(minicbor::encode::Error::write)?;
331-
encoder.encode(&self.signature)?;
332-
Ok(encoder.into_writer())
341+
minicbor::to_vec(self)
333342
}
334343
}
335344

336345
impl<C> Encode<C> for Envelope {
337346
fn encode<W: Write>(
338347
&self,
339348
e: &mut Encoder<W>,
340-
_ctx: &mut C,
349+
ctx: &mut C,
341350
) -> Result<(), minicbor::encode::Error<W::Error>> {
342-
// Spec framing: signed payload array wrapped inside an outer `bstr`.
343-
let signed_bytes = self
344-
.signed_payload_bytes()
345-
.map_err(|err| minicbor::encode::Error::message(err.to_string()))?;
346-
e.bytes(&signed_bytes)?;
351+
let view = SignedPayloadView {
352+
payload: &self.payload,
353+
signature: &self.signature,
354+
};
355+
356+
let inner_bytes = minicbor::to_vec_with(&view, ctx)
357+
.map_err(|e| minicbor::encode::Error::message(e.to_string()))?;
358+
359+
e.bytes(&inner_bytes)?;
347360
Ok(())
348361
}
349362
}
350363

351-
impl<'b> Decode<'b, CborContext> for Envelope {
364+
impl<'b, C> Decode<'b, C> for Envelope {
352365
fn decode(
353366
d: &mut minicbor::Decoder<'b>,
354-
ctx: &mut CborContext,
367+
ctx: &mut C,
355368
) -> Result<Self, minicbor::decode::Error> {
356369
let signed_payload_bytes = d.bytes()?;
357370
let mut signed_decoder = minicbor::Decoder::new(signed_payload_bytes);
@@ -506,4 +519,80 @@ mod tests {
506519
let result = EnvelopePayload::new(signing_key.verifying_key(), payload_bytes);
507520
assert!(result.is_err(), "non-map payload must be rejected");
508521
}
522+
523+
#[test]
524+
fn decode_rejects_missing_outer_bstr() {
525+
// Attempt to decode the array directly without the outer bstr wrapping
526+
let signing_key = signing_key();
527+
let payload_body = sample_payload_body();
528+
let payload =
529+
EnvelopePayload::new(signing_key.verifying_key(), payload_body).expect("payload");
530+
let signature = signing_key
531+
.try_sign(&payload.to_bytes().expect("bytes"))
532+
.expect("signature");
533+
let signature = Signature(signature);
534+
535+
let view = SignedPayloadView {
536+
payload: &payload,
537+
signature: &signature,
538+
};
539+
let encoded_array = minicbor::to_vec(&view).unwrap();
540+
541+
let mut decoder = Decoder::new(&encoded_array);
542+
// We use () context here for simplicity
543+
let result: Result<Envelope, _> = decoder.decode_with(&mut ());
544+
545+
// This should fail because `decode` expects `d.bytes()` (bstr) first,
546+
// but it will encounter an array tag (0x85).
547+
assert!(result.is_err(), "must reject content without outer bstr");
548+
}
549+
550+
#[test]
551+
fn decode_rejects_malformed_inner_array_len() {
552+
let signing_key = signing_key();
553+
let payload_body = sample_payload_body();
554+
let payload =
555+
EnvelopePayload::new(signing_key.verifying_key(), payload_body).expect("payload");
556+
557+
// Construct a fake array of length 4 instead of 5
558+
let mut bad_array = Encoder::new(Vec::new());
559+
bad_array.array(4).unwrap(); // Wrong length
560+
bad_array.encode(&payload.peer).unwrap();
561+
bad_array
562+
.encode_with(payload.seq, &mut CborContext::Tagged)
563+
.unwrap();
564+
bad_array.u64(PROTOCOL_VERSION).unwrap();
565+
// Skip payload & signature to force length error or skip signature
566+
567+
let mut envelope = Encoder::new(Vec::new());
568+
envelope.bytes(&bad_array.into_writer()).unwrap();
569+
let bytes = envelope.into_writer();
570+
571+
let mut decoder = Decoder::new(&bytes);
572+
let result: Result<Envelope, _> = decoder.decode_with(&mut ());
573+
assert!(result.is_err(), "must reject wrong array length");
574+
}
575+
576+
#[test]
577+
fn compiles_with_custom_context() {
578+
// Verify that we can pass a custom struct as context C
579+
struct MyMetrics {
580+
_bytes_read: usize,
581+
}
582+
583+
let signing_key = signing_key();
584+
let payload_body = sample_payload_body();
585+
let payload = EnvelopePayload::new(signing_key.verifying_key(), payload_body.clone())
586+
.expect("payload");
587+
let signature = signing_key.sign(&payload.to_bytes().unwrap());
588+
let envelope = Envelope::new(payload, signature).expect("envelope");
589+
590+
let bytes = envelope.to_bytes().expect("bytes");
591+
592+
let mut ctx = MyMetrics { _bytes_read: 0 };
593+
let mut decoder = Decoder::new(&bytes);
594+
595+
// This validates that the generic <C> is properly propagated
596+
let _decoded: Envelope = decoder.decode_with(&mut ctx).expect("decode with context");
597+
}
509598
}

0 commit comments

Comments
 (0)