1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
//! [Repo](https://github.com/oconnor663/bao) —
//! [Crate](https://crates.io/crates/bao) —
//! [Spec](https://github.com/oconnor663/bao/blob/master/docs/spec.md)
//!
//! Bao is an implementation of BLAKE3 verified streaming. For more about how
//! verified streaming works and what the Bao format looks like, see the
//! [project README](https://github.com/oconnor663/bao) and the [full
//! specification](https://github.com/oconnor663/bao/blob/master/docs/spec.md).
//!
//! # Example
//!
//! ```
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use std::io::prelude::*;
//!
//! // Encode some example bytes.
//! let input = b"some input";
//! let (encoded, hash) = bao::encode::encode(input);
//!
//! // Decode them with one of the all-at-once functions.
//! let decoded_at_once = bao::decode::decode(&encoded, &hash)?;
//!
//! // Also decode them incrementally.
//! let mut decoded_incrementally = Vec::new();
//! let mut decoder = bao::decode::Decoder::new(&*encoded, &hash);
//! decoder.read_to_end(&mut decoded_incrementally)?;
//!
//! // Assert that we got the same results both times.
//! assert_eq!(decoded_at_once, decoded_incrementally);
//!
//! // Flipping a bit in encoding will cause a decoding error.
//! let mut bad_encoded = encoded.clone();
//! let last_index = bad_encoded.len() - 1;
//! bad_encoded[last_index] ^= 1;
//! let err = bao::decode::decode(&bad_encoded, &hash).unwrap_err();
//! assert_eq!(std::io::ErrorKind::InvalidData, err.kind());
//! # Ok(())
//! # }
//! ```
#![forbid(unsafe_code)]
pub mod decode;
pub mod encode;
pub use blake3::Hash;
use std::mem;
/// The size of a `Hash`, 32 bytes.
pub const HASH_SIZE: usize = 32;
pub(crate) const PARENT_SIZE: usize = 2 * HASH_SIZE;
pub(crate) const HEADER_SIZE: usize = 8;
pub(crate) const CHUNK_SIZE: usize = 1024;
pub(crate) const MAX_DEPTH: usize = 54; // 2^54 * CHUNK_SIZE = 2^64
/// An array of `HASH_SIZE` bytes. This will be a wrapper type in a future version.
pub(crate) type ParentNode = [u8; 2 * HASH_SIZE];
pub(crate) fn encode_len(len: u64) -> [u8; HEADER_SIZE] {
debug_assert_eq!(mem::size_of_val(&len), HEADER_SIZE);
len.to_le_bytes()
}
pub(crate) fn decode_len(bytes: &[u8; HEADER_SIZE]) -> u64 {
u64::from_le_bytes(*bytes)
}
// The root node is hashed differently from interior nodes. It gets suffixed
// with the length of the entire input, and we set the Blake2 final node flag.
// That means that no root hash can ever collide with an interior hash, or with
// the root of a different size tree.
#[derive(Clone, Copy, Debug)]
pub(crate) enum Finalization {
NotRoot,
Root,
}
impl Finalization {
fn is_root(self) -> bool {
match self {
Self::NotRoot => false,
Self::Root => true,
}
}
}
#[doc(hidden)]
pub mod benchmarks {
pub const CHUNK_SIZE: usize = super::CHUNK_SIZE;
}
#[cfg(test)]
pub(crate) mod test {
use super::*;
// Interesting input lengths to run tests on.
pub const TEST_CASES: &[usize] = &[
0,
1,
10,
CHUNK_SIZE - 1,
CHUNK_SIZE,
CHUNK_SIZE + 1,
2 * CHUNK_SIZE - 1,
2 * CHUNK_SIZE,
2 * CHUNK_SIZE + 1,
3 * CHUNK_SIZE - 1,
3 * CHUNK_SIZE,
3 * CHUNK_SIZE + 1,
4 * CHUNK_SIZE - 1,
4 * CHUNK_SIZE,
4 * CHUNK_SIZE + 1,
8 * CHUNK_SIZE - 1,
8 * CHUNK_SIZE,
8 * CHUNK_SIZE + 1,
16 * CHUNK_SIZE - 1,
16 * CHUNK_SIZE,
16 * CHUNK_SIZE + 1,
];
}