// Copyright 2024 RustFS Team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! rio_v2 selectively replaces legacy rio components while keeping the //! remaining API surface stable for feature-gated integration. mod compress_reader; mod encrypt_reader; mod s2_index; pub use compress_reader::{CompressReader, DecompressReader}; pub use encrypt_reader::{DecryptReader, EncryptReader, derive_part_key}; pub use rustfs_rio::DEFAULT_ENCRYPTION_BLOCK_SIZE; pub use rustfs_rio::DynReader; pub use rustfs_rio::EtagReader; pub use rustfs_rio::EtagResolvable; pub use rustfs_rio::HardLimitReader; pub use rustfs_rio::HashReader; pub use rustfs_rio::HashReaderDetector; pub use rustfs_rio::HashReaderMut; pub use rustfs_rio::Index; pub use rustfs_rio::LimitReader; pub use rustfs_rio::ReadStream; pub use rustfs_rio::Reader; pub use rustfs_rio::ReaderCapabilities; pub use rustfs_rio::TryGetIndex; pub use rustfs_rio::WarpReader; pub use rustfs_rio::boxed_reader; pub use rustfs_rio::read_checksums; pub use rustfs_rio::resolve_etag_generic; pub use rustfs_rio::wrap_reader; pub use s2_index::{decode_minio_index_bytes, minio_index_storage_bytes}; #[cfg(test)] mod tests { use super::*; use std::io::Cursor; use tokio::io::AsyncReadExt; const DARE_VERSION_20: u8 = 0x20; const DARE_HEADER_SIZE: usize = 16; const DARE_TAG_SIZE: usize = 16; const DARE_PAYLOAD_SIZE: usize = 64 * 1024; const DARE_PACKAGE_SIZE: usize = DARE_HEADER_SIZE + DARE_PAYLOAD_SIZE + DARE_TAG_SIZE; #[tokio::test] async fn encrypt_reader_emits_dare_v2_packages() { let plaintext = vec![0x5Au8; DARE_PAYLOAD_SIZE + 17]; let key_bytes = [0x11u8; 32]; let base_nonce = [0x22u8; 12]; let mut encrypted = Vec::new(); EncryptReader::new(Cursor::new(plaintext), key_bytes, base_nonce) .read_to_end(&mut encrypted) .await .expect("encrypt plaintext into rio_v2 stream"); assert!( encrypted.len() > DARE_PACKAGE_SIZE, "expected at least one full DARE package and one final package" ); assert_eq!(encrypted[0], DARE_VERSION_20, "rio_v2 encrypted streams must start with a DARE V2 header"); assert_eq!( &encrypted[4..16], &base_nonce, "rio_v2 should preserve the configured nonce in the first DARE header" ); let second_header_offset = DARE_PACKAGE_SIZE; assert_eq!( encrypted[second_header_offset], DARE_VERSION_20, "rio_v2 should emit subsequent DARE V2 package headers at 64KiB boundaries" ); assert_ne!( encrypted[second_header_offset + 4] & 0x80, 0, "the final DARE package must set the final flag" ); } }