mirror of
https://github.com/saymrwulf/proof-aware-crypto-tooling-agent.git
synced 2026-09-05 20:10:34 +00:00
- dogfood/quorum/verify-{dalek,anza,risc0,betrusted}: verify-only crates
built from the pinned proven source workspaces (serial backends pinned
per fork; anza entry is the certificate-covered verify_sha512, not the
default Zebra-lineage verify())
- src/pacta/quorum.py: unanimity-required acceptance, divergence
taxonomy (semantic-edge vs unexplained/tamper), small-order/canonicity
edge flags, per-member provenance sidecars with binary hashes
- live smoke: 4/4 members agree on accept and reject
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
78 lines
2.8 KiB
Rust
78 lines
2.8 KiB
Rust
//! warden quorum member: anza (Solana) solana-ed25519 verify path.
|
|
//!
|
|
//! Built against the PINNED proven source workspace, serial backend pinned
|
|
//! (`--cfg curve25519_serial_only`). The entry point is `verify_sha512`
|
|
//! (== `verify_dalek`) - the certificate-covered path - and deliberately
|
|
//! NOT the crate's default `verify()`, whose Zebra-lineage semantics are
|
|
//! outside this fork's proof boundary. This fork's accept() is strictly
|
|
//! stricter than upstream's: it rejects A = 0 and a legacy list of
|
|
//! excluded small-order R values, so a divergence against the dalek-family
|
|
//! members on such inputs is a documented semantic edge, not tampering.
|
|
//!
|
|
//! Usage: <pubkey-hex-32B> <sig-hex-64B> <payload-file>
|
|
//! stdout OK / INVALID; exit 0 = accept, 1 = reject, 2 = input error.
|
|
|
|
use curve25519::ed_sigs::{Signature, VerificationKey};
|
|
use std::process::ExitCode;
|
|
|
|
fn hex_decode(s: &str) -> Result<Vec<u8>, String> {
|
|
if s.len() % 2 != 0 {
|
|
return Err("odd-length hex".into());
|
|
}
|
|
(0..s.len() / 2)
|
|
.map(|i| u8::from_str_radix(&s[2 * i..2 * i + 2], 16).map_err(|e| e.to_string()))
|
|
.collect()
|
|
}
|
|
|
|
fn main() -> ExitCode {
|
|
let args: Vec<String> = std::env::args().collect();
|
|
if args.len() != 4 {
|
|
eprintln!("usage: {} <pubkey-hex> <sig-hex> <payload-file>", args[0]);
|
|
return ExitCode::from(2);
|
|
}
|
|
let pk_bytes = match hex_decode(&args[1]) {
|
|
Ok(b) if b.len() == 32 => b,
|
|
_ => {
|
|
eprintln!("error: public key must be 32 bytes of hex");
|
|
return ExitCode::from(2);
|
|
}
|
|
};
|
|
let sig_bytes = match hex_decode(&args[2]) {
|
|
Ok(b) if b.len() == 64 => b,
|
|
_ => {
|
|
eprintln!("error: signature must be 64 bytes of hex");
|
|
return ExitCode::from(2);
|
|
}
|
|
};
|
|
let payload = match std::fs::read(&args[3]) {
|
|
Ok(p) => p,
|
|
Err(e) => {
|
|
eprintln!("error: cannot read payload: {e}");
|
|
return ExitCode::from(2);
|
|
}
|
|
};
|
|
let mut pk_array = [0u8; 32];
|
|
pk_array.copy_from_slice(&pk_bytes);
|
|
let verification_key = match VerificationKey::try_from(pk_array) {
|
|
Ok(k) => k,
|
|
Err(_) => {
|
|
// Undecodable key = REJECT verdict (same contract as the other
|
|
// quorum members): all members must judge the same bytes.
|
|
println!("INVALID");
|
|
return ExitCode::from(1);
|
|
}
|
|
};
|
|
let mut sig_array = [0u8; 64];
|
|
sig_array.copy_from_slice(&sig_bytes);
|
|
let signature = Signature::from_bytes(&sig_array);
|
|
match verification_key.verify_sha512(&signature, &payload) {
|
|
Ok(()) => {
|
|
println!("OK");
|
|
ExitCode::SUCCESS
|
|
}
|
|
Err(_) => {
|
|
println!("INVALID");
|
|
ExitCode::from(1)
|
|
}
|
|
}
|
|
}
|