blockblaz/hash-zig
A pure zig implementation of hash based signatures inspired from the rust implementation https://github.com/b-wagn/hash-sig
refsA pure Zig implementation of Generalized XMSS hash-based signatures with 100% Rust compatibility. This library implements the complete GeneralizedXMSS signature scheme based on the framework from this paper, matching the hash-sig Rust implementation exactly.
โ ๏ธ Prototype Status: This is a prototype implementation for research and development purposes. Use at your own risk.
Add to your build.zig.zon:
.{
.name = "my_project",
.version = "0.1.0",
.dependencies = .{
.@"hash-zig" = .{
.url = "https://github.com/ch4r10t33r/hash-zig/archive/refs/tags/v2.0.0.tar.gz",
.hash = "1220...", // Will be generated by zig build
},
.@"zig-poseidon" = .{
.url = "https://github.com/blockblaz/zig-poseidon/archive/refs/heads/main.tar.gz",
.hash = "1220...", // Will be generated by zig build
},
},
}
In your build.zig:
const hash_zig_dep = b.dependency("hash-zig", .{
.target = target,
.optimize = optimize,
});
const zig_poseidon_dep = b.dependency("zig_poseidon", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("hash-zig", hash_zig_dep.module("hash-zig"));
exe.root_module.addImport("poseidon", zig_poseidon_dep.module("poseidon"));
git clone https://github.com/ch4r10t33r/hash-zig.git
cd hash-zig
zig build test
const std = @import("std");
const hash_zig = @import("hash-zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Initialize the GeneralizedXMSS signature scheme
var scheme = try hash_zig.GeneralizedXMSSSignatureScheme.init(allocator, .lifetime_2_8);
defer scheme.deinit();
// Generate a keypair with activation parameters
const keypair = try scheme.keyGen(0, 256); // activation_epoch=0, num_active_epochs=256
defer keypair.secret_key.deinit();
// Sign a message
const message = [_]u8{0x42} ** 32;
const signature = try scheme.sign(keypair.secret_key, 0, message);
defer signature.deinit();
// Verify the signature
const is_valid = try scheme.verify(&keypair.public_key, 0, message, signature);
std.debug.print("Signature valid: {}\n", .{is_valid});
}
๐ก Performance Tip: For testing with larger lifetimes (2^18, 2^32), build with zig build -Doptimize=ReleaseFast for significantly better performance. Remember this is prototype software.
The main signature scheme implementation that provides:
keyGen(activation_epoch, num_active_epochs) - Generate keypairssign(secret_key, epoch, message) - Sign messagesverify(public_key, epoch, message, signature) - Verify signatures| Lifetime | Signatures | Use Case | Performance Note |
|---|---|---|---|
2^8 |
256 | Testing, short-term keys | Fast in both debug and optimized builds |
2^18 |
262,144 | Medium-term applications | Requires optimized build for reasonable performance |
2^32 |
4,294,967,296 | Long-term, high-volume | Requires optimized build for reasonable performance |
// Generate a keypair for lifetime 2^8
const keypair = try scheme.keyGen(0, 256);
// Access the public key (using controlled access methods)
const public_key = keypair.public_key;
const root = public_key.getRoot();
std.debug.print("Root: {}\n", .{root.value});
// Access the secret key (using controlled access methods)
const secret_key = keypair.secret_key;
const activation_interval = secret_key.getActivationInterval();
const prepared_interval = secret_key.getPreparedInterval(8);
// Sign a message at epoch 0
const message = [_]u8{0x42} ** 32;
const signature = try scheme.sign(secret_key, 0, message);
// Verify the signature
const is_valid = try scheme.verify(&public_key, 0, message, signature);
// Access signature components (using controlled access methods)
const path = signature.getPath();
const rho = signature.getRho();
const hashes = signature.getHashes();
// Check if key is active for a given epoch
const activation_interval = secret_key.getActivationInterval();
if (activation_interval.contains(epoch)) {
// Key is active for this epoch
}
// Check if key is prepared for a given epoch
const prepared_interval = secret_key.getPreparedInterval(log_lifetime);
if (prepared_interval.contains(epoch)) {
// Key is prepared for this epoch
}
// Advance key preparation (when needed)
try secret_key.advancePreparation(log_lifetime);
# Run comprehensive test suite
zig build test
# Run only Rust compatibility tests
zig build test-rust-compat
# Run specific compatibility tests
zig build test-generalized-xmss-compat
zig build test-shake-prf-compat
zig build test-poseidon2-compat
โ ๏ธ Important: This is a prototype implementation. For larger lifetimes (2^18, 2^32), always use optimized builds, but be aware this is experimental software.
# Optimized build (for testing only - this is prototype software)
zig build -Doptimize=ReleaseFast
# Debug build (for development only)
zig build
# Run performance benchmarks (prototype software - use at your own risk)
zig build benchmark -Doptimize=ReleaseFast
# Run key generation benchmarks
zig build benchmark-keygen -Doptimize=ReleaseFast
The standalone key-generation benchmark compares lifetime configurations while always generating 256 keys for each:
# Debug build (slower, good for development)
zig run scripts/benchmark_keygen.zig -- -i3
# Include lifetime 2^32 as well (can be slower due to larger trees)
zig run scripts/benchmark_keygen.zig -- --include-2-32 -i3
# Recommended for accurate results (prototype software)
zig run scripts/benchmark_keygen.zig -- -i5 -Doptimize=ReleaseFast
What it measures:
Example output (abbreviated):
hash-zig Key Generation Benchmark (Multiple Lifetimes)
=======================================================
Iterations per configuration: 3
Include 2^32 lifetime: true
Note: All tests generate 256 keys to compare lifetime performance
Benchmarking lifetime 2^8 (generating 256 keys)
... โ
0.01s | ... โ
0.02s | ... โ
0.01s
๐ Results for lifetime 2^8 (256 keys):
Average time: 0.01 seconds
Generation rate: 17,000+ keys/second
Benchmarking lifetime 2^18 (generating 256 keys)
... โ
0.05s | ... โ
0.05s | ... โ
0.05s
๐ Results for lifetime 2^18 (256 keys):
Average time: 0.05 seconds
Generation rate: ~5,000 keys/second
Benchmarking lifetime 2^32 (generating 256 keys)
... โ
0.21s | ... โ
0.21s | ... โ
0.21s
๐ Results for lifetime 2^32 (256 keys):
Average time: 0.21 seconds
Generation rate: ~1,200 keys/second
Notes:
-Doptimize=ReleaseFast for realistic throughput numbers.โ ๏ธ Prototype Performance (ReleaseFast):
Debug Build Performance (Prototype):
โ ๏ธ This is prototype software - use at your own risk
-Doptimize=ReleaseFast for testing deployments# Build library and examples
zig build
# Build with documentation
zig build -Ddocs
# Run linting
zig build lint
# Run basic usage example (with timing)
zig build example
# Run basic usage example (optimized)
zig build example -Doptimize=ReleaseFast
src/
โโโ core/ # Core types and parameters
โโโ hash/ # Hash function implementations
โโโ prf/ # PRF implementations
โโโ signature/ # Main signature scheme
โโโ merkle/ # Merkle tree construction
โโโ wots/ # Winternitz OTS
โโโ utils/ # Utility functions
examples/
โโโ basic_usage.zig # Basic usage example with timing
โโโ test_generalized_xmss_compat.zig # Main compatibility test
โโโ test_shake_prf_compatibility.zig # PRF compatibility test
โโโ test_poseidon2_compatibility.zig # Hash function compatibility test
test/
โโโ performance_test.zig # Performance benchmarks
โโโ rust_compatibility_test.zig # Rust compatibility tests
zig build testThis project is licensed under the Apache License 2.0 - see the LICENSE file for details.
The Zig implementation has been thoroughly tested and verified to match the Rust hash-sig implementation exactly:
The implementation includes comprehensive tests that verify:
See the compatibility investigation for detailed analysis and test results.
For questions, issues, or contributions: