Skip to content

Commit 458b4bb

Browse files
fkondejonesk
authored andcommitted
[frontend] Add Blake2s circuit
1 parent e6b2f3c commit 458b4bb

File tree

9 files changed

+2223
-1
lines changed

9 files changed

+2223
-1
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ jobs:
144144
strategy:
145145
fail-fast: false
146146
matrix:
147-
example: [ethsign, zklogin, sha256, sha512, keccak]
147+
example: [ethsign, zklogin, sha256, sha512, keccak, blake2s]
148148
steps:
149149
- name: Checkout Repository
150150
uses: actions/checkout@v4

crates/examples/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ ethsign = "0.9.0"
2121
tiny-keccak = "2.0.2"
2222
rand.workspace = true
2323
base64.workspace = true
24+
blake2.workspace = true
2425
jwt-simple.workspace = true
2526
sha2.workspace = true
2627

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use anyhow::{Result, ensure};
2+
use binius_examples::{Cli, ExampleCircuit};
3+
use binius_frontend::{
4+
circuits::blake2s::Blake2s,
5+
compiler::{CircuitBuilder, circuit::WitnessFiller},
6+
};
7+
use blake2::{Blake2s256, Digest};
8+
use clap::Args;
9+
use rand::prelude::*;
10+
11+
/// Blake2s circuit example demonstrating the Blake2s hash function implementation
12+
struct Blake2sExample {
13+
blake2s_gadget: Blake2s,
14+
}
15+
16+
/// Circuit parameters that affect structure (compile-time configuration)
17+
#[derive(Args, Debug)]
18+
struct Params {
19+
/// Maximum message length in bytes that the circuit can handle.
20+
#[arg(long, default_value_t = 128)]
21+
max_bytes: usize,
22+
}
23+
24+
/// Instance data for witness population (runtime values)
25+
#[derive(Args, Debug)]
26+
#[group(multiple = false)]
27+
struct Instance {
28+
/// Length of the randomly generated message, in bytes (defaults to half of --max-message-len).
29+
#[arg(long)]
30+
message_len: Option<usize>,
31+
32+
/// UTF-8 string to hash (if not provided, random bytes are generated)
33+
#[arg(long)]
34+
message_string: Option<String>,
35+
}
36+
37+
impl ExampleCircuit for Blake2sExample {
38+
type Params = Params;
39+
type Instance = Instance;
40+
41+
fn build(params: Params, builder: &mut CircuitBuilder) -> Result<Self> {
42+
// Create the Blake2s gadget with witness wires
43+
let blake2s_gadget = Blake2s::new_witness(builder, params.max_bytes);
44+
45+
Ok(Self { blake2s_gadget })
46+
}
47+
48+
fn populate_witness(&self, instance: Instance, w: &mut WitnessFiller) -> Result<()> {
49+
// Determine the message bytes to hash
50+
let message_bytes = if let Some(message_string) = instance.message_string {
51+
// Use provided UTF-8 string
52+
message_string.as_bytes().to_vec()
53+
} else {
54+
// Generate random bytes
55+
let mut rng = StdRng::seed_from_u64(0);
56+
let len = instance
57+
.message_len
58+
.unwrap_or(self.blake2s_gadget.max_bytes / 2);
59+
60+
let mut message_bytes = vec![0u8; len];
61+
rng.fill_bytes(&mut message_bytes);
62+
message_bytes
63+
};
64+
65+
// Validate message length
66+
ensure!(
67+
message_bytes.len() <= self.blake2s_gadget.max_bytes,
68+
"Message length ({}) exceeds maximum ({})",
69+
message_bytes.len(),
70+
self.blake2s_gadget.max_bytes
71+
);
72+
73+
// Compute the expected Blake2s digest using the reference implementation
74+
let mut hasher = Blake2s256::new();
75+
hasher.update(&message_bytes);
76+
let digest = hasher.finalize();
77+
let digest_array: [u8; 32] = digest.into();
78+
79+
// Populate the witness values
80+
self.blake2s_gadget.populate_message(w, &message_bytes);
81+
self.blake2s_gadget.populate_digest(w, &digest_array);
82+
83+
Ok(())
84+
}
85+
}
86+
87+
fn main() -> Result<()> {
88+
let _tracing_guard = tracing_profile::init_tracing()?;
89+
90+
// Create and run the CLI
91+
Cli::<Blake2sExample>::new("blake2s")
92+
.about("Blake2s hash function circuit example")
93+
.long_about(
94+
"Blake2s cryptographic hash function circuit implementation.\n\
95+
\n\
96+
This example demonstrates the Blake2s hash function which produces \
97+
32-byte digests. Blake2s is optimized for 32-bit platforms and is \
98+
faster than Blake2b on such architectures.\n\
99+
\n\
100+
The circuit supports variable-length messages up to the specified \
101+
maximum length. It implements the full Blake2s algorithm as \
102+
specified in RFC 7693, including 10 rounds of the compression \
103+
function.\n\
104+
\n\
105+
Examples:\n\
106+
\n\
107+
Hash a string:\n\
108+
cargo run --release --example blake2s -- --message-string \"Hello, World!\"\n\
109+
\n\
110+
Generate and hash random data (64 bytes):\n\
111+
cargo run --release --example blake2s -- --message-len 64\n\
112+
\n\
113+
Test with maximum message length:\n\
114+
cargo run --release --example blake2s -- --max-message-len 256 --message-len 256",
115+
)
116+
.run()
117+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
blake2s circuit
2+
--
3+
Number of gates: 2937
4+
Number of evaluation instructions: 3068
5+
Number of AND constraints: 3897
6+
Number of MUL constraints: 0
7+
Length of value vec: 8192
8+
Constants: 140
9+
Inout: 0
10+
Witness: 137
11+
Internal: 3761
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//! Blake2s constants
2+
//!
3+
//! This module contains the constants used in the Blake2s hash function,
4+
//! including initialization vectors and the message permutation schedule.
5+
6+
/// Blake2s initialization vectors derived from the fractional parts of
7+
/// square roots of the first 8 prime numbers (2, 3, 5, 7, 11, 13, 17, 19).
8+
///
9+
/// These values are specified in RFC 7693 Section 2.6 and provide the
10+
/// initial state for the hash computation. They ensure that even empty
11+
/// messages produce non-zero hashes.
12+
pub const IV: [u32; 8] = [
13+
0x6A09E667, // Frac(sqrt(2)) - first 32 bits
14+
0xBB67AE85, // Frac(sqrt(3))
15+
0x3C6EF372, // Frac(sqrt(5))
16+
0xA54FF53A, // Frac(sqrt(7))
17+
0x510E527F, // Frac(sqrt(11))
18+
0x9B05688C, // Frac(sqrt(13))
19+
0x1F83D9AB, // Frac(sqrt(17))
20+
0x5BE0CD19, // Frac(sqrt(19))
21+
];
22+
23+
/// SIGMA permutation schedule for message word selection.
24+
///
25+
/// Defines which message words are used in each G-function call during
26+
/// the 10 rounds of compression. This permutation ensures that all message
27+
/// words influence the output multiple times in different combinations,
28+
/// providing cryptographic diffusion as specified in RFC 7693 Section 3.1.
29+
///
30+
/// Each row represents one round, with 16 indices selecting message words
31+
/// for the 8 G-function calls (2 words per G-function).
32+
pub const SIGMA: [[usize; 16]; 10] = [
33+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // Round 0
34+
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], // Round 1
35+
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], // Round 2
36+
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], // Round 3
37+
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], // Round 4
38+
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], // Round 5
39+
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], // Round 6
40+
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], // Round 7
41+
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], // Round 8
42+
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], // Round 9
43+
];

0 commit comments

Comments
 (0)