Skip to content

Commit 410726c

Browse files
committed
feat: Align the implementation with the original rust implementation
1 parent 63fa674 commit 410726c

File tree

10 files changed

+811
-182
lines changed

10 files changed

+811
-182
lines changed

README.md

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![Zig](https://img.shields.io/badge/zig-0.14.1-orange.svg)](https://ziglang.org/)
55
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
66

7-
A pure Zig implementation of hash-based signatures using **Poseidon2** and **SHA3** with incomparable encodings. This library implements XMSS-like signatures based on the framework from [this paper](https://eprint.iacr.org/2025/055.pdf), with parameters inspired by the [hypercube-hashsig-parameters](https://github.com/b-wagn/hypercube-hashsig-parameters) project. Poseidon2 here targets the KoalaBear 31‑bit field with Montgomery arithmetic (compatible with plonky3 constants), optimized for throughput.
7+
A pure Zig implementation of hash-based signatures using **Poseidon2** and **SHA3** with incomparable encodings. This library implements XMSS-like signatures based on the framework from [this paper](https://eprint.iacr.org/2025/055.pdf), with parameters matching the [hash-sig](https://github.com/b-wagn/hash-sig) Rust implementation. Poseidon2 here targets the KoalaBear 31‑bit field with Montgomery arithmetic (compatible with plonky3 constants), optimized for throughput.
88

99
## 🌟 Features
1010

@@ -13,7 +13,7 @@ A pure Zig implementation of hash-based signatures using **Poseidon2** and **SHA
1313
- **SHA3 Hash Function**: NIST-standardized cryptographic hash (SHA3-256)
1414
- **128-bit Security**: Focused on a single, well-tested security level
1515
- **Flexible Key Lifetimes**: Support from 2^10 to 2^32 signatures per keypair
16-
- **Optimized Parameters**: 64 chains of length 8 based on [hypercube-hashsig-parameters](https://github.com/b-wagn/hypercube-hashsig-parameters)
16+
- **Rust-Compatible Parameters**: 22 chains of length 256 (w=8) matching [hash-sig](https://github.com/b-wagn/hash-sig) implementation
1717
- **Binary Encoding**: Incomparable binary encoding scheme
1818
- **Pure Zig**: Minimal dependencies, fully type-safe
1919
- **Well-Tested**: Comprehensive unit and integration tests
@@ -23,6 +23,7 @@ A pure Zig implementation of hash-based signatures using **Poseidon2** and **SHA
2323

2424
- [Installation](#installation)
2525
- [Quick Start](#quick-start)
26+
- [Programs](#programs)
2627
- [Usage](#usage)
2728
- [Configuration](#configuration)
2829
- [Architecture](#architecture)
@@ -108,6 +109,49 @@ std.debug.print("Signature valid: {}\n", .{is_valid});
108109
}
109110
```
110111

112+
## 🛠️ Programs
113+
114+
The hash-zig library includes several built-in programs for demonstration, testing, and performance analysis:
115+
116+
### Basic Example (`hash-zig-example`)
117+
**Purpose**: Demonstrates basic usage of the hash-zig library
118+
**Command**: `zig build example` or `zig build run`
119+
**Description**: Shows how to generate keypairs, sign messages, and verify signatures. Includes timing measurements and displays key information. Perfect for understanding the library's core functionality.
120+
121+
### Performance Profiler (`hash-zig-profile`)
122+
**Purpose**: Detailed performance analysis and profiling
123+
**Command**: `zig build profile`
124+
**Description**: Provides in-depth timing analysis of individual operations including WOTS (Winternitz One-Time Signature) operations, hash functions, and full key generation. Useful for understanding performance bottlenecks and optimization opportunities.
125+
126+
### Performance Benchmark (`hash-zig-benchmark`)
127+
**Purpose**: Comprehensive performance benchmarking
128+
**Command**: `zig build benchmark`
129+
**Description**: Runs standardized performance tests across different key lifetimes (2^10 and 2^16). Measures key generation, signing, and verification times with detailed metrics. Outputs results in CI-friendly format for automated testing.
130+
131+
### SIMD Benchmark (`hash-zig-simd-benchmark`)
132+
**Purpose**: Tests SIMD-optimized implementations
133+
**Command**: `zig build simd-benchmark`
134+
**Description**: Benchmarks SIMD-optimized versions of the hash-based signature scheme. Tests both 2^10 and 2^16 lifetimes with SIMD acceleration. Useful for comparing performance improvements from vectorization.
135+
136+
### Building All Programs
137+
```bash
138+
# Build all executables
139+
zig build
140+
141+
# Run specific programs
142+
zig build example # Basic usage demo
143+
zig build profile # Performance profiling
144+
zig build benchmark # Standard benchmark
145+
zig build simd-benchmark # SIMD benchmark
146+
```
147+
148+
### Program Outputs
149+
All programs provide detailed timing information and can be used for:
150+
- **Development**: Understanding library behavior and performance characteristics
151+
- **Testing**: Verifying correct implementation and performance expectations
152+
- **Benchmarking**: Comparing different implementations and optimizations
153+
- **CI/CD**: Automated performance regression testing
154+
111155
## 📖 Usage
112156

113157
### Basic Signing and Verification
@@ -116,7 +160,7 @@ std.debug.print("Signature valid: {}\n", .{is_valid});
116160
const hash_zig = @import("hash-zig");
117161
118162
// Configure parameters with Poseidon2 (default)
119-
// 128-bit security with 64 chains of length 8
163+
// 128-bit security with 22 chains of length 256 (w=8)
120164
const params = hash_zig.Parameters.init(.lifetime_2_16);
121165
var sig = try hash_zig.HashSignature.init(allocator, params);
122166
defer sig.deinit();
@@ -207,17 +251,18 @@ const params_extreme = hash_zig.Parameters.init(.lifetime_2_32);
207251

208252
### Security Parameters
209253

210-
Based on [hypercube-hashsig-parameters](https://github.com/b-wagn/hypercube-hashsig-parameters):
254+
Matching [hash-sig](https://github.com/b-wagn/hash-sig) Rust implementation:
211255

212256
| Parameter | Value | Notes |
213257
|-----------|-------|-------|
214258
| Security Level | 128-bit | Post-quantum secure |
215259
| Hash Output | 32 bytes | 256-bit hash for 128-bit security |
216260
| Encoding | Binary | Incomparable binary encoding |
217-
| Winternitz w | 8 | Chain length (as per hypercube-hashsig) |
218-
| Number of Chains | 64 | Optimized for 128-bit security |
261+
| Winternitz w | 8 | Chain length (matching Rust) |
262+
| Number of Chains | 22 | Matching Rust implementation |
263+
| Chain Length | 256 | Winternitz parameter w=8 |
219264

220-
**Note**: The [hypercube-hashsig-parameters](https://github.com/b-wagn/hypercube-hashsig-parameters) repository recommends "**48 chains of length 10**" or "**64 chains of length 8**". We use **64 chains of length 8**.
265+
**Note**: These parameters match the `SIGWinternitzLifetime18W8` instantiation from the [hash-sig](https://github.com/b-wagn/hash-sig) repository, ensuring compatibility between Rust and Zig implementations.
221266

222267
### Hash Functions
223268

@@ -253,7 +298,8 @@ hash-zig/
253298
│ │ │ ├── generic_montgomery.zig # Generic 31-bit Montgomery arithmetic
254299
│ │ │ └── koalabear/montgomery.zig # KoalaBear field instance
255300
│ │ ├── instances/koalabear16.zig # Width-16 Poseidon2 instance (plonky3 constants)
256-
│ │ └── poseidon2.zig # Core Poseidon2 permutation (Montgomery)
301+
│ │ ├── poseidon2.zig # Rust-compatible Poseidon2 implementation
302+
│ │ └── generic_poseidon2.zig # Generic Poseidon2 constructor (Montgomery)
257303
│ ├── sha3.zig # SHA3 hash implementation
258304
│ ├── encoding.zig # Incomparable encodings
259305
│ ├── tweakable_hash.zig # Domain-separated hashing
@@ -271,7 +317,7 @@ hash-zig/
271317

272318
- **Poseidon2**: Arithmetic hash over KoalaBear (31‑bit) with Montgomery reduction; constants match plonky3
273319
- **SHA3-256**: NIST-standardized Keccak-based hash for general-purpose cryptography
274-
- **Winternitz OTS**: One-time signature with 64 chains of length 8 (w=8)
320+
- **Winternitz OTS**: One-time signature with 22 chains of length 256 (w=8)
275321
- **Merkle Tree**: Binary tree implementation for managing OTS public keys
276322
- **Binary Encoding**: Incomparable binary encoding for 128-bit security
277323
- **Parallel Key Generation**: Multi-threaded with atomic job queues
@@ -319,7 +365,7 @@ Measured on **Apple M2** with Zig 0.14.1, using **Poseidon2** hash and **level_1
319365
| Verify | **99 ms** | Fast (only processes auth path) |
320366

321367
**Performance Notes:**
322-
- Using **w=8** (64 chains of length 8) per [hypercube-hashsig-parameters](https://github.com/b-wagn/hypercube-hashsig-parameters)
368+
- Using **w=8** (22 chains of length 256) matching [hash-sig](https://github.com/b-wagn/hash-sig) implementation
323369
- **Optimized with inline hints** for field arithmetic operations (~23% improvement)
324370
- Parallel key generation uses all available CPU cores automatically
325371
- Falls back to sequential mode for small workloads (< 64 leaves)

src/optimized_hash_v2.zig

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//! Optimized hash implementation for Rust-compatible signatures
2+
//! Version 2 - focuses on performance without caching complexity
3+
4+
const std = @import("std");
5+
const params = @import("params.zig");
6+
const rust_poseidon2 = @import("poseidon2/poseidon2.zig").Poseidon2;
7+
const Parameters = params.Parameters;
8+
const Allocator = std.mem.Allocator;
9+
10+
/// Optimized hash implementation without caching for simplicity
11+
pub const OptimizedHashV2 = struct {
12+
params: Parameters,
13+
allocator: Allocator,
14+
15+
pub fn init(allocator: Allocator, parameters: Parameters) !OptimizedHashV2 {
16+
return .{
17+
.params = parameters,
18+
.allocator = allocator,
19+
};
20+
}
21+
22+
pub fn deinit(self: *OptimizedHashV2) void {
23+
_ = self;
24+
}
25+
26+
pub fn hash(self: *OptimizedHashV2, allocator: Allocator, data: []const u8, tweak: u64) ![]u8 {
27+
_ = self;
28+
29+
// Create tweaked input: tweak (8 bytes) + data
30+
var tweaked_data = try allocator.alloc(u8, 8 + data.len);
31+
defer allocator.free(tweaked_data);
32+
33+
std.mem.writeInt(u64, tweaked_data[0..8], tweak, .big);
34+
@memcpy(tweaked_data[8..], data);
35+
36+
// Use Rust-compatible Poseidon2
37+
const hash_result = rust_poseidon2.hash(tweaked_data);
38+
39+
// Return a copy
40+
const result = try allocator.dupe(u8, &hash_result);
41+
return result;
42+
}
43+
44+
pub fn prfHash(self: *OptimizedHashV2, allocator: Allocator, key: []const u8, index: u64) ![]u8 {
45+
return self.hash(allocator, key, index);
46+
}
47+
};

0 commit comments

Comments
 (0)