Skip to content

Commit

Permalink
Implement EIP-152 Blake2 compression function.
Browse files Browse the repository at this point in the history
  • Loading branch information
g-r-a-n-t authored and axic committed May 12, 2020
1 parent 834ed09 commit 710116f
Show file tree
Hide file tree
Showing 3 changed files with 337 additions and 0 deletions.
20 changes: 20 additions & 0 deletions blake2f/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "ewasm-precompile-blake2f"
version = "0.2.0"
authors = ["Grant Wuerker <[email protected]>"]
license = "Apache-2.0"
repository = "https://github.com/ewasm/ewasm-precompiles"
description = "Ethereum Blake2 compression function precompile in Rust"
publish = false
edition = "2018"

[dependencies]
byteorder = "1.3.4"
ewasm_api = "0.9"
arr_macro = "0.1.3"

[dev-dependencies]
hex = "0.3.1"

[lib]
crate-type = ["cdylib"]
129 changes: 129 additions & 0 deletions blake2f/src/f.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/// Implementation of blake2 compression function F.
///
/// This was copied from https://gist.github.com/seunlanlege/fa848401d316c52919f6e554fba6870b
/// with some modifications. It was initially written by Seun Lanlege and has no explicit license.
/// Message word schedule permutations for each round of both BLAKE2b and BLAKE2s are defined by
/// SIGMA. For BLAKE2b, the two extra permutations for rounds 10 and 11 are
/// SIGMA[10..11] = SIGMA[0..1].
///
/// https://tools.ietf.org/html/rfc7693#section-2.7
const SIGMA: [[usize; 16]; 10] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
];

/// IV[i] = floor(2**w * frac(sqrt(prime(i+1)))), where prime(i) is the i:th prime number
/// ( 2, 3, 5, 7, 11, 13, 17, 19 ) and sqrt(x) is the square root of x.
///
/// https://tools.ietf.org/html/rfc7693#section-2.6
const IV: [u64; 8] = [
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179,
];

/// Compression function F takes as an argument the state vector "h", message block vector "m"
/// (last block is padded with zeros to full block size, if required), 2w-bit offset counter "t",
/// and final block indicator flag "f". Local vector v[0..15] is used in processing. F returns a
/// new state vector. The number of rounds, "r", is 12 for BLAKE2b and 10 for BLAKE2s.
/// Rounds are numbered from 0 to r - 1.
///
/// https://tools.ietf.org/html/rfc7693#section-3.2
pub fn compress(rounds: u32, h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool) {
let mut v = Vec::new();
v.extend(h.iter().cloned());
v.extend_from_slice(&IV);

v[12] ^= t[0];
v[13] ^= t[1];

if f {
v[14] = !v[14]
}

for i in 0..rounds as usize {
let s = &SIGMA[i % 10];
v[0] = v[0].wrapping_add(v[4]).wrapping_add(m[s[0]]);
v[12] = (v[12] ^ v[0]).rotate_right(32);
v[8] = v[8].wrapping_add(v[12]);
v[4] = (v[4] ^ v[8]).rotate_right(24);
v[0] = v[0].wrapping_add(v[4]).wrapping_add(m[s[1]]);
v[12] = (v[12] ^ v[0]).rotate_right(16);
v[8] = v[8].wrapping_add(v[12]);
v[4] = (v[4] ^ v[8]).rotate_right(63);
v[1] = v[1].wrapping_add(v[5]).wrapping_add(m[s[2]]);
v[13] = (v[13] ^ v[1]).rotate_right(32);
v[9] = v[9].wrapping_add(v[13]);
v[5] = (v[5] ^ v[9]).rotate_right(24);
v[1] = v[1].wrapping_add(v[5]).wrapping_add(m[s[3]]);
v[13] = (v[13] ^ v[1]).rotate_right(16);
v[9] = v[9].wrapping_add(v[13]);
v[5] = (v[5] ^ v[9]).rotate_right(63);
v[2] = v[2].wrapping_add(v[6]).wrapping_add(m[s[4]]);
v[14] = (v[14] ^ v[2]).rotate_right(32);
v[10] = v[10].wrapping_add(v[14]);
v[6] = (v[6] ^ v[10]).rotate_right(24);
v[2] = v[2].wrapping_add(v[6]).wrapping_add(m[s[5]]);
v[14] = (v[14] ^ v[2]).rotate_right(16);
v[10] = v[10].wrapping_add(v[14]);
v[6] = (v[6] ^ v[10]).rotate_right(63);
v[3] = v[3].wrapping_add(v[7]).wrapping_add(m[s[6]]);
v[15] = (v[15] ^ v[3]).rotate_right(32);
v[11] = v[11].wrapping_add(v[15]);
v[7] = (v[7] ^ v[11]).rotate_right(24);
v[3] = v[3].wrapping_add(v[7]).wrapping_add(m[s[7]]);
v[15] = (v[15] ^ v[3]).rotate_right(16);
v[11] = v[11].wrapping_add(v[15]);
v[7] = (v[7] ^ v[11]).rotate_right(63);
v[0] = v[0].wrapping_add(v[5]).wrapping_add(m[s[8]]);
v[15] = (v[15] ^ v[0]).rotate_right(32);
v[10] = v[10].wrapping_add(v[15]);
v[5] = (v[5] ^ v[10]).rotate_right(24);
v[0] = v[0].wrapping_add(v[5]).wrapping_add(m[s[9]]);
v[15] = (v[15] ^ v[0]).rotate_right(16);
v[10] = v[10].wrapping_add(v[15]);
v[5] = (v[5] ^ v[10]).rotate_right(63);
v[1] = v[1].wrapping_add(v[6]).wrapping_add(m[s[10]]);
v[12] = (v[12] ^ v[1]).rotate_right(32);
v[11] = v[11].wrapping_add(v[12]);
v[6] = (v[6] ^ v[11]).rotate_right(24);
v[1] = v[1].wrapping_add(v[6]).wrapping_add(m[s[11]]);
v[12] = (v[12] ^ v[1]).rotate_right(16);
v[11] = v[11].wrapping_add(v[12]);
v[6] = (v[6] ^ v[11]).rotate_right(63);
v[2] = v[2].wrapping_add(v[7]).wrapping_add(m[s[12]]);
v[13] = (v[13] ^ v[2]).rotate_right(32);
v[8] = v[8].wrapping_add(v[13]);
v[7] = (v[7] ^ v[8]).rotate_right(24);
v[2] = v[2].wrapping_add(v[7]).wrapping_add(m[s[13]]);
v[13] = (v[13] ^ v[2]).rotate_right(16);
v[8] = v[8].wrapping_add(v[13]);
v[7] = (v[7] ^ v[8]).rotate_right(63);
v[3] = v[3].wrapping_add(v[4]).wrapping_add(m[s[14]]);
v[14] = (v[14] ^ v[3]).rotate_right(32);
v[9] = v[9].wrapping_add(v[14]);
v[4] = (v[4] ^ v[9]).rotate_right(24);
v[3] = v[3].wrapping_add(v[4]).wrapping_add(m[s[15]]);
v[14] = (v[14] ^ v[3]).rotate_right(16);
v[9] = v[9].wrapping_add(v[14]);
v[4] = (v[4] ^ v[9]).rotate_right(63);
}

for i in 0..8 {
h[i] ^= v[i] ^ v[i + 8];
}
}
188 changes: 188 additions & 0 deletions blake2f/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
extern crate ewasm_api;

mod f;

use arr_macro::arr;
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::Cursor;

#[cfg(not(test))]
#[no_mangle]
pub extern "C" fn main() {
match blake2_f(ewasm_api::calldata_acquire()) {
Ok((gas_used, data)) => {
ewasm_api::consume_gas(gas_used);
ewasm_api::finish_data(data.as_slice())
}
Err(err) => ewasm_api::revert_data(err.as_bytes()),
}
}

/// The Blake2F precompile written as Result returning function.
pub fn blake2_f(data: Vec<u8>) -> Result<(u64, Vec<u8>), &'static str> {
if data.len() == 213 {
let (rounds, mut h, m, t, f) = read_data(data)?;
f::compress(rounds, &mut h, m, t, f);

return Ok((rounds as u64, write_data(h)));
}

Err("input length for BLAKE2 F precompile should be exactly 213 bytes")
}

fn read_data(data: Vec<u8>) -> Result<(u32, [u64; 8], [u64; 16], [u64; 2], bool), &'static str> {
let mut reader = Cursor::new(data);

let rounds = reader
.read_u32::<BigEndian>()
.expect("Unable to read data.");
let h = arr![reader.read_u64::<LittleEndian>().expect("Unable to read data."); 8];
let m = arr![reader.read_u64::<LittleEndian>().expect("Unable to read data."); 16];
let t = arr![reader.read_u64::<LittleEndian>().expect("Unable to read data."); 2];
let f = match reader.read_u8().expect("Unable to read data.") {
0 => false,
1 => true,
_ => return Err("incorrect final block indicator flag"),
};

Ok((rounds, h, m, t, f))
}

fn write_data(h: [u64; 8]) -> Vec<u8> {
let mut data = vec![];

for i in 0..8 {
data.write_u64::<LittleEndian>(h[i])
.expect("Unable to write data.")
}

data
}

/// See test cases at https://eips.ethereum.org/EIPS/eip-152.
///
/// You will most likely need to override the default target like so:
/// Example: `cargo test --target x86_64-unknown-linux-gnu --release`
#[cfg(test)]
mod tests {
extern crate hex;

use crate::blake2_f;
use std::time::Instant;

const INPUTS: [&str; 9] = [
"",
"00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
"000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
"0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002",
"0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
"0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
"0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000",
"0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
"ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
];

const OUTPUTS: [&str; 9] = [
"",
"",
"",
"",
"08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b",
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
"75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735",
"b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421",
"fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615",
];

/// 1200 rounds
const BENCH_INPUT: &str = "000004B048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001";

#[test]
fn case0() {
assert_eq!(
blake2_f(hex::decode(INPUTS[0]).unwrap()),
Err("input length for BLAKE2 F precompile should be exactly 213 bytes"),
)
}

#[test]
fn case1() {
assert_eq!(
blake2_f(hex::decode(INPUTS[1]).unwrap()),
Err("input length for BLAKE2 F precompile should be exactly 213 bytes"),
)
}

#[test]
fn case2() {
assert_eq!(
blake2_f(hex::decode(INPUTS[2]).unwrap()),
Err("input length for BLAKE2 F precompile should be exactly 213 bytes"),
)
}

#[test]
fn case3() {
assert_eq!(
blake2_f(hex::decode(INPUTS[3]).unwrap()),
Err("incorrect final block indicator flag"),
)
}

#[test]
fn case4() {
assert_eq!(
blake2_f(hex::decode(INPUTS[4]).unwrap()),
Ok((0, hex::decode(OUTPUTS[4]).unwrap())),
)
}

#[test]
fn case5() {
assert_eq!(
blake2_f(hex::decode(INPUTS[5]).unwrap()),
Ok((12, hex::decode(OUTPUTS[5]).unwrap())),
)
}

#[test]
fn case6() {
assert_eq!(
blake2_f(hex::decode(INPUTS[6]).unwrap()),
Ok((12, hex::decode(OUTPUTS[6]).unwrap())),
)
}

#[test]
fn case7() {
assert_eq!(
blake2_f(hex::decode(INPUTS[7]).unwrap()),
Ok((1, hex::decode(OUTPUTS[7]).unwrap())),
)
}

// This test takes excessively long.
//
// #[test]
// fn case8() {
// assert_eq!(
// blake2_f(hex::decode(INPUTS[8]).unwrap()),
// Ok((4294967295, hex::decode(OUTPUTS[8]).unwrap())),
// );
// }

/// run with `-- --nocapture`
#[test]
fn benchmark() {
let n = 1000;
let mut sum = 0;

for _ in 0..n {
let now = Instant::now();
blake2_f(hex::decode(BENCH_INPUT).unwrap()).expect("Couldn't compress BENCH_INPUT.");
sum += now.elapsed().as_nanos();
}

println!("Average elapsed time for 1200 rounds: {}ns", sum / n)
}
}

0 comments on commit 710116f

Please sign in to comment.