massa_models/
secure_share.rs

1use std::fmt::Display;
2
3use crate::{address::Address, error::ModelsError};
4use massa_hash::Hash;
5use massa_serialization::{Deserializer, SerializeError, Serializer};
6use massa_signature::{
7    KeyPair, PublicKey, PublicKeyDeserializer, Signature, SignatureDeserializer,
8};
9use nom::{
10    error::{context, ContextError, ParseError},
11    sequence::tuple,
12    IResult,
13};
14use serde::{Deserialize, Serialize};
15
16/// Packages type T such that it can be securely sent and received in a trust-free network
17///
18/// If the internal content is mutated, then it must be re-wrapped, as the associated
19/// signature, serialized data, etc. would no longer be in sync
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
21pub struct SecureShare<T, ID>
22where
23    T: Display + SecureShareContent,
24    ID: Id,
25{
26    /// Reference contents. Not required for the security protocols.
27    ///
28    /// Use the Lightweight equivalent structures when you need verifiable
29    /// serialized data, but do not need to read the values directly (such as when sending)
30    pub content: T,
31    #[serde(skip)]
32    /// Content in sharable, deserializable form. Is used in the secure verification protocols.
33    pub serialized_data: Vec<u8>,
34
35    /// A cryptographically generated value using `serialized_data` and a public key.
36    pub signature: Signature,
37    /// The public-key component used in the generation of the signature
38    pub content_creator_pub_key: PublicKey,
39    /// Derived from the same public key used to generate the signature
40    pub content_creator_address: Address,
41    /// A secure hash of the data. See also [massa_hash::Hash]
42    pub id: ID,
43}
44
45/// Used by signed structure
46/// TODO: Make this trait use versions
47pub trait Id {
48    /// New id from hash
49    fn new(hash: Hash) -> Self;
50    /// Get a reference to the underlying hash
51    fn get_hash(&self) -> &Hash;
52}
53
54/// Trait that define a structure that can be signed for secure sharing.
55pub trait SecureShareContent
56where
57    Self: Sized + Display,
58{
59    /// Sign the SecureShare given the content
60    fn sign(&self, keypair: &KeyPair, content_hash: &Hash) -> Result<Signature, ModelsError> {
61        Ok(keypair.sign(&self.compute_signed_hash(&keypair.get_public_key(), content_hash))?)
62    }
63
64    /// verify signature
65    fn verify_signature(
66        &self,
67        public_key: &PublicKey,
68        content_hash: &Hash,
69        signature: &Signature,
70    ) -> Result<(), ModelsError> {
71        Ok(public_key.verify_signature(
72            &self.compute_signed_hash(public_key, content_hash),
73            signature,
74        )?)
75    }
76
77    /// Using the provided key-pair, applies a cryptographic signature, and packages
78    /// the data required to share and verify the data in a trust-free network of peers.
79    fn new_verifiable<Ser: Serializer<Self>, ID: Id>(
80        self,
81        content_serializer: Ser,
82        keypair: &KeyPair,
83        chain_id: u64,
84    ) -> Result<SecureShare<Self, ID>, ModelsError> {
85        let mut content_serialized = Vec::new();
86        content_serializer.serialize(&self, &mut content_serialized)?;
87        let public_key = keypair.get_public_key();
88        let hash = Self::compute_hash(&self, &content_serialized, &public_key, chain_id);
89        let creator_address = Address::from_public_key(&public_key);
90        Ok(SecureShare {
91            signature: self.sign(keypair, &hash)?,
92            content_creator_pub_key: public_key,
93            content_creator_address: creator_address,
94            content: self,
95            serialized_data: content_serialized,
96            id: ID::new(hash),
97        })
98    }
99
100    /// Compute hash
101    fn compute_hash(
102        &self,
103        content_serialized: &[u8],
104        content_creator_pub_key: &PublicKey,
105        _chain_id: u64,
106    ) -> Hash {
107        let mut hash_data = Vec::new();
108        hash_data.extend(content_creator_pub_key.to_bytes());
109        hash_data.extend(content_serialized);
110        Hash::compute_from(&hash_data)
111    }
112
113    /// Compute hash used for signature
114    fn compute_signed_hash(&self, _public_key: &PublicKey, content_hash: &Hash) -> Hash {
115        *content_hash
116    }
117
118    /// Serialize the secured structure
119    fn serialize(
120        signature: &Signature,
121        creator_public_key: &PublicKey,
122        serialized_content: &[u8],
123        buffer: &mut Vec<u8>,
124    ) -> Result<(), SerializeError> {
125        buffer.extend(signature.to_bytes());
126        buffer.extend(creator_public_key.to_bytes());
127        buffer.extend(serialized_content);
128        Ok(())
129    }
130
131    /// Deserialize the secured structure
132    fn deserialize<
133        'a,
134        E: ParseError<&'a [u8]> + ContextError<&'a [u8]>,
135        Deser: Deserializer<Self>,
136        ID: Id,
137    >(
138        content_serializer: Option<&dyn Serializer<Self>>,
139        signature_deserializer: &SignatureDeserializer,
140        creator_public_key_deserializer: &PublicKeyDeserializer,
141        content_deserializer: &Deser,
142        buffer: &'a [u8],
143        chain_id: u64,
144    ) -> IResult<&'a [u8], SecureShare<Self, ID>, E> {
145        let (serialized_data, (signature, creator_public_key)) = context(
146            "Failed SecureShare deserialization",
147            tuple((
148                context("Failed signature deserialization", |input| {
149                    signature_deserializer.deserialize(input)
150                }),
151                context("Failed public_key deserialization", |input| {
152                    creator_public_key_deserializer.deserialize(input)
153                }),
154            )),
155        )(buffer)?;
156        let (rest, content) = content_deserializer.deserialize(serialized_data)?;
157        let content_serialized = if let Some(content_serializer) = content_serializer {
158            let mut content_buffer = Vec::new();
159            content_serializer
160                .serialize(&content, &mut content_buffer)
161                .map_err(|_| {
162                    nom::Err::Error(ParseError::from_error_kind(
163                        rest,
164                        nom::error::ErrorKind::Fail,
165                    ))
166                })?;
167            content_buffer
168        } else {
169            // Avoid getting the rest of the data in the serialized data
170            serialized_data[..serialized_data.len() - rest.len()].to_vec()
171        };
172        let creator_address = Address::from_public_key(&creator_public_key);
173        let hash = Self::compute_hash(&content, &content_serialized, &creator_public_key, chain_id);
174
175        Ok((
176            rest,
177            SecureShare {
178                content,
179                signature,
180                content_creator_pub_key: creator_public_key,
181                content_creator_address: creator_address,
182                serialized_data: content_serialized.to_vec(),
183                id: ID::new(hash),
184            },
185        ))
186    }
187}
188
189impl<T, ID> Display for SecureShare<T, ID>
190where
191    T: Display + SecureShareContent,
192    ID: Id,
193{
194    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195        writeln!(f, "Signature: {}", self.signature)?;
196        writeln!(f, "Creator pubkey: {}", self.content_creator_pub_key)?;
197        writeln!(f, "Creator address: {}", self.content_creator_address)?;
198        writeln!(f, "Id: {}", self.id.get_hash())?;
199        writeln!(f, "{}", self.content)?;
200        Ok(())
201    }
202}
203
204impl<T, ID> SecureShare<T, ID>
205where
206    T: Display + SecureShareContent,
207    ID: Id,
208{
209    /// Sign the SecureShare given the content
210    pub fn sign(
211        keypair: &KeyPair,
212        content_hash: &Hash,
213        _content: &T,
214    ) -> Result<Signature, ModelsError> {
215        Ok(keypair.sign(content_hash)?)
216    }
217
218    /// check if self has been signed by public key
219    pub fn verify_signature(&self) -> Result<(), ModelsError> {
220        self.content.verify_signature(
221            &self.content_creator_pub_key,
222            self.id.get_hash(),
223            &self.signature,
224        )
225    }
226
227    /// Compute the signed hash
228    pub fn compute_signed_hash(&self) -> Hash {
229        self.content
230            .compute_signed_hash(&self.content_creator_pub_key, self.id.get_hash())
231    }
232
233    /// get full serialized size
234    pub fn serialized_size(&self) -> usize {
235        self.serialized_data
236            .len()
237            .saturating_add(self.signature.get_ser_len())
238            .saturating_add(self.content_creator_pub_key.get_ser_len())
239    }
240}
241
242// NOTE FOR EXPLICATION: No content serializer because serialized data is already here.
243/// Serializer for `SecureShare` structure
244#[derive(Default, Clone)]
245pub struct SecureShareSerializer;
246
247impl SecureShareSerializer {
248    /// Creates a new `SecureShareSerializer`
249    pub const fn new() -> Self {
250        Self
251    }
252
253    /// This method is used to serialize a `SecureShare` structure and use a custom serializer instead of
254    /// using the serialized form of the content stored in `serialized_data`.
255    /// This is useful when the content need to be serialized in a lighter form in specific cases.
256    ///
257    /// # Arguments:
258    /// * `content_serializer`: Custom serializer to be used instead of the data in `serialized_data`
259    /// * `value`: SecureShare structure to be serialized
260    /// * `buffer`: buffer of serialized data to be extend
261    pub fn serialize_with<Ser, T, ID>(
262        &self,
263        content_serializer: &Ser,
264        value: &SecureShare<T, ID>,
265        buffer: &mut Vec<u8>,
266    ) -> Result<(), SerializeError>
267    where
268        Ser: Serializer<T>,
269        T: Display + SecureShareContent,
270        ID: Id,
271    {
272        let mut content_buffer = Vec::new();
273        content_serializer.serialize(&value.content, &mut content_buffer)?;
274        T::serialize(
275            &value.signature,
276            &value.content_creator_pub_key,
277            &content_buffer,
278            buffer,
279        )
280    }
281}
282
283impl<T, ID> Serializer<SecureShare<T, ID>> for SecureShareSerializer
284where
285    T: Display + SecureShareContent,
286    ID: Id,
287{
288    fn serialize(
289        &self,
290        value: &SecureShare<T, ID>,
291        buffer: &mut Vec<u8>,
292    ) -> Result<(), SerializeError> {
293        T::serialize(
294            &value.signature,
295            &value.content_creator_pub_key,
296            &value.serialized_data,
297            buffer,
298        )
299    }
300}
301
302/// Deserializer for SecureShare structure
303pub struct SecureShareDeserializer<T, Deser>
304where
305    T: Display + SecureShareContent,
306    Deser: Deserializer<T>,
307{
308    signature_deserializer: SignatureDeserializer,
309    public_key_deserializer: PublicKeyDeserializer,
310    content_deserializer: Deser,
311    chain_id: u64,
312    marker_t: std::marker::PhantomData<T>,
313}
314
315impl<T, Deser> SecureShareDeserializer<T, Deser>
316where
317    T: Display + SecureShareContent,
318    Deser: Deserializer<T>,
319{
320    /// Creates a new `SecureShareDeserializer`
321    ///
322    /// # Arguments
323    /// * `content_deserializer` - Deserializer for the content
324    pub const fn new(content_deserializer: Deser, chain_id: u64) -> Self {
325        Self {
326            signature_deserializer: SignatureDeserializer::new(),
327            public_key_deserializer: PublicKeyDeserializer::new(),
328            content_deserializer,
329            chain_id,
330            marker_t: std::marker::PhantomData,
331        }
332    }
333
334    /// This method is used to deserialize data that has been serialized in a lightweight form.
335    /// The buffer doesn't have the whole content serialized and so
336    /// this serialized data isn't coherent with the full structure and can't be used to calculate id and signature.
337    /// We pass a serializer to serialize the full structure and retrieve a coherent `serialized_data`
338    /// that can be use for the id and signature computing.
339    ///
340    /// # Arguments:
341    /// * `content_serializer`: Serializer use to compute the `serialized_data` from the content
342    /// * `buffer`: buffer of serialized data to be deserialized
343    ///
344    /// # Returns:
345    /// A rest (data left over from deserialization), an instance of `T`, and the data enabling signature verification
346    pub fn deserialize_with<
347        'a,
348        E: ParseError<&'a [u8]> + ContextError<&'a [u8]>,
349        ID: Id,
350        Ser: Serializer<T>,
351    >(
352        &self,
353        content_serializer: &Ser,
354        buffer: &'a [u8],
355    ) -> IResult<&'a [u8], SecureShare<T, ID>, E> {
356        T::deserialize(
357            Some(content_serializer),
358            &self.signature_deserializer,
359            &self.public_key_deserializer,
360            &self.content_deserializer,
361            buffer,
362            self.chain_id,
363        )
364    }
365}
366
367impl<T, ID, Deser> Deserializer<SecureShare<T, ID>> for SecureShareDeserializer<T, Deser>
368where
369    T: Display + SecureShareContent,
370    ID: Id,
371    Deser: Deserializer<T>,
372{
373    /// ```
374    /// # use massa_models::{endorsement::{Endorsement, EndorsementSerializer, EndorsementDeserializer}, slot::Slot, secure_share::{SecureShare, SecureShareSerializer, SecureShareDeserializer, SecureShareContent}};
375    /// use massa_models::block_id::BlockId;
376    /// # use massa_serialization::{Deserializer, Serializer, DeserializeError, U16VarIntSerializer, U16VarIntDeserializer};
377    /// # use massa_signature::KeyPair;
378    /// # use std::ops::Bound::Included;
379    /// # use massa_hash::Hash;
380    /// use massa_models::config::CHAINID;
381    ///
382    /// let content = Endorsement {
383    ///    slot: Slot::new(10, 1),
384    ///    index: 0,
385    ///    endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk".as_bytes())),
386    /// };
387    /// let keypair = KeyPair::generate(0).unwrap();
388    /// let secured: SecureShare<Endorsement, BlockId> = Endorsement::new_verifiable(
389    ///    content,
390    ///    EndorsementSerializer::new(),
391    ///    &keypair,
392    ///    *CHAINID
393    /// ).unwrap();
394    /// let mut serialized_data = Vec::new();
395    /// let serialized = SecureShareSerializer::new().serialize(&secured, &mut serialized_data).unwrap();
396    /// let deserializer = SecureShareDeserializer::new(EndorsementDeserializer::new(32, 1), 77);
397    /// let (rest, deserialized): (&[u8], SecureShare<Endorsement, BlockId>) = deserializer.deserialize::<DeserializeError>(&serialized_data).unwrap();
398    /// assert!(rest.is_empty());
399    /// assert_eq!(secured.id, deserialized.id);
400    /// ```
401    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
402        &self,
403        buffer: &'a [u8],
404    ) -> IResult<&'a [u8], SecureShare<T, ID>, E> {
405        T::deserialize(
406            None,
407            &self.signature_deserializer,
408            &self.public_key_deserializer,
409            &self.content_deserializer,
410            buffer,
411            self.chain_id,
412        )
413    }
414}