123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- // The dead code warnings create too much noise during wire-framing.
- // TODO: Delete this prior to release.
- #![allow(dead_code)]
- use std::{
- collections::HashMap,
- convert::TryFrom,
- hash::Hash as Hashable,
- fmt::{self, Display, Formatter},
- time::{Duration, SystemTime},
- ops::{Add, Sub}
- };
- use serde::{Serialize, Deserialize};
- use serde_big_array::BigArray;
- #[cfg(test)]
- mod test_helpers;
- #[cfg(test)]
- mod serde_tests;
- mod crypto;
- use crypto::{Hash, HashKind, Signature, SymKey, AsymKey, Public, Cryptotext};
- /// A Block tagged with its version number.
- #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
- enum VersionedBlock {
- V0(Block)
- }
- /// A container which binds together ciphertext along with the metadata needed to identify,
- /// verify and decrypt it.
- #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
- struct Block {
- /// Identifies this block and defines its location in the tree.
- path: Path,
- /// This field contains a collection of `Readcap`s indexed by the principal who holds them.
- /// `Readcap`s are envelopments of the key used to encrypt this block.
- readcaps: HashMap<Principal, Cryptotext<SymKey>>,
- /// This field is used to verify that the signer of this block had permission to write it.
- /// It contains a certificate chain that must lead back to the root key for the tree this block
- /// is part of.
- writecap: Writecap,
- /// The encrypted data contained in this block.
- body: Cryptotext<Vec<u8>>,
- /// The contents of the block are covered by a digital signature contained in this field.
- signature: Signature
- }
- /// An envelopment of a key, which is tagged with the principal who the key is meant for.
- #[derive(Debug, PartialEq, Serialize, Deserialize)]
- 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: Cryptotext<SymKey>,
- }
- impl Readcap {
- fn new(issued_to: Hash, key: Cryptotext<SymKey>) -> Readcap {
- Readcap { issued_to: Principal(issued_to), key }
- }
- }
- /// Verifies that a principal is authorized to write blocks in a tree.
- #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
- struct Writecap {
- /// The principal this `Writecap` was issued to.
- issued_to: Principal,
- /// The path where this write caps's validity begins.
- path: Path,
- /// 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: AsymKey<Public>,
- /// 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, Serialize, Deserialize)]
- struct Fragment {
- /// The path to the block this fragment is from.
- path: Path,
- /// 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`.
- fn new(path_str: &str, serial_num: u32, body: Vec<u8>) -> Result<Fragment, PathError> {
- let result = Path::try_from(path_str);
- Ok(Fragment {
- path: result?,
- serial: FragmentSerial(serial_num),
- body
- })
- }
- }
- /// The body of every non-leaf node in a tree contains this data structure.
- #[derive(Debug, PartialEq, Serialize, Deserialize)]
- struct Directory {
- /// The nodes that are attached to the tree at this block.
- nodes: Vec<Principal>,
- /// This block's descendants.
- children: HashMap<String, HashMap<FragmentSerial, FragmentRecord>>,
- }
- /// Keeps track of which principal is storing a fragment.
- #[derive(Debug, PartialEq, Serialize, Deserialize)]
- 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.
- fn new(serial: u32, stored_by: Hash) -> 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)]
- pub struct Principal(Hash);
- impl Principal {
- fn kind(&self) -> HashKind {
- HashKind::from(&self.0)
- }
- }
- /// Trait for types which are owned by a `Principal`.
- pub trait Owned {
- /// Returns the `Principal` that owns `self`, using the given hash algorithm.
- fn owner_of_kind(&self, kind: HashKind) -> Principal;
- /// Returns the `Principal` that owns `self`, using the default hash algorithm.
- fn owner(&self) -> Principal {
- self.owner_of_kind(HashKind::default())
- }
- }
- /// An identifier for a block in a tree.
- #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
- struct Path {
- owner: Principal,
- components: Vec<String>,
- }
- impl Path {
- /// The character that is used to separate path components.
- const SEP: char = '/';
- /// The limit, in bytes, of a `Path`'s length.
- const BYTE_LIMIT: usize = 4096;
- /// Returns a result which, when successful, contains the index after the last character in the
- /// current path component.
- fn component_end<I: Iterator<Item = (usize, char)>>(
- start: usize, first: char, pairs: &mut I
- ) -> Result<usize, PathError> {
- if first == Path::SEP {
- return Err(PathError::EmptyComponent);
- }
- let end;
- let mut last = start;
- loop {
- match pairs.next() {
- Some((index, Path::SEP)) => {
- end = index;
- break;
- },
- Some((index, _)) => last = index,
- None => {
- end = last + 1;
- break
- }
- }
- }
- if end == start {
- Err(PathError::EmptyComponent)
- }
- else {
- Ok(end)
- }
- }
- /// Asserts that the number of bytes in the given string is no more than `Path::BYTE_LIMIT`.
- fn assert_not_too_long(string: &str) -> Result<(), PathError> {
- let len = string.len();
- if len > Path::BYTE_LIMIT {
- return Err(PathError::PathTooLong(len))
- }
- Ok(())
- }
- /// Returns true if `other` is a subpath of this `Path`.
- fn contains(&self, other: &Path) -> bool {
- if self.owner != other.owner {
- return false;
- };
- // This path must be no longer than the other path.
- if self.components.len() > other.components.len() {
- return false;
- }
- // Skip the component containing the owner.
- let self_iter = self.components.iter().skip(1);
- let other_iter = other.components.iter().skip(1);
- for pair in self_iter.zip(other_iter) {
- if pair.0 != pair.1 {
- return false;
- }
- }
- true
- }
- }
- impl<'s> TryFrom<&'s str> for Path {
- type Error = PathError;
- fn try_from(string: &'s str) -> Result<Path, PathError> {
- Path::assert_not_too_long(string)?;
- let mut pairs = string.char_indices();
- let mut components = Vec::new();
- let mut last_end = 0;
- while let Some((start, c)) = pairs.next() {
- let end = Path::component_end(start, c, &mut pairs)?;
- last_end = end;
- let slice = &string[start..end];
- components.push(slice.to_string());
- }
- // An empty component is added to the end to indicate if there was a trailing slash.
- if string.len() - 1 == last_end {
- components.push("".to_string());
- }
- let leading = components.get(0).ok_or(PathError::InvalidLeadingComponent)?;
- let hash = Hash::try_from(leading.as_str())
- .map_err(|_| PathError::InvalidLeadingComponent)?;
- Ok(Path {
- owner: Principal(hash),
- components,
- })
- }
- }
- impl Display for Path {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if self.components.is_empty() {
- return write!(f, "");
- };
- let mut iter = self.components.iter();
- let first = iter.next().unwrap();
- let mut output = write!(f, "{}", first);
- for component in iter {
- output = write!(f, "{}{}", Path::SEP, component)
- }
- output
- }
- }
- /// Errors which can occur when converting a string to a `Path`.
- #[derive(Debug, PartialEq)]
- enum PathError {
- /// Occurs when the number of bytes in a string is greater than `Path::BYTE_LIMIT`.
- PathTooLong(usize),
- /// Indicates that a path string was empty.
- Empty,
- /// Occurs when a component in a path string was empty.
- EmptyComponent,
- /// Occurs when the leading component of a path is not in the correct format.
- InvalidLeadingComponent,
- }
- impl Display for PathError {
- fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
- match self {
- PathError::PathTooLong(length) => formatter.write_fmt(format_args!(
- "path contained {} bytes, which is over the {} byte limit",
- length,
- Path::BYTE_LIMIT)
- ),
- PathError::Empty => formatter.write_str("path was empty"),
- PathError::EmptyComponent => formatter.write_str("component of path was empty"),
- PathError::InvalidLeadingComponent
- => formatter.write_str("invalid leading path component")
- }
- }
- }
- /// 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)]
- struct Epoch(u64);
- impl Epoch {
- /// Returns the current epoch time.
- 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())
- }
- }
- impl Copy for Epoch {}
- 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)]
- struct FragmentSerial(u32);
- fn main() {
- println!("Hello, world!");
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- use test_helpers::*;
- fn path_from_str_test_case(
- expected: Result<Path, PathError>, input: &str
- ) -> Result<(), PathError> {
- let result = Path::try_from(input);
- assert_eq!(expected, result);
- Ok(())
- }
- #[test]
- fn path_from_str_multiple_components_ok() -> Result<(), PathError> {
- let expected = make_path(vec!["red", "green", "blue"]);
- let input = format!("{}/red/green/blue", expected.owner.0);
- path_from_str_test_case(Ok(expected), input.as_str())?;
- Ok(())
- }
- #[test]
- fn path_from_str_one_component_ok() -> Result<(), PathError> {
- let expected = make_path(vec![]);
- let input = expected.owner.0.to_string();
- path_from_str_test_case(Ok(expected), input.as_str())?;
- Ok(())
- }
- #[test]
- fn path_from_str_trailing_slash_ok() -> Result<(), PathError> {
- // Notice the empty component at the end of this path due to the trailing slash.
- let expected = make_path(vec!["orange", "banana", "shotgun", ""]);
- let input = format!("{}/orange/banana/shotgun/", expected.owner.0);
- path_from_str_test_case(Ok(expected), input.as_str())?;
- Ok(())
- }
- #[test]
- fn path_from_str_path_too_long_fail() -> Result<(), PathError> {
- let principal = make_principal();
- let input = format!("{}/{}", principal.0, "*".repeat(4097));
- let expected = Err(PathError::PathTooLong(input.len()));
- path_from_str_test_case(expected, input.as_str())?;
- Ok(())
- }
- #[test]
- fn path_from_str_multiple_slashes_fail() -> Result<(), PathError> {
- let expected = Err(PathError::EmptyComponent);
- let input = format!("{}//orange", make_principal().0);
- path_from_str_test_case(expected, input.as_str())?;
- Ok(())
- }
- #[test]
- fn path_from_str_leading_slash_fail() -> Result<(), PathError> {
- let expected = Err(PathError::EmptyComponent);
- let input = format!("/{}/orange/banana/shotgun", make_principal().0);
- path_from_str_test_case(expected, input.as_str())?;
- Ok(())
- }
- #[test]
- fn path_round_trip() -> Result<(), PathError> {
- let expected = make_path(vec!["interstitial", "inter-related", "intersections"]);
- let actual = Path::try_from(expected.to_string().as_str())?;
- assert_eq!(expected, actual);
- Ok(())
- }
- #[test]
- fn path_contains_true() {
- let larger = make_path(vec!["apps"]);
- let smaller = make_path(vec!["apps", "bohdi"]);
- assert!(larger.contains(&smaller));
- }
- #[test]
- fn path_contains_true_only_owner() {
- let larger = make_path(vec![]);
- let smaller = make_path(vec![]);
- assert!(larger.contains(&smaller));
- }
- #[test]
- fn path_contains_false_self_is_longer() {
- let first = make_path(vec!["apps", "bohdi"]);
- let second = make_path(vec!["apps"]);
- assert!(!first.contains(&second));
- }
- #[test]
- fn path_contains_false_same_owners() {
- let first = make_path(vec!["apps"]);
- let second = make_path(vec!["nodes"]);
- assert!(!first.contains(&second));
- }
- #[test]
- fn path_contains_false_different_owners() {
- let first = make_path(vec!["apps"]);
- let mut second = make_path(vec!["apps"]);
- second.owner = Principal(Hash::Sha2_256(PRINCIPAL2));
- assert!(!first.contains(&second));
- }
- }
|