This work extends the prior art by dynamically generating composite proofs based on an input SPARQL query. That is, given a SPARQL query, the datasets that are used to answer the query will be selectively disclosed to the extent necessary to satisfy the query. So, rather than proving the execution of a SPARQL query in zero-knowledge or proving existence of solution bindings, we produce a result dataset that (a) contains all the solution bindings to the SPARQL query and (b) proves knowledge of undisclosed terms (i.e. not selected variables).
We can
- hide or reveal individual terms in digitally signed datasets, and prove knowledge of the datasets' signatures
- prove equality of (occurrences of) individual terms in and across such datasets
- or hide that equality between occurrences of individual terms
- prove numeric bounds (within u64 range) of
xsd:integerandxsd:dateTimeliterals (only positive numbers supported atm) - prove numeric bounds (within i32 range) of
xsd:integerandxsd:dateTimeliterals (which is a simple modification)
This is an experimental proof-of-concept, not a library.
- benches/ - Benchmark suite.
- risc0 SPARQL verification baseline (adapted from publicly available "SPARQL-in-zkVM" implementations).
- zkSPARQL bench suite (adapted from existing zkSPARQL benchmarks and normalized to TriG notation).
- data/ some example datasets to play around with when executing a bin
- src/ source code.
For example (assume prefixes to be set):
Assume there is a digitally signed dataset, e.g., a Verifiable Credential like
# e.g. for an internship
[] a org:Membership;
org:member <http://example.org/users#user123>;
org:organization <http://example.org/organisations#aCompany> ;
time:hasBeginning "2024-01-01T00:00:00Z"^^xsd:dateTimeStamp ;
time:hasEnd "2025-12-31T23:59:59Z"^^xsd:dateTimeStamp .
# BBS+ signature by the issuer and additional VC terms are omitted for brevity.and another one like
# to show range proof on integers work
<http://example.org/users#user123> foaf:age 25.
# BBS+ signature by the issuer and additional VC terms are omitted for brevity.Now assume a SPARQL query (for the sake of the example)
SELECT ?org WHERE {
?user foaf:age ?age .
?membership org:member ?user.
?membership org:organization ?org .
?membership time:hasEnd ?endDate .
FILTER(?endDate > xsd:dateTime("2025-10-01T00:00:00Z") )
FILTER(?age <= 25 )
}The full result can be found here, here an excerpt:
GRAPH _:0_4 {
_:0_0 _:0_1 _:0_2 .
_:0_5 _:0_6 _:0_7 .
_:0_10 time:hasEnd _:0_12 .
_:0_10 org:member _:0_17 .
_:0_10 org:organization <http://example.org/organisations#aCompany> .
}
GRAPH _:2_4 {
_:0_17 foaf:age _:2_2 .
}
GRAPH _:presentationProofGraph {
_:cproof rdf:type zkp:CompositeProof .
_:cproof zkp:hasComponent _:p0 .
_:cproof zkp:hasComponent _:p1 .
_:cproof zkp:hasComponent _:p2 .
_:cproof zkp:hasComponent _:p3 .
_:p0 rdf:type bbsp16:PoKS16 .
_:p1 rdf:type lg16:LegoGroth16ProofOfRangeMembership .
_:p2 rdf:type bbsp16:PoKS16 .
_:p3 rdf:type lg16:LegoGroth16ProofOfRangeMembership .
# more proof details omitted
}Note that _:0_17 is re-used across graphs.
The proof includes the fact that all occurrences of _:0_17 refers to the same underlying secret value, the identifier of the user.
To this end, two proofs of knowledge of signature are needed; one for each respective original graph of _:0_4 and _:2_4.
The two additional proofs are the proofs of numeric bounds of _:0_12, the end time of the membership, and of _:2_2, the age of the user.
Note that the company's identifier is revealed, as requested in the SELECT statement of the query.
All other information are only proven to be true:
- the signatures of the graphs are proven to be valid,
- the graphs' triples are proven to be known, without needing to reveal terms.
- the numeric bounds as per the FILTER statements are proven to be fulfilled without revealing the literal.
- the relations between user and organisation are proven to exist without revealing the membership identifier
Only information explicitly requested (or previously known as the IRIs) in the query are revealed, while still being proven to have been signed.
For example, note that in the first two triples of graph _:0_4, we do not reveal that their subjects are actually the same value that also underlies _:0_10.
# set up the repo
git clone <repo-url>
cd <repo>
cargo run --bin issuer
cargo run --bin prover
cargo run --bin verifier
# benchmark the performance
cargo bench
# or just one at a time
cargo bench --bench risc0-bench
cargo bench --bench zkSPARQL-benchWe used the rust compiler rustc 1.88.0 (6b00bc388 2025-06-23).
Or use the Dockerfile
docker build -t sparql-zkp-demo .
docker run --rm sparql-zkp-demoPlease cite this work by referring to
Christoph H.-J. Braun, Jesse Wright, Tobias Käfer: Proving Soundness of SPARQL Query Results Using Selective Disclosure of RDF Datasets and Zero-Knowledge Proofs. ESWC (1) 2026: 297-318
or use the following BibTeX record
@inproceedings{DBLP:conf/esws/BraunWK26,
author = {Christoph H.{-}J. Braun and
Jesse Wright and
Tobias K{\"{a}}fer},
editor = {Maribel Acosta and
Marieke van Erp and
Sebastian Rudolph and
Olaf Hartig and
Blerina Spahiu and
Anisa Rula and
Daniel Garijo and
Francesco Osborne},
title = {Proving Soundness of {SPARQL} Query Results Using Selective Disclosure
of {RDF} Datasets and Zero-Knowledge Proofs},
booktitle = {The Semantic Web - 23rd European Semantic Web Conference, {ESWC} 2026,
Dubrovnik, Croatia, May 10-14, 2026, Proceedings, Part {I}},
series = {Lecture Notes in Computer Science},
pages = {297--318},
publisher = {Springer},
year = {2026},
url = {https://doi.org/10.1007/978-3-032-25156-5\_16},
doi = {10.1007/978-3-032-25156-5\_16},
timestamp = {Wed, 20 May 2026 08:34:03 +0200},
biburl = {https://dblp.org/rec/conf/esws/BraunWK26.bib},
bibsource = {dblp computer science bibliography, https://dblp.org}
}