Skip to main content

rns_core/resource/
assembly.rs

1//! Owned, authenticated Resource data that can be verified away from its receiver.
2use super::{
3    parts::extract_metadata_owned,
4    proof::{build_proof_data, data_hash_state, finish_with},
5    ResourceAction, ResourceError,
6};
7use crate::{
8    buffer::types::{Compressor, DecompressError},
9    constants::RESOURCE_RANDOM_HASH_SIZE,
10};
11use alloc::{sync::Arc, vec, vec::Vec};
12
13/// Identity of one assembly attempt. Equality compares attempts, not Resource hashes.
14#[derive(Clone, Debug)]
15pub struct AssemblyId(Arc<()>);
16impl AssemblyId {
17    pub(super) fn new() -> Self {
18        Self(Arc::new(()))
19    }
20}
21impl PartialEq for AssemblyId {
22    fn eq(&self, other: &Self) -> bool {
23        Arc::ptr_eq(&self.0, &other.0)
24    }
25}
26impl Eq for AssemblyId {}
27
28/// A consuming handoff. Authentication has already succeeded when encryption is used.
29/// No protocol actions are published until the receiver accepts the returned result.
30pub struct ResourceAssembly {
31    pub(super) id: AssemblyId,
32    pub(super) data: AssemblyData,
33}
34/// A completed attempt, bound to its original receiver. Contents are deliberately private.
35pub struct AssemblyResult {
36    pub(super) id: AssemblyId,
37    pub(super) result: Result<Vec<ResourceAction>, ResourceError>,
38}
39impl ResourceAssembly {
40    pub fn id(&self) -> &AssemblyId {
41        &self.id
42    }
43    /// Owned plaintext capacity retained while the attempt is queued.
44    pub fn input_capacity(&self) -> usize {
45        self.data.plaintext.capacity()
46    }
47    /// Decoder output bound, independent of the advertised data size.
48    pub fn max_output_size(&self) -> usize {
49        self.data.limit
50    }
51    pub fn run(self, compressor: &dyn Compressor) -> AssemblyResult {
52        AssemblyResult {
53            id: self.id,
54            result: self.data.run(compressor),
55        }
56    }
57}
58
59pub(super) struct AssemblyData {
60    pub plaintext: Vec<u8>,
61    pub random_hash: [u8; RESOURCE_RANDOM_HASH_SIZE],
62    pub resource_hash: [u8; 32],
63    pub compressed: bool,
64    pub metadata: bool,
65    pub limit: usize,
66}
67impl AssemblyData {
68    pub fn run(self, compressor: &dyn Compressor) -> Result<Vec<ResourceAction>, ResourceError> {
69        // The receiver already dropped the random prefix.
70        let plaintext = self.plaintext;
71        let decompressed = if self.compressed {
72            compressor
73                .decompress_bounded(&plaintext, self.limit)
74                .map_err(|error| match error {
75                    DecompressError::TooLarge => ResourceError::TooLarge,
76                    DecompressError::InvalidData => ResourceError::DecompressionFailed,
77                })?
78        } else {
79            plaintext
80        };
81        let data_state = data_hash_state(&decompressed);
82        let hash = finish_with(&data_state, &self.random_hash);
83        if hash != self.resource_hash {
84            return Err(ResourceError::HashMismatch);
85        }
86        // Proof covers the complete data, including its metadata prefix.
87        let proof = build_proof_data(&hash, &finish_with(&data_state, &hash));
88        let (data, metadata) = if self.metadata {
89            let (metadata, data) =
90                extract_metadata_owned(decompressed).ok_or(ResourceError::InvalidPart)?;
91            (data, Some(metadata))
92        } else {
93            (decompressed, None)
94        };
95        Ok(vec![
96            ResourceAction::SendProof(proof),
97            ResourceAction::DataReceived { data, metadata },
98            ResourceAction::Completed,
99        ])
100    }
101}