Bumps [p3-air](https://github.com/Plonky3/Plonky3) from 0.4.3 to 0.6.1. - [Release notes](https://github.com/Plonky3/Plonky3/releases) - [Changelog](https://github.com/Plonky3/Plonky3/blob/main/CHANGELOG.md) - [Commits](https://github.com/Plonky3/Plonky3/compare/p3-air-v0.4.3...p3-air-v0.6.1) --- updated-dependencies: - dependency-name: p3-air dependency-version: 0.6.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> |
||
|---|---|---|
| .. | ||
| examples | ||
| src | ||
| Cargo.toml | ||
| readme.md | ||
Ed25519 Proof of Knowledge of Seed (POKOS)
This crate is an experimental implementation testing (post-quantum) Proof of Knowledge (PoK) on an Ed25519 seed (see https://eprint.iacr.org/2025/1368). It constructs a zero-knowledge proof that a prover knows a seed used to derive an Ed25519 secret key—without revealing the seed or the key itself.
This mechanism is useful for seamless blockchain migration, specifically on networks like Solana that natively rely on Ed25519 for account authentication. Because accounts are intrinsically tied to these public keys, a traditional transition to post-quantum signatures would require users to transfer assets and state to entirely new addresses. However, because EdDSA keys are deterministically derived from a seed (per RFC 8032), that seed can act as a reusable witness in a Post-Quantum Non-Interactive Zero-Knowledge (PQ-NIZK) proof. This allows a user to authorize a new quantum-safe key under their original on-chain identity, protecting dormant accounts and avoiding the friction of a network-wide asset migration even if the legacy public key has already been exposed.
The goal is not simply to prove knowledge of an Ed25519 secret key. The seed, or a commitment to it, is intended to be reused in a separate protocol that derives post-quantum keys from the same root. Consequently, the proof must constrain the entire seed derivation path, rather than merely validating the final Ed25519 key material.
Protocol shape
The public statement is (commit_of_seed, hash_of_sk). The prover holds the
secret seed and proves the following derivation chain in zero knowledge:
commit_of_seed = SHA512("ed25519-pokos/commit/v1" || seed)
prf_output = SHA512("ed25519-pokos/derive-sk/v1" || seed)
sk_seed = prf_output[0..32]
hash_of_sk = SHA512("ed25519-pokos/hash-sk/v1" || sk_seed)
Each domain label is zero-padded to 32 bytes, then concatenated with the 32-byte payload to form a single 64-byte message hashed in one SHA-512 block.
All three SHA-512 evaluations are proved by a single Plonky3 STARK over the
SHA-512 compression function. The outer proof envelope is then signed by the
Ed25519 key derived from sk_seed, binding the proof to the key owner without
exposing either the seed or the secret key.
Security tradeoff
This is a temporary construction. It does not prove sk → pk inside the
circuit; that link is enforced externally by the Ed25519 signature over the
proof transcript.
- The ZK proof ties
seed → sk. - The Ed25519 signature ties
sk → pk.
This is weaker than a fully end-to-end statement because the linkage to pk is
not proven inside the circuit — it is enforced by an external signature.
In the pre-quantum setting this is sufficient: only the legitimate owner of sk
can produce a valid signature. In a post-quantum setting, once an adversary can
recover sk from pk, the signature no longer adds meaningful security. At
that point the temporary authentication layer becomes unnecessary anyway, and the
system can rely on the revealed sk directly.
This construction should be read as a temporary engineering compromise, not as the ideal final statement.
Fully sound version
The fully sound end-to-end statement would use these public inputs:
commit_of_seedpk
And prove:
commit_of_seedcommits to some seed- the seed is transformed into the Ed25519 secret key
skby the specified PRF - the corresponding Ed25519 public key derived from
skis exactlypk
This directly binds the seed commitment to the Ed25519 public key inside the proof, with no external signature required.
Why the fully sound version is not used here
The main bottleneck is proving Ed25519 group operations inside the circuit under
emulated arithmetic. Our proof-of-concept for the fully end-to-end version
exceeds 1 billion gates. The dominant cost is in-circuit elliptic-curve
arithmetic for the sk → pk derivation.
By contrast, this construction avoids that expensive step: instead of proving
sk → pk in circuit it exposes hash_of_sk and delegates the sk → pk link
to an external Ed25519 signature. The result is far more practical:
| Construction | Proof size (default settings) |
|---|---|
| This crate (temporary) | ~401 KB |
| Fully end-to-end | not yet practical |
Crate layout
src/
lib.rs — public types, constants, and re-exports
prover.rs — key derivation, statement assembly, proof generation
verifier.rs — STARK verification, Ed25519 auth check, serialization
private_seed_chain.rs — public witness/layout types for the three-segment chain
sha512/ — Plonky3 AIR, trace builder, constraints, proof API
air.rs — Sha512RoundAir (implements the Plonky3 Air trait)
circuit.rs — Sha512Circuit: reference hash, block compression, trace gen
private_seed_chain.rs — STARK prover/verifier for the seed chain
proof_api/ — STARK config, FRI settings, serialization
The prover and verifier modules are intentionally separated so that a
verifier binary need not link any prover code.
Wire format
serialize_proof / deserialize_proof produce a versioned byte sequence:
[magic: 8 bytes "EPKOS001"]
[commit_of_seed: 64 bytes]
[hash_of_sk: 64 bytes]
[sha512_proof_len: 8 bytes big-endian u64] [sha512_proof: N bytes]
[authentication_key: 32 bytes]
[authentication_signature: 64 bytes]
Usage
use ed25519_pokos::{Seed, gen_pokos, verify_pokos, serialize_proof, deserialize_proof};
// ── Prover ──────────────────────────────────────────────────────────────────
let seed: Seed = [7_u8; 32];
let proof = gen_pokos(seed)?;
// Inspect the public statement without running the verifier:
println!("commit_of_seed: {:02x?}", proof.statement.commit_of_seed);
println!("hash_of_sk: {:02x?}", proof.statement.hash_of_sk);
// Serialize for transport:
let proof_bytes = serialize_proof(&proof);
// ── Verifier (can run in a separate process / binary) ─────────────────────
let proof = deserialize_proof(&proof_bytes)?;
verify_pokos(&proof)?;
# Ok::<(), String>(())
Run the bundled example:
cargo run --release -p ed25519-pokos --example gen_verify_pokos
Sample output (fixed seed [7u8; 32], timings vary by machine):
commit_of_seed: [01, ff, 79, 8a, 05, 6f, b4, a4, 25, 13, 16, d5, 9e, a3, fe, 21, e9, 1e, 56, dd, 6b, cb, 69, db, 03, dd, 27, f2, 06, 8d, 9a, a2, ca, c0, cc, f2, 6f, d3, 9f, 4c, cb, ac, 00, 8a, d8, c1, c0, 70, f8, 51, d2, c4, 64, b0, e7, bb, de, 4f, 86, bb, 0e, 2f, e1, dc]
hash_of_sk: [52, 5c, 52, 0a, 8a, 61, bf, 28, df, 5b, a5, 4e, 31, fe, 53, 2d, 43, 3a, 35, 91, 5d, 6e, 78, 71, 4f, 01, 4c, de, c7, d8, 5f, 48, 9e, 91, a4, 3a, c9, be, 1b, ce, 63, a1, 15, 79, 84, 14, b4, ef, 06, c6, ec, c9, 60, 78, 0a, 52, 0c, ff, 48, d8, 75, fd, 6a, 67]
proving_time_ms: 55
verification_time_ms: 13
air_trace_rows: 512
air_trace_cols: 1076
proof_bytes: 400848
verification: ok
The trace shape and proof envelope bytes are stable unless the AIR or the
default Sha512ProofSettings change.
Current implementation status
- The STARK proves all three SHA-512 relations in a single concatenated three-segment proof.
- The authentication key and signature use the Ed25519 implementation in this
repo (
curve25519crate). - The
sk → pklink is not proved inside the circuit; it remains external and is enforced by the Ed25519 signature over the proof statement. - The proof settings (
Sha512ProofSettings) are configurable, but the verifier enforces minimum security thresholds and rejects proofs produced with weaker parameters.