exocore_core/framing/
multihash.rs

1use std::io;
2
3use bytes::Bytes;
4use multihash::Multihash;
5
6use super::{check_from_size, check_into_size, Error, FrameBuilder, FrameReader};
7use crate::sec::hash::MultihashDigestExt;
8
9/// Check summed frame using a multihash encoded digest
10pub struct MultihashFrame<const S: usize, D: MultihashDigestExt<S>, I: FrameReader> {
11    inner: I,
12    phantom: std::marker::PhantomData<D>,
13}
14
15impl<const S: usize, D: MultihashDigestExt<S>, I: FrameReader> MultihashFrame<S, D, I> {
16    pub fn new(inner: I) -> Result<MultihashFrame<S, D, I>, Error> {
17        check_from_size(D::multihash_size(), inner.exposed_data())?;
18        Ok(MultihashFrame {
19            inner,
20            phantom: std::marker::PhantomData,
21        })
22    }
23
24    pub fn verify(&self) -> Result<bool, Error> {
25        let mut data_digest = D::default();
26        data_digest.update(self.exposed_data());
27        let data_multihash = data_digest.to_multihash();
28
29        let frame_multihash_bytes = self.multihash_bytes();
30        let frame_multihash = Multihash::<S>::from_bytes(frame_multihash_bytes)?;
31
32        Ok(data_multihash == frame_multihash)
33    }
34
35    pub fn multihash_bytes(&self) -> &[u8] {
36        let multihash_size = D::multihash_size();
37        let inner_exposed_data = self.inner.exposed_data();
38        &inner_exposed_data[inner_exposed_data.len() - multihash_size..]
39    }
40}
41
42impl<const S: usize, D: MultihashDigestExt<S>, I: FrameReader> FrameReader
43    for MultihashFrame<S, D, I>
44{
45    type OwnedType = MultihashFrame<S, D, I::OwnedType>;
46
47    fn exposed_data(&self) -> &[u8] {
48        let multihash_size = D::multihash_size();
49        let inner_exposed_data = self.inner.exposed_data();
50        &inner_exposed_data[..inner_exposed_data.len() - multihash_size]
51    }
52
53    fn whole_data(&self) -> &[u8] {
54        self.inner.whole_data()
55    }
56
57    fn to_owned_frame(&self) -> Self::OwnedType {
58        MultihashFrame {
59            inner: self.inner.to_owned_frame(),
60            phantom: std::marker::PhantomData,
61        }
62    }
63}
64
65impl<const S: usize, D: MultihashDigestExt<S>, I: FrameReader + Clone> Clone
66    for MultihashFrame<S, D, I>
67{
68    fn clone(&self) -> Self {
69        MultihashFrame {
70            inner: self.inner.clone(),
71            phantom: std::marker::PhantomData,
72        }
73    }
74}
75
76/// Multihash frame builder
77pub struct MultihashFrameBuilder<const S: usize, D: MultihashDigestExt<S>, I: FrameBuilder> {
78    inner: I,
79    phantom: std::marker::PhantomData<D>,
80}
81
82impl<const S: usize, D: MultihashDigestExt<S>, I: FrameBuilder> MultihashFrameBuilder<S, D, I> {
83    pub fn new(inner: I) -> MultihashFrameBuilder<S, D, I> {
84        MultihashFrameBuilder {
85            inner,
86            phantom: std::marker::PhantomData,
87        }
88    }
89
90    pub fn inner(&mut self) -> &mut I {
91        &mut self.inner
92    }
93}
94
95impl<const S: usize, D: MultihashDigestExt<S>, I: FrameBuilder> FrameBuilder
96    for MultihashFrameBuilder<S, D, I>
97{
98    type OwnedFrameType = MultihashFrame<S, D, Bytes>;
99
100    fn write_to<W: io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
101        // TODO: optimize by creating a proxied writer that digests in streaming
102        let mut buffer = Vec::new();
103        self.inner.write_to(&mut buffer)?;
104        writer.write_all(&buffer)?;
105
106        let mut digest = D::default();
107        digest.update(&buffer);
108        let digest_multihash = digest.to_multihash();
109        digest_multihash.write(writer)?;
110
111        Ok(buffer.len() + D::multihash_size())
112    }
113
114    fn write_into(&self, into: &mut [u8]) -> Result<usize, Error> {
115        let inner_size = self.inner.write_into(into)?;
116
117        let mut digest = D::default();
118        digest.update(&into[..inner_size]);
119        let digest_multihash = digest.to_multihash();
120
121        let total_size = inner_size + D::multihash_size();
122        check_into_size(total_size, into)?;
123
124        digest_multihash.write(&mut into[inner_size..total_size])?;
125
126        Ok(total_size)
127    }
128
129    fn expected_size(&self) -> Option<usize> {
130        self.inner
131            .expected_size()
132            .map(|inner_size| inner_size + D::multihash_size())
133    }
134
135    fn as_owned_frame(&self) -> Self::OwnedFrameType {
136        MultihashFrame::new(self.as_bytes()).expect("Couldn't read just-created frame")
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use bytes::BytesMut;
143
144    use super::*;
145    use crate::{
146        framing::assert_builder_equals,
147        sec::hash::{Sha3_256, Sha3_512},
148    };
149
150    #[test]
151    fn can_build_and_read_multihash() -> anyhow::Result<()> {
152        let inner = Bytes::from_static(b"hello");
153        let builder = MultihashFrameBuilder::<32, Sha3_256, _>::new(inner.clone());
154
155        assert_builder_equals(&builder)?;
156        let frame_bytes = builder.as_bytes();
157
158        let reader1 = MultihashFrame::<32, Sha3_256, _>::new(&frame_bytes[..])?;
159        assert_eq!(frame_bytes, reader1.whole_data());
160        assert_eq!(inner, reader1.exposed_data());
161        assert!(reader1.verify()?);
162
163        let mut modified_buffer = BytesMut::from(frame_bytes.as_ref());
164        modified_buffer[0..5].copy_from_slice(b"world");
165        let reader2 = MultihashFrame::<32, Sha3_256, _>::new(&modified_buffer[..])?;
166        assert!(!reader2.verify()?);
167
168        Ok(())
169    }
170
171    #[test]
172    fn can_build_to_owned() -> anyhow::Result<()> {
173        let inner = Bytes::from_static(b"hello");
174        let builder = MultihashFrameBuilder::<32, Sha3_256, _>::new(inner);
175
176        let frame = builder.as_owned_frame();
177        assert!(frame.verify()?);
178
179        assert_eq!(b"hello", frame.exposed_data());
180
181        Ok(())
182    }
183
184    #[test]
185    fn different_hashes() {
186        let inner = Bytes::from_static(b"hello");
187        let sha3_256 = MultihashFrameBuilder::<32, Sha3_256, _>::new(inner.clone());
188        let sha2_256 = MultihashFrameBuilder::<64, Sha3_512, _>::new(inner);
189
190        assert_ne!(sha3_256.as_bytes(), sha2_256.as_bytes());
191    }
192}