12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271 |
- #![allow(incomplete_features)]
- #![feature(trait_upcasting)]
- mod block_path;
- pub use block_path::{BlockPath, BlockPathError};
- pub mod blocktree;
- /// Code which enables cryptographic operations.
- pub mod crypto;
- pub mod msg;
- mod sectored_buf;
- mod trailered;
- #[cfg(feature = "testing")]
- pub mod test_helpers;
- #[macro_use]
- extern crate static_assertions;
- #[cfg(feature = "testing")]
- #[macro_use]
- extern crate lazy_static;
- use brotli::{CompressorWriter, Decompressor};
- use crypto::{
- AsymKeyPub, Ciphertext, Creds, Decrypter, DecrypterExt, Encrypter, EncrypterExt, HashKind,
- MerkleStream, PublicCreds, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind, VarHash,
- };
- use fuse_backend_rs::{
- abi::fuse_abi::{stat64, Attr},
- file_traits::FileReadWriteVolatile,
- };
- use log::error;
- use sectored_buf::SectoredBuf;
- use serde::{Deserialize, Serialize};
- use serde_big_array::BigArray;
- use std::{
- collections::{btree_map, hash_map::DefaultHasher, BTreeMap, HashMap},
- convert::{Infallible, TryFrom},
- fmt::{self, Display, Formatter},
- hash::{Hash as Hashable, Hasher},
- io::{self, Read, Seek, SeekFrom, Write},
- net::SocketAddr,
- ops::{Add, Sub},
- sync::PoisonError,
- time::{Duration, SystemTime},
- };
- use trailered::Trailered;
- #[derive(Debug)]
- pub enum Error {
- MissingWritecap,
- Io(std::io::Error),
- Serde(btserde::Error),
- Crypto(crypto::Error),
- IncorrectSize { expected: usize, actual: usize },
- NoBlockKey,
- Custom(Box<dyn std::fmt::Debug + Send + Sync>),
- }
- impl Error {
- fn custom<E: std::fmt::Debug + Send + Sync + 'static>(err: E) -> Error {
- Error::Custom(Box::new(err))
- }
- }
- impl Display for Error {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- match self {
- Error::MissingWritecap => write!(f, "missing writecap"),
- Error::Io(err) => err.fmt(f),
- Error::Serde(err) => err.fmt(f),
- Error::Crypto(err) => err.fmt(f),
- Error::IncorrectSize { expected, actual } => {
- write!(f, "incorrect size {actual}, expected {expected}")
- }
- Error::NoBlockKey => write!(f, "no block key is present"),
- Error::Custom(err) => err.fmt(f),
- }
- }
- }
- impl std::error::Error for Error {}
- impl From<std::io::Error> for Error {
- fn from(err: std::io::Error) -> Self {
- Error::Io(err)
- }
- }
- impl From<Error> for std::io::Error {
- fn from(err: Error) -> Self {
- io::Error::new(io::ErrorKind::Other, err)
- }
- }
- impl From<btserde::Error> for Error {
- fn from(err: btserde::Error) -> Self {
- Error::Serde(err)
- }
- }
- impl From<crypto::Error> for Error {
- fn from(err: crypto::Error) -> Self {
- Error::Crypto(err)
- }
- }
- impl From<std::num::TryFromIntError> for Error {
- fn from(err: std::num::TryFromIntError) -> Self {
- Error::custom(err)
- }
- }
- impl<T: std::fmt::Debug> From<PoisonError<T>> for Error {
- fn from(err: PoisonError<T>) -> Self {
- Error::custom(err.to_string())
- }
- }
- type Result<T> = std::result::Result<T, Error>;
- /// TODO: Remove this once the error_chain crate is integrated.
- trait BoxInIoErr<T> {
- fn box_err(self) -> std::result::Result<T, io::Error>;
- }
- impl<T, E: std::error::Error + Send + Sync + 'static> BoxInIoErr<T> for std::result::Result<T, E> {
- fn box_err(self) -> std::result::Result<T, io::Error> {
- self.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
- }
- }
- /// TODO: Remove this once the error_chain crate is integrated.
- trait StrInIoErr<T> {
- fn str_err(self) -> std::result::Result<T, io::Error>;
- }
- impl<T, E: Display> StrInIoErr<T> for std::result::Result<T, E> {
- fn str_err(self) -> std::result::Result<T, io::Error> {
- self.map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))
- }
- }
- /// The default sector size to use for new blocks.
- const SECTOR_SZ_DEFAULT: usize = 4096;
- /// Trait for types which provide read and write access to blocks.
- pub trait Block: Read + Write + Seek + MetaAccess + FileReadWriteVolatile {}
- // A trait for streams which only allow reads and writes in fixed sized units called sectors.
- pub trait Sectored {
- // Returns the size of the sector for this stream.
- fn sector_sz(&self) -> usize;
- fn assert_sector_sz(&self, actual: usize) -> Result<()> {
- let expected = self.sector_sz();
- if expected == actual {
- Ok(())
- } else {
- Err(Error::IncorrectSize { expected, actual })
- }
- }
- }
- /// A version of the `Write` trait, which allows integrity information to be supplied when flushing.
- pub trait WriteInteg: Write {
- fn flush_integ(&mut self, integrity: &[u8]) -> io::Result<()>;
- }
- trait Decompose<T> {
- fn into_inner(self) -> T;
- }
- trait TryCompose<T, U: Decompose<T>> {
- type Error;
- fn try_compose(self, inner: T) -> std::result::Result<U, Self::Error>;
- }
- trait Compose<T, U> {
- fn compose(self, inner: T) -> U;
- }
- impl<T, U: Decompose<T>, S: TryCompose<T, U, Error = Infallible>> Compose<T, U> for S {
- fn compose(self, inner: T) -> U {
- let result = self.try_compose(inner);
- // Safety: Infallible has no values, so `result` must be `Ok`.
- unsafe { result.unwrap_unchecked() }
- }
- }
- /// Trait for accessing the metadata associated with a block.
- pub trait MetaAccess {
- fn meta(&self) -> &BlockMeta;
- fn mut_meta(&mut self) -> &mut BlockMeta;
- fn meta_body(&self) -> &BlockMetaBody {
- self.meta().body()
- }
- fn mut_meta_body(&mut self) -> &mut BlockMetaBody {
- self.mut_meta().mut_body()
- }
- }
- /// Extensions to the `Read` trait.
- trait ReadExt: Read {
- /// Reads repeatedly until one of the following occur:
- /// 1. The given buffer is full.
- /// 2. A call to `read` returns 0.
- /// 3. A call to `read` returns an error.
- /// The number of bytes read is returned. If an error is returned, then no bytes were read.
- fn fill_buf(&mut self, mut dest: &mut [u8]) -> io::Result<usize> {
- let dest_len_start = dest.len();
- while !dest.is_empty() {
- let byte_ct = match self.read(dest) {
- Ok(byte_ct) => byte_ct,
- Err(err) => {
- if dest_len_start == dest.len() {
- return Err(err);
- } else {
- // We're not allowed to return an error since we've already read from self.
- error!("an error occurred in fill_buf: {}", err);
- break;
- }
- }
- };
- if 0 == byte_ct {
- break;
- }
- dest = &mut dest[byte_ct..];
- }
- Ok(dest_len_start - dest.len())
- }
- }
- impl<T: Read> ReadExt for T {}
- trait HashExt: Hashable {
- /// Returns the hash produced by the `DefaultHasher`.
- fn default_hash(&self) -> u64 {
- let mut hasher = DefaultHasher::new();
- self.hash(&mut hasher);
- hasher.finish()
- }
- }
- impl<T: Hashable> HashExt for T {}
- /// Metadata that is encrypted.
- #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Hash)]
- pub struct BlockMetaSecrets {
- /// The inode number of the file.
- inode: u64,
- /// Mode of file.
- mode: u32,
- /// Owner UID of file.
- uid: u32,
- /// Owner GID of file.
- gid: u32,
- /// Last access time.
- atime: Epoch,
- /// Last data modification.
- mtime: Epoch,
- /// Last status change.
- ctime: Epoch,
- /// Size of the file in bytes.
- size: u64,
- /// Number of hard links to the file.
- nlink: u32,
- /// The sector size used by the block.
- sect_sz: u32,
- tags: BTreeMap<String, Vec<u8>>,
- }
- impl BlockMetaSecrets {
- pub fn new() -> BlockMetaSecrets {
- Self::default()
- }
- pub fn attr(&self) -> Attr {
- self.into()
- }
- pub fn stat(&self) -> stat64 {
- self.attr().into()
- }
- /// Returns the number of sectors occupied by the block's data.
- pub fn sectors(&self) -> u64 {
- let sect_sz = self.sect_sz as u64;
- if self.size % sect_sz == 0 {
- self.size / sect_sz
- } else {
- self.size / sect_sz + 1
- }
- }
- }
- impl Default for BlockMetaSecrets {
- fn default() -> Self {
- Self {
- inode: 0,
- mode: 0,
- uid: 0,
- gid: 0,
- atime: Epoch::default(),
- mtime: Epoch::default(),
- ctime: Epoch::default(),
- size: 0,
- nlink: 0,
- sect_sz: SECTOR_SZ_DEFAULT.try_into().unwrap(),
- tags: BTreeMap::new(),
- }
- }
- }
- impl From<&BlockMetaSecrets> for Attr {
- fn from(value: &BlockMetaSecrets) -> Self {
- Attr {
- ino: value.inode,
- size: value.size,
- atime: value.atime.value(),
- mtime: value.mtime.value(),
- ctime: value.ctime.value(),
- atimensec: 0,
- mtimensec: 0,
- ctimensec: 0,
- mode: value.mode,
- nlink: value.nlink,
- uid: value.uid,
- gid: value.gid,
- rdev: 0,
- blksize: value.sect_sz,
- blocks: value.sectors(),
- flags: 0,
- }
- }
- }
- /// This struct contains all of the metadata fields associated with a block, except for its
- /// signature. Since this struct implements `Serialize`, this allows for convenient signature
- /// calculations.
- #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
- pub struct BlockMetaBody {
- path: BlockPath,
- /// A copy of the block key encrypted using this block's parent's key. If this is None, then
- /// this block is not encrypted.
- inherit: Option<Ciphertext<SymKey>>,
- readcaps: BTreeMap<Principal, Ciphertext<SymKey>>,
- writecap: Option<Writecap>,
- /// A hash which provides integrity for the contents of the block body.
- integrity: Option<VarHash>,
- /// The public key that corresponds to the private key used to sign these metadata.
- signing_key: AsymKeyPub<Sign>,
- /// Additional metadata which is subject to confidentiality protection.
- secrets: Ciphertext<BlockMetaSecrets>,
- #[serde(skip)]
- /// The cleartext block key.
- block_key: Option<SymKey>,
- #[serde(skip)]
- /// The clear text secret metadata.
- secrets_struct: Option<BlockMetaSecrets>,
- }
- impl BlockMetaBody {
- fn new<C: Creds>(creds: &C) -> Result<BlockMetaBody> {
- let block_key = SymKey::generate(SymKeyKind::default())?;
- let mut readcaps = BTreeMap::new();
- readcaps.insert(creds.principal(), creds.ser_encrypt(&block_key)?);
- Ok(BlockMetaBody {
- path: BlockPath::default(),
- inherit: None,
- readcaps,
- writecap: creds.writecap().map(|e| e.to_owned()),
- integrity: None,
- signing_key: creds.public_sign().to_owned(),
- secrets: block_key.ser_encrypt(&BlockMetaSecrets::default())?,
- block_key: Some(block_key),
- secrets_struct: None,
- })
- }
- pub fn unlock_block_key<C: Creds>(&mut self, creds: &C) -> Result<()> {
- if self.block_key.is_some() {
- return Ok(());
- }
- let readcap = self
- .readcaps
- .get(&creds.principal())
- .ok_or(crypto::Error::NoReadCap)?;
- self.block_key = Some(creds.ser_decrypt(readcap)?);
- Ok(())
- }
- pub fn access_secrets<T, F: FnOnce(&mut BlockMetaSecrets) -> Result<T>>(
- &mut self,
- accessor: F,
- ) -> Result<T> {
- let secrets = match self.secrets_struct.as_mut() {
- Some(secrets) => secrets,
- None => {
- let block_key = self.block_key()?;
- self.secrets_struct = Some(block_key.ser_decrypt(&self.secrets)?);
- self.secrets_struct.as_mut().unwrap()
- }
- };
- let prev = secrets.default_hash();
- let output = accessor(secrets)?;
- if prev != secrets.default_hash() {
- self.secrets = self
- .block_key
- .as_ref()
- .ok_or(Error::NoBlockKey)?
- .ser_encrypt(secrets)?;
- }
- Ok(output)
- }
- pub fn secrets(&self) -> Result<&BlockMetaSecrets> {
- self.secrets_struct
- .as_ref()
- .ok_or_else(|| Error::custom("secrets have not been decrypted"))
- }
- pub fn block_key(&self) -> Result<&SymKey> {
- self.block_key.as_ref().ok_or(Error::NoBlockKey)
- }
- pub fn use_block_key_for<C: Creds>(&mut self, creds: &C) -> Result<&SymKey> {
- let readcap = self
- .readcaps
- .get(&creds.principal())
- .ok_or(crypto::Error::NoReadCap)?;
- let block_key = creds.ser_decrypt(readcap)?;
- self.block_key = Some(block_key);
- self.block_key()
- }
- pub fn mut_block_key(&mut self) -> &mut Option<SymKey> {
- &mut self.block_key
- }
- pub fn integrity(&self) -> Option<&[u8]> {
- self.integrity.as_ref().map(|hash| hash.as_slice())
- }
- pub fn add_readcap_for<E: Encrypter>(&mut self, owner: Principal, key: &E) -> Result<()> {
- let block_key = self.block_key()?;
- self.readcaps.insert(owner, key.ser_encrypt(block_key)?);
- Ok(())
- }
- pub fn set_path(&mut self, path: BlockPath) {
- self.path = path
- }
- }
- /// Signed metadata associated with a block.
- #[derive(Serialize, Deserialize)]
- pub struct BlockMeta {
- body: BlockMetaBody,
- sig: Signature,
- }
- impl BlockMeta {
- fn new<C: Creds>(creds: &C) -> Result<BlockMeta> {
- let body = BlockMetaBody::new(creds)?;
- let sig = Signature::empty(body.signing_key.scheme());
- Ok(BlockMeta { body, sig })
- }
- pub fn body(&self) -> &BlockMetaBody {
- self.as_ref()
- }
- pub fn mut_body(&mut self) -> &mut BlockMetaBody {
- self.as_mut()
- }
- }
- impl AsRef<BlockMetaBody> for BlockMeta {
- fn as_ref(&self) -> &BlockMetaBody {
- &self.body
- }
- }
- impl AsMut<BlockMetaBody> for BlockMeta {
- fn as_mut(&mut self) -> &mut BlockMetaBody {
- &mut self.body
- }
- }
- struct BlockStream<T, C> {
- trailered: Trailered<T, BlockMeta>,
- meta: BlockMeta,
- meta_body_buf: Vec<u8>,
- creds: C,
- }
- impl<T: Read + Seek, C: Creds> BlockStream<T, C> {
- fn new(inner: T, creds: C) -> Result<BlockStream<T, C>> {
- let (trailered, meta) = Trailered::<_, BlockMeta>::new(inner)?;
- let meta = match meta {
- Some(mut meta) => {
- meta.assert_valid()?;
- // We need to use the writecap and signing_key provided by the current credentials.
- meta.body.writecap = creds.writecap().map(|e| e.to_owned());
- meta.body.signing_key = creds.public_sign().to_owned();
- meta.body.use_block_key_for(&creds)?;
- meta
- }
- None => {
- let mut meta = BlockMeta::new(&creds)?;
- meta.mut_body().add_readcap_for(creds.principal(), &creds)?;
- meta.body.writecap = creds.writecap().map(|e| e.to_owned());
- meta
- }
- };
- Ok(BlockStream {
- trailered,
- meta,
- meta_body_buf: Vec::new(),
- creds,
- })
- }
- }
- impl<T: Write + Seek, C: Signer> Write for BlockStream<T, C> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- self.trailered.write(buf)
- }
- fn flush(&mut self) -> io::Result<()> {
- Err(io::Error::new(
- io::ErrorKind::Unsupported,
- "flush is not supported, use flush_integ instead",
- ))
- }
- }
- impl<T: Write + Seek, C: Signer> WriteInteg for BlockStream<T, C> {
- fn flush_integ(&mut self, integrity: &[u8]) -> io::Result<()> {
- let meta_body = &mut self.meta.body;
- let integ = meta_body.integrity.get_or_insert_with(VarHash::default);
- integ.as_mut().copy_from_slice(integrity);
- self.meta_body_buf.clear();
- self.meta.sig = self
- .creds
- .ser_sign_into(&meta_body, &mut self.meta_body_buf)?;
- self.trailered.flush(&self.meta)?;
- Ok(())
- }
- }
- impl<T: Read + Seek, C> Read for BlockStream<T, C> {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- self.trailered.read(buf)
- }
- }
- impl<T: Seek, C> Seek for BlockStream<T, C> {
- /// Seeks to the given position in the stream. If a position beyond the end of the stream is
- /// specified, the the seek will be to the end of the stream.
- fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
- self.trailered.seek(pos)
- }
- }
- impl<T, C: Decrypter + Principaled> MetaAccess for BlockStream<T, C> {
- fn meta(&self) -> &BlockMeta {
- &self.meta
- }
- fn mut_meta(&mut self) -> &mut BlockMeta {
- &mut self.meta
- }
- }
- impl<T: FileReadWriteVolatile, C> FileReadWriteVolatile for BlockStream<T, C> {
- fn read_volatile(
- &mut self,
- slice: fuse_backend_rs::file_buf::FileVolatileSlice,
- ) -> io::Result<usize> {
- self.trailered.read_volatile(slice)
- }
- fn write_volatile(
- &mut self,
- slice: fuse_backend_rs::file_buf::FileVolatileSlice,
- ) -> io::Result<usize> {
- self.trailered.write_volatile(slice)
- }
- fn read_at_volatile(
- &mut self,
- slice: fuse_backend_rs::file_buf::FileVolatileSlice,
- offset: u64,
- ) -> io::Result<usize> {
- self.trailered.read_at_volatile(slice, offset)
- }
- fn write_at_volatile(
- &mut self,
- slice: fuse_backend_rs::file_buf::FileVolatileSlice,
- offset: u64,
- ) -> io::Result<usize> {
- self.trailered.write_at_volatile(slice, offset)
- }
- }
- impl<T: Read + Write + Seek + MetaAccess + FileReadWriteVolatile, C: Creds> Block
- for BlockStream<T, C>
- {
- }
- pub struct BlockOpenOptions<T, C> {
- inner: T,
- creds: C,
- encrypt: bool,
- compress: bool,
- }
- impl BlockOpenOptions<(), ()> {
- pub fn new() -> BlockOpenOptions<(), ()> {
- BlockOpenOptions {
- inner: (),
- creds: (),
- encrypt: true,
- compress: true,
- }
- }
- }
- impl<T, C> BlockOpenOptions<T, C> {
- pub fn with_inner<U>(self, inner: U) -> BlockOpenOptions<U, C> {
- BlockOpenOptions {
- inner,
- creds: self.creds,
- encrypt: self.encrypt,
- compress: self.compress,
- }
- }
- pub fn with_creds<D>(self, creds: D) -> BlockOpenOptions<T, D> {
- BlockOpenOptions {
- inner: self.inner,
- creds,
- encrypt: self.encrypt,
- compress: self.compress,
- }
- }
- pub fn with_encrypt(mut self, encrypt: bool) -> Self {
- self.encrypt = encrypt;
- self
- }
- pub fn with_compress(mut self, compress: bool) -> Self {
- self.compress = compress;
- self
- }
- }
- impl<T: Read + Write + Seek + FileReadWriteVolatile + 'static, C: Creds + 'static>
- BlockOpenOptions<T, C>
- {
- pub fn open(self) -> Result<Box<dyn Block>> {
- let stream = BlockStream::new(self.inner, self.creds)?;
- let block_key = stream.meta_body().block_key().map(|e| e.to_owned())?;
- let mut stream = MerkleStream::new(stream)?;
- stream.assert_root_integrity()?;
- let sect_sz = stream.sector_sz();
- stream.mut_meta_body().access_secrets(|secrets| {
- secrets.sect_sz = sect_sz.try_into()?;
- Ok(())
- })?;
- if self.encrypt {
- let stream = SecretStream::new(block_key).try_compose(stream)?;
- let stream = SectoredBuf::new().try_compose(stream)?;
- Ok(Box::new(stream))
- } else {
- let stream = SectoredBuf::new().try_compose(stream)?;
- Ok(Box::new(stream))
- }
- }
- }
- impl Default for BlockOpenOptions<(), ()> {
- fn default() -> Self {
- Self::new()
- }
- }
- impl<T: Write> Decompose<T> for CompressorWriter<T> {
- fn into_inner(self) -> T {
- self.into_inner()
- }
- }
- impl<T: Read> Decompose<T> for Decompressor<T> {
- fn into_inner(self) -> T {
- self.into_inner()
- }
- }
- #[derive(Clone)]
- pub struct BrotliParams {
- buf_sz: usize,
- quality: u32,
- window_sz: u32,
- }
- impl BrotliParams {
- pub fn new(buf_sz: usize, quality: u32, window_sz: u32) -> BrotliParams {
- BrotliParams {
- buf_sz,
- quality,
- window_sz,
- }
- }
- }
- impl<T: Write> TryCompose<T, CompressorWriter<T>> for BrotliParams {
- type Error = Error;
- fn try_compose(self, inner: T) -> Result<CompressorWriter<T>> {
- Ok(CompressorWriter::new(
- inner,
- self.buf_sz,
- self.quality,
- self.window_sz,
- ))
- }
- }
- impl<T: Read> TryCompose<T, Decompressor<T>> for BrotliParams {
- type Error = Error;
- fn try_compose(self, inner: T) -> Result<Decompressor<T>> {
- Ok(Decompressor::new(inner, self.buf_sz))
- }
- }
- /// An envelopment of a key, which is tagged with the principal who the key is meant for.
- #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
- pub struct Readcap {
- /// The principal this `Readcap` was issued to.
- issued_to: Principal,
- /// An encipherment of a block key using the public key of the principal.
- key: Ciphertext<SymKey>,
- }
- impl Readcap {
- pub fn new(issued_to: VarHash, key: Ciphertext<SymKey>) -> Readcap {
- Readcap {
- issued_to: Principal(issued_to),
- key,
- }
- }
- }
- #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
- pub struct WritecapBody {
- /// The principal this `Writecap` was issued to.
- issued_to: Principal,
- /// The path where this write caps's validity begins.
- path: BlockPath,
- /// The point in time after which this write cap is no longer valid.
- expires: Epoch,
- /// The public key used to sign this write cap.
- signing_key: AsymKeyPub<Sign>,
- }
- /// Verifies that a principal is authorized to write blocks in a tree.
- #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
- pub struct Writecap {
- /// A container for the fields of this writecap which are covered by the signature.
- body: WritecapBody,
- /// A digital signature which covers all of the fields in the write cap except for next.
- signature: Signature,
- /// The next write cap in the chain leading back to the root.
- next: Option<Box<Writecap>>,
- }
- /// Fragments are created from blocks using Erasure Encoding and stored with other nodes in the
- /// network to provide availability and redundancy of data.
- #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
- pub struct Fragment {
- /// The path to the block this fragment is from.
- path: BlockPath,
- /// The serial number of this fragment.
- serial: FragmentSerial,
- /// The actual data.
- body: Vec<u8>,
- }
- impl Fragment {
- /// Create a new fragment with the given fields. If `path_str` cannot be parsed then a failed
- /// `Result` is returned containing a `PathError`.
- pub fn new(
- path_str: &str,
- serial_num: u32,
- body: Vec<u8>,
- ) -> std::result::Result<Fragment, BlockPathError> {
- let result = BlockPath::try_from(path_str);
- Ok(Fragment {
- path: result?,
- serial: FragmentSerial(serial_num),
- body,
- })
- }
- }
- #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
- /// Structure for keeping track of block information in a directory.
- pub struct BlockRecord {
- /// The ID of the block. Note that this is only guaranteed to be unique in the directory this
- /// record is stored in.
- pub inode: u64,
- /// A mapping from fragment serial numbers to fragment records.
- /// TODO: Does this need to have a consistent order?
- pub frags: HashMap<FragmentSerial, FragmentRecord>,
- }
- #[derive(Debug, PartialEq, Serialize, Deserialize)]
- /// Structure for keeping track of server information in a directory.
- pub struct ServerRecord {
- /// The most up-to-date address for this server.
- addr: SocketAddr,
- /// The public credentials for this server.
- pub_creds: PublicCreds,
- }
- #[derive(Debug, PartialEq, Serialize, Deserialize)]
- pub enum DirEntry {
- Directory(BlockRecord),
- File(BlockRecord),
- Server(ServerRecord),
- }
- /// This is the body contained in directory blocks.
- #[derive(Debug, PartialEq, Serialize, Deserialize)]
- pub struct Directory {
- /// This block's descendants.
- entries: BTreeMap<String, DirEntry>,
- }
- impl Directory {
- pub fn new() -> Directory {
- Directory {
- entries: BTreeMap::new(),
- }
- }
- pub fn add_file(&mut self, name: String, inode: u64) -> Result<&mut BlockRecord> {
- let entry = match self.entries.entry(name) {
- btree_map::Entry::Occupied(entry) => {
- return Err(Error::custom(format!(
- "directory already contains entry '{}'",
- entry.key()
- )));
- }
- btree_map::Entry::Vacant(entry) => entry,
- };
- let dir_entry = entry.insert(DirEntry::File(BlockRecord {
- inode,
- frags: HashMap::new(),
- }));
- match dir_entry {
- DirEntry::File(record) => Ok(record),
- _ => Err(Error::custom("DirEntry was not the File variant")),
- }
- }
- }
- impl Default for Directory {
- fn default() -> Self {
- Self::new()
- }
- }
- /// Keeps track of which principal is storing a fragment.
- #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
- pub struct FragmentRecord {
- /// The fragment serial number this record is for.
- serial: FragmentSerial,
- /// The principal who is storing this fragment.
- stored_by: Principal,
- }
- impl FragmentRecord {
- /// Creates a new `FragmentRecord` whose `serial` and `stored_by` fields are set to
- /// the given values.
- pub fn new(serial: u32, stored_by: VarHash) -> FragmentRecord {
- FragmentRecord {
- serial: FragmentSerial(serial),
- stored_by: Principal(stored_by),
- }
- }
- }
- /// An identifier for a security principal, which is any entity that can be authenticated.
- #[derive(
- Debug, PartialEq, Eq, Serialize, Deserialize, Hashable, Clone, Default, PartialOrd, Ord,
- )]
- pub struct Principal(VarHash);
- impl Principal {
- fn kind(&self) -> HashKind {
- HashKind::from(&self.0)
- }
- }
- impl Display for Principal {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
- }
- /// Trait for types which are owned by a `Principal`.
- pub trait Principaled {
- /// Returns the `Principal` that owns `self`, using the given hash algorithm.
- fn principal_of_kind(&self, kind: HashKind) -> Principal;
- /// Returns the `Principal` that owns `self`, using the default hash algorithm.
- fn principal(&self) -> Principal {
- self.principal_of_kind(HashKind::default())
- }
- }
- /// An instant in time represented by the number of seconds since January 1st 1970, 00:00:00 UTC.
- #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
- pub struct Epoch(u64);
- impl Epoch {
- /// Returns the current epoch time.
- pub fn now() -> Epoch {
- let now = SystemTime::now();
- // If the system clock is before the unix epoch, just panic.
- let epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
- Epoch(epoch.as_secs())
- }
- pub fn from_value(value: u64) -> Self {
- value.into()
- }
- pub fn value(self) -> u64 {
- self.into()
- }
- }
- impl Copy for Epoch {}
- impl From<u64> for Epoch {
- fn from(value: u64) -> Self {
- Self(value)
- }
- }
- impl From<Epoch> for u64 {
- fn from(value: Epoch) -> Self {
- value.0
- }
- }
- impl Add<Duration> for Epoch {
- type Output = Self;
- fn add(self, other: Duration) -> Self {
- Epoch(self.0 + other.as_secs())
- }
- }
- impl Sub<Duration> for Epoch {
- type Output = Self;
- fn sub(self, other: Duration) -> Self {
- Epoch(self.0 - other.as_secs())
- }
- }
- /// The serial number of a block fragment.
- #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Hashable)]
- pub struct FragmentSerial(u32);
- /// A struct which may contain a closure. When this struct is dropped, if it contains a closure
- /// then that closure is called. This closure can be removed by calling `disarm`.
- pub struct DropTrigger<F: FnOnce()>(Option<F>);
- impl<F: FnOnce()> DropTrigger<F> {
- pub fn new(trigger: F) -> DropTrigger<F> {
- DropTrigger(Some(trigger))
- }
- pub fn disarm(&mut self) {
- self.0.take();
- }
- }
- impl<F: FnOnce()> Drop for DropTrigger<F> {
- fn drop(&mut self) {
- if let Some(trigger) = self.0.take() {
- trigger()
- }
- }
- }
- #[cfg(feature = "testing")]
- #[cfg(test)]
- mod tests {
- use std::{fs::OpenOptions, io::Cursor, path::PathBuf};
- use crate::crypto::{
- tpm::{TpmCredStore, TpmCreds},
- ConcreteCreds, CredStore, CredsPriv,
- };
- use super::*;
- use btserde::{from_vec, to_vec};
- use tempdir::TempDir;
- use test_helpers::*;
- #[test]
- fn brotli_compress_decompress() {
- const SECT_SZ: usize = SECTOR_SZ_DEFAULT;
- const SECT_CT: usize = 16;
- let params = BrotliParams::new(SECT_SZ, 8, 20);
- let mut memory = Cursor::new([0u8; SECT_SZ * SECT_CT]);
- {
- let write: CompressorWriter<_> = params
- .clone()
- .try_compose(&mut memory)
- .expect("compose for write failed");
- write_fill(write, SECT_SZ, SECT_CT);
- }
- memory.seek(SeekFrom::Start(0)).expect("seek failed");
- {
- let read: Decompressor<_> = params
- .try_compose(&mut memory)
- .expect("compose for read failed");
- read_check(read, SECT_SZ, SECT_CT);
- }
- }
- #[test]
- fn block_can_create_empty() {
- let harness = SwtpmHarness::new().expect("failed to start swtpm");
- let context = harness.context().expect("failed to retrieve context");
- let cred_store = TpmCredStore::new(context, harness.state_path())
- .expect("failed to create TpmCredStore");
- let creds = cred_store.node_creds().expect("failed to get node creds");
- BlockOpenOptions::new()
- .with_inner(BtCursor::new(Vec::<u8>::new()))
- .with_creds(creds)
- .with_encrypt(true)
- .open()
- .expect("failed to open block");
- }
- /// Tests that the `BlockMetaBody` struct has an updated secrets struct after it is modified
- /// in the `access_secrets` method.
- #[test]
- fn block_meta_body_secrets_updated_after_access() {
- const UID: u32 = 1000;
- let creds = test_helpers::NODE_CREDS.clone();
- let vec = {
- let mut body = BlockMetaBody::new(&creds).expect("failed to create meta body");
- body.access_secrets(|secrets| {
- secrets.uid = UID;
- Ok(())
- })
- .expect("access secrets failed");
- to_vec(&body).expect("to_vec failed")
- };
- let mut body = from_vec::<BlockMetaBody>(&vec).expect("from_vec failed");
- body.unlock_block_key(&creds)
- .expect("unlock_block_key failed");
- let actual_uid = body
- .access_secrets(|secrets| Ok(secrets.uid))
- .expect("access_secrets failed");
- assert_eq!(UID, actual_uid);
- }
- struct BlockTestCase {
- root_creds: TpmCreds,
- node_creds: TpmCreds,
- _swtpm: SwtpmHarness,
- temp_dir: TempDir,
- }
- impl BlockTestCase {
- const ROOT_PASSWORD: &'static str = "(1337Prestidigitation7331)";
- fn new() -> BlockTestCase {
- let temp_dir = TempDir::new("block_test").expect("failed to create temp dir");
- let swtpm = SwtpmHarness::new().expect("failed to start swtpm");
- let context = swtpm.context().expect("failed to retrieve context");
- let cred_store = TpmCredStore::new(context, swtpm.state_path())
- .expect("failed to create TpmCredStore");
- let root_creds = cred_store
- .gen_root_creds(Self::ROOT_PASSWORD)
- .expect("failed to get root creds");
- let mut node_creds = cred_store.node_creds().expect("failed to get node creds");
- let writecap = root_creds
- .issue_writecap(
- node_creds.principal(),
- vec!["nodes".to_string(), "phone".to_string()],
- Epoch::now() + Duration::from_secs(3600),
- )
- .expect("failed to issue writecap");
- node_creds.set_writecap(writecap);
- BlockTestCase {
- temp_dir,
- _swtpm: swtpm,
- node_creds,
- root_creds,
- }
- }
- fn fs_path(&self, path: &crate::BlockPath) -> PathBuf {
- let mut fs_path = self.temp_dir.path().to_owned();
- fs_path.extend(path.components());
- fs_path
- }
- fn open_new(&mut self, path: &crate::BlockPath) -> Box<dyn Block> {
- let file = OpenOptions::new()
- .create_new(true)
- .read(true)
- .write(true)
- .open(&self.fs_path(path))
- .expect("failed to open file");
- let mut block = BlockOpenOptions::new()
- .with_inner(file)
- .with_creds(self.node_creds.clone())
- .with_encrypt(true)
- .open()
- .expect("failed to open block");
- block
- .mut_meta_body()
- .set_path(self.node_creds.writecap().unwrap().body.path.clone());
- block
- }
- fn open_existing(&mut self, path: &crate::BlockPath) -> Box<dyn Block> {
- let file = OpenOptions::new()
- .read(true)
- .write(true)
- .open(&self.fs_path(path))
- .expect("failed to reopen file");
- BlockOpenOptions::new()
- .with_inner(file)
- .with_creds(self.node_creds.clone())
- .with_encrypt(true)
- .open()
- .expect("failed to reopen block")
- }
- }
- #[test]
- fn block_contents_persisted() {
- const EXPECTED: &[u8] = b"Silly sordid sulking sultans.";
- let mut case = BlockTestCase::new();
- let path = BlockPath::new(case.root_creds.principal(), vec!["test.blk".to_string()]);
- {
- let mut block = case.open_new(&path);
- block.write(EXPECTED).expect("failed to write");
- block.flush().expect("flush failed");
- }
- let mut block = case.open_existing(&path);
- let mut actual = [0u8; EXPECTED.len()];
- block.read(&mut actual).expect("read failed");
- assert_eq!(EXPECTED, actual);
- }
- #[test]
- fn block_write_twice() {
- const EXPECTED: &[u8] = b"Cool callous calamitous colonels.";
- const MID: usize = EXPECTED.len() / 2;
- let mut case = BlockTestCase::new();
- let path = BlockPath::new(case.root_creds.principal(), vec!["test.blk".to_string()]);
- {
- let mut block = case.open_new(&path);
- block.write(&EXPECTED[..MID]).expect("first write failed");
- block.flush().expect("first flush failed");
- }
- {
- let mut block = case.open_existing(&path);
- block
- .seek(SeekFrom::Start(MID.try_into().unwrap()))
- .expect("seek failed");
- block.write(&EXPECTED[MID..]).expect("second write failed");
- block.flush().expect("second flush failed");
- }
- {
- let mut block = case.open_existing(&path);
- let mut actual = [0u8; EXPECTED.len()];
- block.read(&mut actual).expect("read failed");
- assert_eq!(EXPECTED, actual);
- }
- }
- #[test]
- fn block_write_with_different_creds() {
- const EXPECTED: &[u8] = b"Cool callous calamitous colonels.";
- const MID: usize = EXPECTED.len() / 2;
- let mut case = BlockTestCase::new();
- let path = BlockPath::new(case.root_creds.principal(), vec!["test.blk".to_string()]);
- let app_creds = {
- let mut app_creds = ConcreteCreds::generate().expect("failed to generate app creds");
- let writecap = case
- .root_creds
- .issue_writecap(
- app_creds.principal(),
- path.components().map(|e| e.to_string()).collect(),
- Epoch::now() + Duration::from_secs(60),
- )
- .expect("failed to issue writecap");
- app_creds.set_writecap(writecap);
- app_creds
- };
- {
- let mut block = case.open_new(&path);
- block
- .mut_meta_body()
- .add_readcap_for(app_creds.principal(), &app_creds)
- .expect("failed to add readcap");
- block.write(&EXPECTED[..MID]).expect("first write failed");
- block.flush().expect("first flush failed");
- }
- {
- let file = OpenOptions::new()
- .read(true)
- .write(true)
- .open(case.fs_path(&path))
- .expect("failed to reopen file");
- let mut block = BlockOpenOptions::new()
- .with_inner(file)
- // Note that this write is performed using app_creds.
- .with_creds(app_creds)
- .with_encrypt(true)
- .open()
- .expect("failed to reopen block");
- block
- .seek(SeekFrom::Start(MID.try_into().unwrap()))
- .expect("seek failed");
- block.write(&EXPECTED[MID..]).expect("second write failed");
- block.flush().expect("second flush failed");
- }
- {
- let file = OpenOptions::new()
- .read(true)
- .write(true)
- .open(case.fs_path(&path))
- .expect("failed to reopen file");
- let mut block = BlockOpenOptions::new()
- .with_inner(file)
- .with_creds(case.node_creds)
- .with_encrypt(true)
- .open()
- .expect("failed to reopen block");
- let mut actual = [0u8; EXPECTED.len()];
- block.read(&mut actual).expect("read failed");
- assert_eq!(EXPECTED, actual);
- }
- }
- }
|