Commitment Scheme & CommitmentTypes
aegis intermediate 5 min read
ELI5
A commitment is a locked ballot box: you drop your vote in, lock it, and hand over the locked box. Later you prove you voted correctly by opening the box. AEGIS uses four box types — for user identity, document id, timestamp, and access pattern — so none of those facts leak until deliberately opened.
Technical Deep Dive
Commitment and CommitmentManager live in src/commitments.rs. A commitment hides one of four attributes:
CommitmentType | Hidden attribute |
|---|---|
UserCommitment | User identity |
DocumentCommitment | Document identifier |
TimestampCommitment | Access timestamp |
AccessCommitment | Access pattern / level |
Commitment Construction
sequenceDiagram participant Caller participant Commitment participant blake3 Caller->>Commitment: Commitment::new(id, type, value) Commitment->>blake3: hash(id + now_ms) blake3-->>Commitment: nonce (32 bytes) Commitment-->>Caller: Commitment { value, nonce, timestamp_ms }The nonce is derived as blake3::hash(format!("{}{}", id, now).as_bytes()). This is deterministic given the same id and timestamp, which is a known limitation — callers requiring unpredictability should supply their own nonce via with_nonce(nonce).
Open (Verify) Phase
CommitmentManager::verify_commitment(id, secret, nonce) recomputes:
recomputed = blake3::hash(secret || nonce)and checks recomputed == commitment.value. The commitment scheme is therefore Commit(secret) = blake3(secret || nonce).
Class Diagram
classDiagram class Commitment { +commitment_id: String +commitment_type: CommitmentType +value: Vec~u8~ +nonce: Vec~u8~ +timestamp_ms: u64 +is_valid() bool +size_bytes() usize } class CommitmentManager { -commitments: HashMap~String, Commitment~ +create_commitment(id, type, value) Result~Commitment~ +get_commitment(id) Option~Commitment~ +verify_commitment(id, secret, nonce) bool +commitment_count() usize +average_size() usize } class CommitmentType { UserCommitment DocumentCommitment TimestampCommitment AccessCommitment } CommitmentManager "1" --> "0..*" Commitment : stores Commitment --> CommitmentType : typed bysize_bytes
size_bytes() = value.len() + nonce.len(). For a blake3 hash value (32 bytes) and blake3 nonce (32 bytes), a typical commitment is 64 bytes.
Key Terms
- Commitment →
(value, nonce)pair hiding a secret attribute;value = blake3(secret || nonce) - CommitmentType → Four-variant enum tagging what attribute a commitment hides
- nonce → 32-byte blake3 hash of
(id, timestamp)by default; overridable viawith_nonce - verify_commitment → Open phase: recomputes
blake3(secret || nonce)and compares to stored value - hiding → Commitment property: value reveals nothing about the secret without the nonce
Q&A
Q: If two commitments are created with the same id in the same millisecond, do they share the same nonce?
A: Yes. The nonce formula is blake3(id + now_ms), so identical (id, timestamp) pairs produce identical nonces. Callers requiring unique nonces must use with_nonce with a random value.
Q: create_commitment inserts with id as key — does a second call with the same id overwrite the first?
A: Yes. HashMap::insert replaces the existing entry. The old commitment is dropped.
Q: Does the commitment scheme provide binding (can’t change the secret after committing)?
A: Binding holds computationally under blake3 collision resistance. Hiding holds computationally if the nonce is uniformly random; the deterministic default nonce weakens hiding for identical (id, timestamp) inputs.
Examples
Create and open a user identity commitment:
let mut manager = CommitmentManager::new();let secret = b"user-secret-key";let nonce = blake3::hash(b"unique-nonce").as_bytes().to_vec();let value = blake3::hash(&[secret.as_ref(), &nonce].concat()).as_bytes().to_vec();manager.create_commitment("u1", CommitmentType::UserCommitment, value).unwrap();
// Open phase: verifylet valid = manager.verify_commitment("u1", secret, &nonce);assert!(valid);neighbors on the map
- FNP Halo2 Zero-Knowledge Circuits understanding zero-knowledge proofs in FNP
- FNP M²-ORE Order-Revealing Encryption understanding how the server orders encrypted positions
- ZKProof & ZKEngine tracing why a ZK proof fails verify_structure but not verify_zk_proof
- Verifier & Verification Protocol diagnosing why verify_zk_proof returns Ok(false) for a proof that appears structurally valid