use serde::{Deserialize, Serialize};
use super::bundle::ProducerAssertion;
use super::error::SealedTransferError;
pub const VERSION: u8 = 1;
pub const MAX_PAYLOAD_FRAGMENT: usize = 32 * 1024;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChunkPlaintext {
pub version: u8,
pub bundle_id: [u8; 16],
pub chunk_index: u16,
pub total_chunks: u16,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub producer_did: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub producer_assertion: Option<ProducerAssertion>,
pub payload_fragment: Vec<u8>,
}
impl ChunkPlaintext {
pub fn aad(&self, digest_algo: &str) -> Vec<u8> {
let algo = digest_algo.as_bytes();
let mut buf = Vec::with_capacity(1 + 16 + 2 + 2 + 1 + algo.len());
buf.push(self.version);
buf.extend_from_slice(&self.bundle_id);
buf.extend_from_slice(&self.chunk_index.to_be_bytes());
buf.extend_from_slice(&self.total_chunks.to_be_bytes());
let algo_len = u8::try_from(algo.len()).unwrap_or(u8::MAX);
buf.push(algo_len);
buf.extend_from_slice(&algo[..algo_len as usize]);
buf
}
}
pub fn reassemble(mut chunks: Vec<ChunkPlaintext>) -> Result<Vec<u8>, SealedTransferError> {
if chunks.is_empty() {
return Err(SealedTransferError::Wire("no chunks supplied".into()));
}
let total = chunks[0].total_chunks as usize;
if total == 0 {
return Err(SealedTransferError::Wire("total_chunks == 0".into()));
}
let bundle_id = chunks[0].bundle_id;
let version = chunks[0].version;
if version != VERSION {
return Err(SealedTransferError::UnsupportedVersion(version));
}
for c in &chunks {
if c.bundle_id != bundle_id {
return Err(SealedTransferError::ChunkMismatch("bundle_id".into()));
}
if c.total_chunks as usize != total {
return Err(SealedTransferError::ChunkMismatch("total_chunks".into()));
}
if c.version != version {
return Err(SealedTransferError::ChunkMismatch("version".into()));
}
if (c.chunk_index as usize) >= total {
return Err(SealedTransferError::ChunkMismatch(
"index out of range".into(),
));
}
}
chunks.sort_by_key(|c| c.chunk_index);
for (i, c) in chunks.iter().enumerate() {
if c.chunk_index as usize != i {
if i > 0 && chunks[i - 1].chunk_index == c.chunk_index {
return Err(SealedTransferError::DuplicateChunk(c.chunk_index));
}
return Err(SealedTransferError::MissingChunks {
have: chunks.len(),
expected: total,
});
}
}
if chunks.len() != total {
return Err(SealedTransferError::MissingChunks {
have: chunks.len(),
expected: total,
});
}
let mut out = Vec::new();
for c in chunks {
out.extend_from_slice(&c.payload_fragment);
}
Ok(out)
}