Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/byte_storage_checksum_collision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fuzz_target!(|test_case: ChecksumTestCase| {
}

// Create valid envelope first
let envelope = match StorageEnvelope::new(test_case.data.clone(), "msgpack".to_string()) {
let envelope = match StorageEnvelope::new(&test_case.data, "msgpack".to_string()) {
Ok(env) => env,
Err(_) => return, // Skip if data too large
};
Expand Down
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/byte_storage_format_injection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fuzz_target!(|data: &[u8]| {

// Create envelope with potentially malicious format
let test_data = vec![b'x'; 100];
let envelope = match StorageEnvelope::new(test_data, format.clone()) {
let envelope = match StorageEnvelope::new(&test_data, format.clone()) {
Ok(env) => env,
Err(_) => return, // Skip if envelope creation fails (acceptable)
};
Expand Down Expand Up @@ -50,7 +50,7 @@ fuzz_target!(|data: &[u8]| {

for pattern in &injection_patterns {
let pattern_data = vec![b'y'; 50];
if let Ok(env) = StorageEnvelope::new(pattern_data, pattern.to_string()) {
if let Ok(env) = StorageEnvelope::new(&pattern_data, pattern.to_string()) {
// Format stored as-is
assert_eq!(env.format, *pattern);

Expand Down
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/integration_layered_security.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fuzz_target!(|data: &[u8]| {
};

// Step 1: Create ByteStorage envelope (compression + checksum)
let envelope = match StorageEnvelope::new(plaintext.to_vec(), "msgpack".to_string()) {
let envelope = match StorageEnvelope::new(plaintext, "msgpack".to_string()) {
Ok(env) => env,
Err(_) => return, // Plaintext too large
};
Expand Down
22 changes: 13 additions & 9 deletions src/byte_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,13 @@ pub struct StorageEnvelope {
}

impl StorageEnvelope {
/// Create new envelope with data compression and checksum
/// Create new envelope with data compression and checksum.
///
/// Takes the input by shared slice: it is only compressed and hashed (both
/// borrow), never retained, so there is no reason to own it. Avoids a
/// full-payload copy (up to `MAX_UNCOMPRESSED_SIZE`) on the write path.
#[cfg(all(feature = "compression", feature = "checksum"))]
pub fn new(data: Vec<u8>, format: String) -> Result<Self, ByteStorageError> {
pub fn new(data: &[u8], format: String) -> Result<Self, ByteStorageError> {
// Security: Check input size before compression
if data.len() > MAX_UNCOMPRESSED_SIZE {
return Err(ByteStorageError::InputTooLarge);
Expand All @@ -77,15 +81,15 @@ impl StorageEnvelope {
let original_size = data.len() as u32;

// Compress with LZ4
let compressed_data = lz4_flex::compress(&data);
let compressed_data = lz4_flex::compress(data);

// Security: Check compressed size
if compressed_data.len() > MAX_COMPRESSED_SIZE {
return Err(ByteStorageError::InputTooLarge);
}

// Generate xxHash3-64 checksum of original data (big-endian = xxhash canonical format)
let checksum = xxh3_64(&data).to_be_bytes();
let checksum = xxh3_64(data).to_be_bytes();

Ok(StorageEnvelope {
compressed_data,
Expand Down Expand Up @@ -181,7 +185,7 @@ impl ByteStorage {
let compression_start = Instant::now();
let original_size = data.len();

let envelope = StorageEnvelope::new(data.to_vec(), format)?;
let envelope = StorageEnvelope::new(data, format)?;

#[cfg(not(target_arch = "wasm32"))]
let compression_micros = compression_start.elapsed().as_micros() as u64;
Expand Down Expand Up @@ -321,21 +325,21 @@ mod tests {
#[test]
fn test_storage_envelope_roundtrip() {
let data = b"Hello, World! This is test data for compression.".to_vec();
let envelope = StorageEnvelope::new(data.clone(), "test".to_string()).unwrap();
let envelope = StorageEnvelope::new(&data, "test".to_string()).unwrap();
let extracted = envelope.extract().unwrap();
assert_eq!(data, extracted);
}

#[test]
fn test_compression_works() {
let data = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_vec(); // Highly compressible
let envelope = StorageEnvelope::new(data.clone(), "test".to_string()).unwrap();
let envelope = StorageEnvelope::new(&data, "test".to_string()).unwrap();
assert!(envelope.compressed_data.len() < data.len());
}

#[test]
fn test_checksum_validation() {
let mut envelope = StorageEnvelope::new(b"test".to_vec(), "test".to_string()).unwrap();
let mut envelope = StorageEnvelope::new(b"test", "test".to_string()).unwrap();
// Corrupt the checksum
envelope.checksum[0] = !envelope.checksum[0];
assert!(envelope.extract().is_err());
Expand Down Expand Up @@ -367,7 +371,7 @@ mod tests {
fn test_size_limits_envelope() {
// Create data exactly at the limit
let max_data = vec![0u8; MAX_UNCOMPRESSED_SIZE];
let envelope_result = StorageEnvelope::new(max_data, "test".to_string());
let envelope_result = StorageEnvelope::new(&max_data, "test".to_string());

// Should succeed at exactly the limit
assert!(envelope_result.is_ok());
Expand Down
Loading