Merge pull request #544 from zcash/451-f4jumble-docs
Add documentation to `f4jumble` crate
This commit is contained in:
commit
6c22d41a31
|
@ -13,10 +13,15 @@ readme = "README.md"
|
|||
license = "MIT OR Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
blake2b_simd = { version = "1", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4"
|
||||
proptest = "1"
|
||||
|
||||
[features]
|
||||
|
|
|
@ -1,4 +1,50 @@
|
|||
//! This crate provides a mechanism for "jumbling" byte slices in a reversible way.
|
||||
//!
|
||||
//! Many byte encodings such as [Base64] and [Bech32] do not have "cascading" behaviour:
|
||||
//! changing an input byte at one position has no effect on the encoding of bytes at
|
||||
//! distant positions. This can be a problem if users generally check the correctness of
|
||||
//! encoded strings by eye, as they will tend to only check the first and/or last few
|
||||
//! characters of the encoded string. In some situations (for example, a hardware device
|
||||
//! displaying on its screen an encoded string provided by an untrusted computer), it is
|
||||
//! potentially feasible for an adversary to change some internal portion of the encoded
|
||||
//! string in a way that is beneficial to them, without the user noticing.
|
||||
//!
|
||||
//! [Base64]: https://en.wikipedia.org/wiki/Base64
|
||||
//! [Bech32]: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#Bech32
|
||||
//!
|
||||
//! The function F4Jumble (and its inverse function, F4Jumble⁻¹) are length-preserving
|
||||
//! transformations can be used to trivially introduce cascading behaviour to existing
|
||||
//! encodings:
|
||||
//! - Prepare the raw `message` bytes as usual.
|
||||
//! - Pass `message` through [`f4jumble`] or [`f4jumble_mut`] to obtain the jumbled bytes.
|
||||
//! - Encode the jumbled bytes with the encoding scheme.
|
||||
//!
|
||||
//! Changing any byte of `message` will result in a completely different sequence of
|
||||
//! jumbled bytes. Specifically, F4Jumble uses an unkeyed 4-round Feistel construction to
|
||||
//! approximate a random permutation.
|
||||
//!
|
||||
//! ![Diagram of 4-round unkeyed Feistel construction](https://zips.z.cash/zip-0316-f4.png)
|
||||
//!
|
||||
//! ## Efficiency
|
||||
//!
|
||||
//! The cost is dominated by 4 BLAKE2b compressions for message lengths up to 128 bytes.
|
||||
//! For longer messages, the cost increases to 6 BLAKE2b compressions for 128 < lₘ ≤ 192,
|
||||
//! and 10 BLAKE2b compressions for 192 < lₘ ≤ 256, for example. The maximum cost for
|
||||
//! which the algorithm is defined would be 196608 BLAKE2b compressions at lₘ = 4194368.
|
||||
//!
|
||||
//! The implementations in this crate require memory of roughly lₘ bytes plus the size of
|
||||
//! a BLAKE2b hash state. It is possible to reduce this by (for example, with F4Jumble⁻¹)
|
||||
//! streaming the `d` part of the jumbled encoding three times from a less
|
||||
//! memory-constrained device. It is essential that the streamed value of `d` is the same
|
||||
//! on each pass, which can be verified using a Message Authentication Code (with key held
|
||||
//! only by the Consumer) or collision-resistant hash function. After the first pass of
|
||||
//! `d`, the implementation is able to compute `y`; after the second pass it is able to
|
||||
//! compute `a`; and the third allows it to compute and incrementally parse `b`. The
|
||||
//! maximum memory usage during this process would be 128 bytes plus two BLAKE2b hash
|
||||
//! states.
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
use blake2b_simd::{Params as Blake2bParams, OUTBYTES};
|
||||
|
||||
|
@ -136,6 +182,28 @@ fn ceildiv(num: usize, den: usize) -> usize {
|
|||
(num + den - 1) / den
|
||||
}
|
||||
|
||||
/// Encodes the given message in-place using F4Jumble.
|
||||
///
|
||||
/// Returns an error if the message is an invalid length. `message` will be unmodified in
|
||||
/// this case.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let mut message_a = *b"The package from Alice arrives tomorrow morning.";
|
||||
/// f4jumble::f4jumble_mut(&mut message_a[..]).unwrap();
|
||||
/// assert_eq!(
|
||||
/// hex::encode(message_a),
|
||||
/// "861c51ee746b0313476967a3483e7e1ff77a2952a17d3ed9e0ab0f502e1179430322da9967b613545b1c36353046ca27",
|
||||
/// );
|
||||
///
|
||||
/// let mut message_b = *b"The package from Sarah arrives tomorrow morning.";
|
||||
/// f4jumble::f4jumble_mut(&mut message_b[..]).unwrap();
|
||||
/// assert_eq!(
|
||||
/// hex::encode(message_b),
|
||||
/// "af1d55f2695aea02440867bbbfae3b08e8da55b625de3fa91432ab7b2c0a7dff9033ee666db1513ba5761ef482919fb8",
|
||||
/// );
|
||||
/// ```
|
||||
pub fn f4jumble_mut(message: &mut [u8]) -> Result<(), Error> {
|
||||
if VALID_LENGTH.contains(&message.len()) {
|
||||
State::new(message).apply_f4jumble();
|
||||
|
@ -145,6 +213,26 @@ pub fn f4jumble_mut(message: &mut [u8]) -> Result<(), Error> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Decodes the given message in-place using F4Jumble⁻¹.
|
||||
///
|
||||
/// Returns an error if the message is an invalid length. `message` will be unmodified in
|
||||
/// this case.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let mut message_a = hex::decode(
|
||||
/// "861c51ee746b0313476967a3483e7e1ff77a2952a17d3ed9e0ab0f502e1179430322da9967b613545b1c36353046ca27")
|
||||
/// .unwrap();
|
||||
/// f4jumble::f4jumble_inv_mut(&mut message_a).unwrap();
|
||||
/// assert_eq!(message_a, b"The package from Alice arrives tomorrow morning.");
|
||||
///
|
||||
/// let mut message_b = hex::decode(
|
||||
/// "af1d55f2695aea02440867bbbfae3b08e8da55b625de3fa91432ab7b2c0a7dff9033ee666db1513ba5761ef482919fb8")
|
||||
/// .unwrap();
|
||||
/// f4jumble::f4jumble_inv_mut(&mut message_b).unwrap();
|
||||
/// assert_eq!(message_b, b"The package from Sarah arrives tomorrow morning.");
|
||||
/// ```
|
||||
pub fn f4jumble_inv_mut(message: &mut [u8]) -> Result<(), Error> {
|
||||
if VALID_LENGTH.contains(&message.len()) {
|
||||
State::new(message).apply_f4jumble_inv();
|
||||
|
@ -154,13 +242,57 @@ pub fn f4jumble_inv_mut(message: &mut [u8]) -> Result<(), Error> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Encodes the given message using F4Jumble, and returns the encoded message as a vector
|
||||
/// of bytes.
|
||||
///
|
||||
/// Returns an error if the message is an invalid length.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let message_a = b"The package from Alice arrives tomorrow morning.";
|
||||
/// let encoded_a = f4jumble::f4jumble(message_a).unwrap();
|
||||
/// assert_eq!(
|
||||
/// hex::encode(encoded_a),
|
||||
/// "861c51ee746b0313476967a3483e7e1ff77a2952a17d3ed9e0ab0f502e1179430322da9967b613545b1c36353046ca27",
|
||||
/// );
|
||||
///
|
||||
/// let message_b = b"The package from Sarah arrives tomorrow morning.";
|
||||
/// let encoded_b = f4jumble::f4jumble(message_b).unwrap();
|
||||
/// assert_eq!(
|
||||
/// hex::encode(encoded_b),
|
||||
/// "af1d55f2695aea02440867bbbfae3b08e8da55b625de3fa91432ab7b2c0a7dff9033ee666db1513ba5761ef482919fb8",
|
||||
/// );
|
||||
/// ```
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||
pub fn f4jumble(message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let mut result = message.to_vec();
|
||||
f4jumble_mut(&mut result).map(|()| result)
|
||||
}
|
||||
|
||||
/// Decodes the given message using F4Jumble⁻¹, and returns the decoded message as a
|
||||
/// vector of bytes.
|
||||
///
|
||||
/// Returns an error if the message is an invalid length.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let encoded_a = hex::decode(
|
||||
/// "861c51ee746b0313476967a3483e7e1ff77a2952a17d3ed9e0ab0f502e1179430322da9967b613545b1c36353046ca27")
|
||||
/// .unwrap();
|
||||
/// let message_a = f4jumble::f4jumble_inv(&encoded_a).unwrap();
|
||||
/// assert_eq!(message_a, b"The package from Alice arrives tomorrow morning.");
|
||||
///
|
||||
/// let encoded_b = hex::decode(
|
||||
/// "af1d55f2695aea02440867bbbfae3b08e8da55b625de3fa91432ab7b2c0a7dff9033ee666db1513ba5761ef482919fb8")
|
||||
/// .unwrap();
|
||||
/// let message_b = f4jumble::f4jumble_inv(&encoded_b).unwrap();
|
||||
/// assert_eq!(message_b, b"The package from Sarah arrives tomorrow morning.");
|
||||
/// ```
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||
pub fn f4jumble_inv(message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let mut result = message.to_vec();
|
||||
f4jumble_inv_mut(&mut result).map(|()| result)
|
||||
|
|
Loading…
Reference in New Issue