A simple archiving format, designed for storing assets in compact secure containers
vach
, pronounced like "puck" but with a "v", is an archiving and resource transmission format. It was built to be secure, contained and protected. It was, in fact, designed by the SCP to keep your anomalous assets compact and secure during transmission. vach
also has in-built support for multiple compression schemes (LZ4, Snappy and Brolti), data signing, leaf bitflags, encryption and some degree of archive customization. Check out the vach
spec at spec.txt. Any and all help will be much appreciated, especially proof reading the docs and code review.
- You just released some software and don't want your assets pirated or easily read.
- You want a simple convinient way to manage, decompress, decrypt and authenticate assets in distribution.
- You want a pure Rust™️ archive format with no C bindings underneath (bindings for C may become available in the future).
- You want your product to be neat, and all your assets to be in one neat secure container.
- vach: An archiving format, like
tar
,zip
andrar
. Also the base crate for handling.vach
files in your application. - vach-cli:
A CLI tool for dealing with
.vach
files.
- Archive Source: Any source of data. That implements
io::Seek
andio::Read
, for example a file (fs::File
) or in memory buffer (io::Cursor<Vec<u8>>
). - Leaf: Any actual data endpoint within an archive, for example
footstep1.wav
insounds.vach
. - Entry: Some data in the registry section of a
vach
source on an correspondingleaf
. For example,{ id: footstep.wav, location: 45, offset: 2345, flags: 0b0000_0000_0000_0000u16 }
.
use std::{io::Cursor, fs::File};
use vach::prelude::{Builder, BuilderConfig};
let config = BuilderConfig::default();
let mut builder = Builder::default();
// Use `Builder::add( reader, ID )` to add data to the write queue
builder.add(File::open("test_data/background.wav")?, "ambient").unwrap();
builder.add(vec![12, 23, 34, 45, 56, 67, 78, 89, 10], "ftstep").unwrap();
builder.add(b"Fast-Acting Long-Lasting, *Bathroom Reader*" as &[u8], "hello").unwrap();
// let mut target = File::create("sounds.vach")?;
let mut target = Cursor::new(Vec::new());
// The number of bytes written to the file
let size = builder.dump(&mut target, &config).unwrap();
use std::fs::File;
use vach::prelude::{Archive, Resource, Flags};
let target = File::open("sounds.vach")?;
let mut archive = Archive::new(target)?;
let resource: Resource = archive.fetch_mut("ambient")?;
// By default all resources are flagged as NOT authenticated
println!("{}", Sound::new(&resource.data)?);
assert!(!resource.authenticated);
use std::{io::Cursor, fs::File};
use vach::prelude::{Builder, BuilderConfig, Keypair};
use vach::crypto_utils::gen_keypair;
let keypair: Keypair = gen_keypair();
let config: BuilderConfig = BuilderConfig::default().keypair(keypair);
let mut builder: Builder = Builder::default();
// Use different data types under the same builder umbrella, uses dynamic dispatch
let data_1 = vec![12, 23, 45, 56, 67 ,78, 89, 69];
let data_2 = File::open("test_data/footstep.wav").unwrap();
let data_3 = b"Hello, Cassandra!" as &[u8];
// Use `Builder::add( reader, ID )` to add data to the write queue
builder.add(data_3, "ambient").unwrap();
builder.add(data_2, "ftstep").unwrap();
builder.add(data_1.as_slice(), "hello").unwrap();
let mut target = File::create("sounds.vach")?;
builder.dump(&mut target, &config).unwrap();
As Keypair
, SecretKey
and PublicKey
are reflected from ed25519_dalek, you could refer to their docs to read further about them.
use vach::prelude::{Keypair, SecretKey, PublicKey};
use vach::crypto_utils::gen_keypair;
// Generate keys
let keypair : Keypair = gen_keypair();
let secret : SecretKey = keypair.secret;
let public : PublicKey = keypair.public;
// Serialize
let public_key_bytes : [u8; vach::PUBLIC_KEY_LENGTH] = public.to_bytes();
let secret_key_bytes : [u8; vach::SECRET_KEY_LENGTH] = secret.to_bytes();
let keypair_bytes : [u8; vach::KEYPAIR_LENGTH] = keypair.to_bytes();
// Deserialize
let public_key : PublicKey = PublicKey::from_bytes(&public_key_bytes).unwrap();
let secret_key : SecretKey = SecretKey::from_bytes(&secret_key_bytes).unwrap();
let keypair : Keypair = Keypair::from_bytes(&keypair_bytes).unwrap();
// Load public_key
let mut public_key_bytes: [u8; crate::PUBLIC_KEY_LENGTH] = include_bytes!(PUBLIC_KEY);
// Build the Loader config
let mut config = ArchiveConfig::default().key(PublicKey::from_bytes(&public_key_bytes)?);
let target = File::open("sounds.vach")?;
let mut archive = Archive::with_config(target, &config)?;
// Resources are marked as secure (=true) if the signatures match the data
let resource = archive.fetch_mut("ambient")?;
println!("{}", Sound::new(&resource.data)?);
assert!(resource.authenticated);
const MAGIC: &[u8; 5] = b"CSDTD";
let mut target: Cursor<Vec<u8>> = Cursor::new(Vec::new());
// Data to be written
let data_1 = b"Around The World, Fatter better stronker" as &[u8];
let data_2 = b"Imagine if this made sense" as &[u8];
let data_3 = b"Fast-Acting Long-Lasting, *Bathroom Reader*" as &[u8];
// Builder definition
let mut builder = Builder::new();
let config = BuilderConfig::default().magic(*MAGIC);
// Add data
builder.add_leaf(Leaf::new(data_1).id("d1").compress(CompressMode::Always))?;
builder.add_leaf(Leaf::new(data_2).id("d2").compress(CompressMode::Never))?;
builder.add_leaf(Leaf::new(data_3).id("d3").compress(CompressMode::Detect))?;
// Dump data
builder.dump(&mut target, &config)?;
// Load data
let config = ArchiveConfig::default().magic(*MAGIC);
let mut archive = Archive::with_config(target, &config)?;
// Quick assertions
assert_eq!(archive.fetch_mut("d1")?.data.as_slice(), data_1);
assert_eq!(archive.fetch_mut("d2")?.data.as_slice(), data_2);
assert_eq!(archive.fetch_mut("d3")?.data.as_slice(), data_3);
For more information on how to use the library, read the documentation. Always read the documentation! or read the tests, they offer great insight into how the crate works.
- An official CLI, check it out.
- Data encryption.
- Benchmarks.
- Features to turn off (or to turn on) either the
Builder
or theLoader
modules. -
Some(examples)
instead ofNone
- Skynet, (coming very soon).
- Some proper benchmarking code. (Call for participation)
If you appreciate the works of this repo, consider dropping a star. It will be much appreciated; 🌟