ZK Proof-of-Observation: Cryptographic Evidence You Saw It First
MalCloud generates auditable zk-SNARK proofs that an organization observed a threat indicator before it was shared — without revealing the indicator.
Threat intelligence has a provenance problem. When two organizations independently discover the same C2 infrastructure, who observed it first? When an insurer needs to verify that a policyholder had indicators of compromise before a breach disclosure, what constitutes evidence? When regulators ask whether an organization was “aware” of a threat prior to an incident, what proof survives scrutiny?
Timestamps in SIEM logs don’t cut it. They’re mutable, they’re controlled by the claiming party, and they carry zero cryptographic weight. An organization asserting “we saw this domain on March 3rd” based on Splunk logs is making an unfalsifiable claim. The logs could have been written yesterday.
This matters in three concrete contexts:
- Cyber insurance — Insurers evaluating claims need to establish timelines. Did the policyholder observe precursor indicators before the breach? Were they negligent in not acting on intelligence they had? The answer currently depends on logs the claimant controls.
- Regulatory compliance — DORA Article 19 requires financial entities to report major ICT incidents. Demonstrating that threat indicators were identified before an incident became material affects liability. The difference between “we detected and were responding” and “we missed it entirely” can be the difference between a finding and a fine.
- Attribution and credit — Threat intelligence producers who discover infrastructure first have no way to prove priority without publishing the indicator, which burns it. By the time credit matters, the indicator is public and everyone claims prior knowledge.
Why naive approaches fail
Signed timestamps — You can sign a log entry with your organization’s key and a timestamp. This proves you possessed the key at signing time, but says nothing about when you actually observed the indicator. You could observe it today and backdate the signed entry. Signing proves authorship, not chronology.
Trusted third parties — Send your IOC to a timestamping service. Now you’ve disclosed the indicator to a third party. If the service is compromised or subpoenaed, your watchlist is exposed. You’ve traded a provenance problem for a confidentiality problem.
Blockchain anchoring — Hash your IOC and write the hash to a public blockchain. This gives you a verifiable timestamp for the hash, but anyone monitoring the chain who independently knows the IOC can confirm you were tracking it. For common indicators, this is a direct leak. For novel ones, the hash itself becomes a correlation target.
Append-only logs with audit — Maintain tamper-evident logs (like Certificate Transparency). Better, but the log contents are readable. An append-only log of your IOC observations is a browsable inventory of your defensive focus.
Each approach solves one property while breaking another. Signed timestamps lack temporal binding. Third parties lack confidentiality. Blockchains lack privacy. Append-only logs lack secrecy. What’s needed is a mechanism that simultaneously provides temporal binding, confidentiality, and verifiability.
Our approach: commit, accumulate, prove
MalCloud’s Proof-of-Observation system operates in three phases:
Phase 1: Commitment
When an IOC is ingested — from a feed, manual entry, extractor, or SIEM correlation — MalCloud immediately computes a cryptographic commitment:
commitment = MiMC(IOC_value || nonce || timestamp)
The nonce is a 256-bit random value generated per-commitment. It prevents dictionary attacks against the hash: without the nonce, an attacker who knows the IOC could brute-force the timestamp component. With the nonce, the commitment is indistinguishable from random even if the IOC is publicly known.
We use MiMC (Minimal Multiplicative Complexity) rather than SHA-256 because MiMC is algebraically structured for arithmetic circuits. In a Groth16 zk-SNARK circuit, SHA-256 requires approximately 25,000 R1CS constraints. MiMC achieves equivalent security with roughly 200 constraints — a 125x reduction that directly translates to faster proof generation and smaller proving keys.
Phase 2: Merkle accumulation
Commitments are inserted into an append-only Merkle tree. The tree is partitioned into epochs (configurable; default is 1 hour). At the close of each epoch, MalCloud publishes the Merkle root to a public record: a transparency log endpoint, a signed receipt, or optionally an external anchor like a blockchain or RFC 3161 timestamping authority.
The published root is the tamper-evidence anchor. Any modification to a commitment after the epoch closes would change the root, invalidating all proofs derived from it. The append-only structure means commitments can be added but never removed or altered.
Epoch 2026-04-16T08:00Z
├── commitment_0: MiMC(IOC_a || nonce_a || ts_a)
├── commitment_1: MiMC(IOC_b || nonce_b || ts_b)
├── ...
├── commitment_n: MiMC(IOC_n || nonce_n || ts_n)
└── root: 0x7a3f...b812 (published)
Phase 3: zk-SNARK proof
When an organization needs to prove they observed a specific indicator before a given time, MalCloud generates a Groth16 zk-SNARK proof. The proof demonstrates knowledge of private inputs (the IOC, nonce, and timestamp) that satisfy public constraints (the commitment is in the published tree and the timestamp precedes a bound) — without revealing any private input.
The circuit
The Groth16 circuit, implemented in gnark (Go), encodes the following relation:
Circuit: ProofOfObservation
Public inputs:
- merkle_root : field element (published epoch root)
- time_bound : field element (the "before this time" threshold)
Private inputs (witness):
- ioc_value : field element (the indicator)
- nonce : field element (256-bit random)
- timestamp : field element (observation time)
- merkle_path : [depth]field element (sibling hashes)
- path_indices : [depth]bit (left/right at each level)
Constraints:
1. commitment = MiMC(ioc_value || nonce || timestamp)
2. MerkleVerify(commitment, merkle_path, path_indices) == merkle_root
3. timestamp <= time_bound
Constraint 1 binds the private inputs to a specific commitment. Constraint 2 proves that commitment exists in the published Merkle tree. Constraint 3 proves the observation happened before the claimed time bound.
The verifier sees only the Merkle root and the time bound. They learn nothing about the IOC value, the nonce, or the exact timestamp — only that some indicator was observed before the bound and its commitment is in the published root.
Performance
| Metric | Value |
|---|---|
| R1CS constraints | 15,774 |
| Proof size | 164 bytes (Groth16, compressed) |
| Proof generation | <2s (AMD EPYC 7763, single core) |
| Verification | <5ms |
| Merkle tree depth | 20 (supports ~1M commitments per epoch) |
| Commitment cost | <1ms per IOC |
The constraint count breaks down as: ~200 for MiMC hash, ~15,000 for Merkle path verification (20 levels × ~750 constraints per MiMC hash at each level), ~574 for the timestamp comparison. Groth16’s constant-size proofs mean the 164-byte proof is the same whether the tree has 100 or 1,000,000 commitments.
Trust model
MalCloud operates as the ledger keeper. The organization’s MalCloud instance computes commitments, maintains the Merkle tree, and generates proofs. The published epoch roots serve as the tamper-evidence anchor.
This is not a trustless system. The trust assumptions are explicit:
- MalCloud instance integrity — The instance must be running unmodified code. A compromised instance could fabricate commitments with false timestamps. This is the same trust model as any local security tool: if the attacker controls your infrastructure, all bets are off.
- Epoch root publication — The root must be published before the epoch can be cited as evidence. Publication to an external trust anchor (RFC 3161 TSA, blockchain, or third-party transparency log) provides stronger guarantees than self-publication.
- Clock accuracy — Timestamps are sourced from the host system clock. NTP synchronization is assumed. A manipulated clock produces commitments with incorrect timestamps, but the epoch publication timestamp (externally anchored) bounds the maximum possible backdating.
The key property: once an epoch root is published, MalCloud cannot retroactively insert, remove, or modify commitments without detection. The published root is a one-way commitment to the entire epoch’s contents.
What we explicitly don’t claim
This is not a blockchain. There is no consensus mechanism, no distributed ledger, no mining. The Merkle tree is a local data structure. The published roots are assertions by a single party (the MalCloud operator), optionally anchored to external systems.
This is not decentralized. The MalCloud instance is the single authority over its own tree. Cross-organization verification requires trusting each organization’s published roots, or anchoring to a mutually trusted third party.
This is not proof-of-absence. The system proves “I observed indicator X before time T.” It cannot prove “I did not observe indicator Y.” Absence proofs require a different cryptographic construction (e.g., authenticated dictionaries with non-membership proofs) that we haven’t implemented.
This is not timestamping-as-a-service. MalCloud proves observation time relative to epoch boundaries. The granularity is bounded by epoch duration. If your epoch is 1 hour, you can prove observation before the hour boundary, not down to the millisecond.
What’s next
Cross-org verification. Two organizations with their own MalCloud instances should be able to prove they both observed the same IOC without revealing it — combining Proof-of-Observation with the ZK-STIX set intersection protocol. The circuit extension is straightforward: prove that the same preimage appears in two different Merkle roots.
External trust anchors. We’re evaluating integration with RFC 3161 timestamping authorities and Certificate Transparency-style logs for epoch root publication. The goal: epoch roots with temporal binding that doesn’t depend on the MalCloud operator’s clock or publishing discipline.
Selective disclosure extensions. Proving observation of an IOC that matches a specific pattern (e.g., “I observed a domain matching *.evil[.]com before April 1st”) without revealing the specific subdomain. This requires range proofs or regex-to-circuit compilation, both active areas of ZK research.
The Proof-of-Observation circuit is live in MalCloud. Every IOC ingested generates a commitment. Epoch roots publish on schedule. When you need to prove you saw it first, the proof is already waiting.
Interested in self-hosted threat intelligence?
Request a Briefing