Skip to content
commit-reveal Source  →

A cryptuon library · v1.0.0 · MIT · Python ≥ 3.8

Cryptographic commit–reveal, with zero-knowledge proofs, in pure Python.

A commitment binds you to a value without revealing it; the reveal proves you bound to that value and no other. This library implements that primitive with the Python standard library and nothing else — eight hash algorithms, optional Schnorr ZKPs on secp256k1, and a CLI that never writes plaintext to disk.

Runtime deps in pyproject.toml

0

Only python = "^3.8". Nothing transitive.

Modules imported from stdlib

hashlib, hmac, secrets

Plus getpass for the secure CLI.

Curve, implemented from scratch

secp256k1

The same curve as Bitcoin. Hand-rolled affine arithmetic.

§ 1

What commit-reveal is for

Any protocol where one party must bind to a choice without yet announcing it. The two-phase structure removes the timing advantage that would otherwise let later participants peek at earlier ones.

  • Sealed-bid auctions

    Bidders publish a commitment to their bid during the commit phase; nobody, including the auctioneer, can read it. After the deadline, they reveal. The protocol guarantees no late-bidder could have peeked at earlier bids.

    — docs/use-cases.md: "Auction Systems"

  • On-chain randomness without a trusted oracle

    Multiple parties commit to seeds, then reveal them. The final randomness is a hash of all reveals. As long as one participant is honest, the outcome is uniform and unmanipulable.

    — classical commit-reveal RNG

  • Fair-launch and anti-MEV ordering

    Order intents are committed during a sealed window, then revealed for matching. Front-runners cannot react to orders they have not yet seen, because they have not yet been revealed.

    — MEV-resistant DEX design

  • Verifiable hidden votes

    Each voter commits to a choice, then reveals. With a ZKP, a voter can prove "my reveal matches my commitment" without re-publishing the ballot for everyone to see during verification.

    — voting protocol literature

§ 2

What is actually implemented

Two primitives, both grounded in the source. There are no Pedersen commitments, no KZG, no Merkle commitments, no pairing-friendly curves — if you need those, see the comparison.

Primitive § 2.1

Hash-based commitments

H(value || salt) where the hash is selectable per scheme instance. Salt is 32 random bytes from secrets.token_bytes by default. Reveal comparison uses hmac.compare_digest for constant-time equality.

sha256sha384sha512sha3_256sha3_384sha3_512blake2bblake2s

Primitive § 2.2

Schnorr zero-knowledge proofs

Non-interactive Schnorr proof of knowledge of the secret derived from (value, salt), made non-interactive via the Fiat-Shamir heuristic with SHA-256 as the challenge hash. Proves "I committed to this value" without revealing it.

secp256k1

§ 3

A worked example

Lifted verbatim from the README. The library refuses to start with an insecure hash — md5 and sha1 raise SecurityError at construction time.

Reveal is timing-safe: comparison goes through hmac.compare_digest, not ==.

Listing 1 — basic commit and reveal

from commit_reveal import CommitRevealScheme

scheme = CommitRevealScheme()

# commit: share the commitment, keep the salt secret
commitment, salt = scheme.commit("my secret value")

# reveal: prove you committed to this value
assert scheme.reveal("my secret value", salt, commitment)      # True
assert not scheme.reveal("wrong value", salt, commitment)      # False

Listing 2 — with a Schnorr ZKP on secp256k1

scheme = CommitRevealScheme(use_zkp=True)

commitment, salt = scheme.commit("secret")
public_key, R_compressed, challenge, response = scheme.create_zkp_proof(
    "secret", salt, commitment
)

# anyone can verify you know the secret — without learning it
assert scheme.verify_zkp_proof(
    commitment, public_key, R_compressed, challenge, response
)

Listing 3 — the secure CLI

# prompts via getpass; never echoes; never writes plaintext to disk
commit-reveal-secure commit my-secret
commit-reveal-secure reveal my-secret
commit-reveal-secure list

Table 1

Supported hash algorithms

Configured per scheme instance through CommitRevealScheme(hash_algorithm=...). Anything not in this table raises ValidationError; md5 and sha1 raise SecurityError.

Identifier Output length Family Notes
sha25632 bytesSHA-2Default; widely audited.
sha38448 bytesSHA-2
sha51264 bytesSHA-2Higher security margin.
sha3_25632 bytesSHA-3 / KeccakNIST post-Merkle–Damgård family.
sha3_38448 bytesSHA-3 / Keccak
sha3_51264 bytesSHA-3 / Keccak
blake2b64 bytesBLAKE2Fast on 64-bit platforms.
blake2s32 bytesBLAKE2Fast on 32-bit / embedded.

§ 4

API at a glance

One class, three pure-function methods for the commit-reveal core, three more for the ZKP path. ZKP methods raise ValueError unless the scheme was constructed with use_zkp=True.

class CommitRevealScheme:
    def __init__(
        self,
        hash_algorithm: str = "sha256",
        use_zkp: bool = False,
        enable_audit: bool = True,
    ): ...

    # commit-reveal core
    def commit(value, salt=None) -> tuple[bytes, bytes]: ...
    def reveal(value, salt, commitment) -> bool: ...
    def verify(value, salt, commitment) -> bool:    # alias of reveal
        ...

    # Schnorr ZKP on secp256k1 (requires use_zkp=True)
    def create_zkp_proof(value, salt, commitment) -> tuple: ...
    def verify_zkp_proof(
        commitment, public_key, R_compressed, challenge, response
    ) -> bool: ...
    def verify_commitment_consistency(
        value, salt, commitment, public_key
    ) -> bool: ...

# raised exceptions:
#   ValidationError  — invalid input
#   SecurityError    — insecure hash (md5, sha1) or unsafe operation

¶ Colophon

Read the source. It is short, typed, and audit-friendly.

Eight files under commit_reveal/, the longest of which is the secp256k1 implementation. mypy strict. Hypothesis property tests. Black formatted. Bandit clean.