Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ consumers must preserve unknown fields.
| `@verifiable-okf/canon` | ✅ M0 | Canonicalization (§5) + JCS (§6) + signing-payload builder. The shared, version-pinned core. |
| `@verifiable-okf/signer` | ✅ M0 | `vokf-sign` — attaches `provenance` without mutating any other byte. |
| `@verifiable-okf/verifier` | ✅ M0 | `vokf-verify` — the §10 verification algorithm (status + reason codes), schema validation, did:web/did:key. |
| `@verifiable-okf/visualizer` | ⏳ M1 | Bundle → single offline HTML with per-concept badges. |
| `@verifiable-okf/visualizer` | ✅ M1 | Bundle → single offline HTML with per-concept badges. |
| `@verifiable-okf/attest-zk-groth16` | ✅ M1 | `zk-groth16` attestation verifier (Groth16/BN254 via snarkjs, offline); bundles the `contains_no_pii` demo circuit. |

## Quickstart (< 5 min)

Expand Down Expand Up @@ -47,4 +48,25 @@ pnpm build
pnpm test
```

## Attestations (zk-groth16)

Attestations are pluggable, machine-checkable claims carried in `provenance.attestations`.
`@verifiable-okf/attest-zk-groth16` provides an offline Groth16 proof verifier; pass it to the
verifier's policy to turn the §10 attestation path into real ZK checks:

```ts
import { verifyConcept } from "@verifiable-okf/verifier";
import { groth16AttestationVerifier } from "@verifiable-okf/attest-zk-groth16";

await verifyConcept(concept, {
policy: { requiredClaims: ["contains_no_pii"] },
attestationVerifiers: { "zk-groth16": groth16AttestationVerifier },
});
```

The `contains_no_pii` demo circuit (`circuits/contains_no_pii/`, regenerate with `gen.sh`) proves a
private field is absent; verification needs only the bundled verification key — no proving key, no
circom, no network. Proof *issuance* (and metering) is a separate, commercial concern, not part of
this OSS engine.

See [`CANONICALIZATION.md`](./CANONICALIZATION.md) and [`SECURITY.md`](./SECURITY.md).
4 changes: 4 additions & 0 deletions circuits/contains_no_pii/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build/
*.ptau
*.zkey
*.wtns
14 changes: 14 additions & 0 deletions circuits/contains_no_pii/contains_no_pii.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pragma circom 2.1.9;

// Minimal demo circuit for the `contains_no_pii` claim.
// Proves (in zero knowledge) that a private field equals 0 — i.e. a PII
// counter / flag is absent. A valid proof therefore attests "no PII",
// without revealing anything further. Public output `ok` is always 1.
template ContainsNoPii() {
signal input pii_field; // private witness (e.g. count of PII matches)
signal output ok; // public
pii_field === 0; // constraint: only satisfiable when pii_field == 0
ok <== 1;
}

component main = ContainsNoPii();
18 changes: 18 additions & 0 deletions circuits/contains_no_pii/gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Regenerate the contains_no_pii Groth16 artifacts (circom 2 + snarkjs).
# Outputs build/, then copies vkey.json + a sample proof to the circuit root.
set -e
cd "$(dirname "$0")"
mkdir -p build && cd build
circom ../contains_no_pii.circom --r1cs --wasm -o .
npx snarkjs powersoftau new bn128 8 pot_0.ptau
npx snarkjs powersoftau contribute pot_0.ptau pot_1.ptau --name=vokf -e="$(head -c 32 /dev/urandom | xxd -p)"
npx snarkjs powersoftau prepare phase2 pot_1.ptau pot_final.ptau
npx snarkjs groth16 setup contains_no_pii.r1cs pot_final.ptau cnp_0.zkey
npx snarkjs zkey contribute cnp_0.zkey cnp_final.zkey --name=vokf -e="$(head -c 32 /dev/urandom | xxd -p)"
npx snarkjs zkey export verificationkey cnp_final.zkey vkey.json
echo '{"pii_field":"0"}' > input.json
node contains_no_pii_js/generate_witness.js contains_no_pii_js/contains_no_pii.wasm input.json witness.wtns
npx snarkjs groth16 prove cnp_final.zkey witness.wtns proof.json public.json
npx snarkjs groth16 verify vkey.json public.json proof.json
cp vkey.json ../vkey.json && cp proof.json ../proof.sample.json && cp public.json ../public.sample.json
28 changes: 28 additions & 0 deletions circuits/contains_no_pii/proof.sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"pi_a": [
"17461939730782852438770980938862447298870257132741896786032076201177378191598",
"8607488854563628719732053192769587193635892693849864793195992234487286965736",
"1"
],
"pi_b": [
[
"11728139912199972330023136803603667424732587153851331680813612153155232420464",
"11200038715154571765669600573424442998636501858871986095753385916766423332238"
],
[
"15898827097719954490264646923107552463817122452175480198633607290137491470234",
"39309480319576073215644807530307792566864076610431022681114688907195330005"
],
[
"1",
"0"
]
],
"pi_c": [
"20569737558178479334100010919083231235115538011684311997543995295726755938354",
"7407113397076647680008114124813864763833032734057969247775727988171334507260",
"1"
],
"protocol": "groth16",
"curve": "bn128"
}
3 changes: 3 additions & 0 deletions circuits/contains_no_pii/public.sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
"1"
]
94 changes: 94 additions & 0 deletions circuits/contains_no_pii/vkey.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{
"protocol": "groth16",
"curve": "bn128",
"nPublic": 1,
"vk_alpha_1": [
"1232894381328749349801999194020364750860714090412859425702737227783362329035",
"4436118752635877068544674281701685683067136631392057119874998162712106835950",
"1"
],
"vk_beta_2": [
[
"14716936584645639048355760343770265219429753952539561837148583891266482232582",
"16016704211999055611191571745890472454130292775011844893044274766649904226588"
],
[
"9057080589359257783096967548757429701196839693892521700242482106975202632599",
"20533264667873917127608317601690877061735828347178346689977693657285325072488"
],
[
"1",
"0"
]
],
"vk_gamma_2": [
[
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
],
[
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
],
[
"1",
"0"
]
],
"vk_delta_2": [
[
"3474799006316917099767645587129093771951975749530518410992048528464400789842",
"14906175476459237866989995382063729370101320006270423677795159512995448203917"
],
[
"5379252983319465967809417952863222386238200287388003645473005624099013594517",
"18986786293210347468478260290195243983141901438725909310713432120103963521504"
],
[
"1",
"0"
]
],
"vk_alphabeta_12": [
[
[
"403749106030442990619315241768518264979636829364042921852032020812672065137",
"7621931038878796889452159494924950157179135094573670140480507261568496685977"
],
[
"16865090028061755323054514475260851841876300676446183862568668824480223320175",
"18327880048007991543735184721865349268992505013333563818619430008549808266562"
],
[
"8787872532129923515649140272636460985886459682924130307132452782207431646038",
"13260321749514392872666733411862938534642858847009090275335091916896884689184"
]
],
[
[
"6387628840775407748790935730268315938700872123575627156397259578938427030883",
"16771398627571756483969946929083216360331205551563536906712676092481376278695"
],
[
"15792279552950039777871914493874300116271355423474748217406866705529595597418",
"16476731688131302025407637631205413052485691063167902404388883062051157327695"
],
[
"14261736778804873940795974174260233059211405548185751146935318338563960445767",
"6447326858417875907252629836795830459066202724682340898082801712486689440581"
]
]
],
"IC": [
[
"18641926387932197056747795654697750247402624656485328984590349834099713095425",
"5797156867949200385824451310324450740426988220911984895467204416449729528236",
"1"
],
[
"566468554377789334102443174096780922474059205361865467335928056340967116619",
"19928558876800948858731875360898910993093008468686622797144379353445393047770",
"1"
]
]
}
32 changes: 32 additions & 0 deletions fixtures/signed/with-bad-zk-attestation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: GA4 Purchase Event
resource: https://example.com/okf/ga4/purchase
description: Server-side GA4 purchase event mapping for the storefront.
tags:
- analytics
- ga4
provenance:
spec_version: verifiable-okf/0.3
issuer: did:key:z6MkneMkZqwqRiU5mJzSG3kDwzt9P8C59N4NGTfBLfSGE7c7
content_hash: sha256:94ea40b78f963160edd233a2ad2aa10c5da2d945eb40e4059dd80399d4ccbd00
valid_from: 2026-01-01T00:00:00Z
valid_until: 2027-01-01T00:00:00Z
attestations:
- claim: contains_no_pii
scheme: zk-groth16
circuit: contains_no_pii
proof: ewogInBpX2EiOiBbCiAgIjE3NDYxOTM5NzMwNzgyODUyNDM4NzcwOTgwOTM4ODYyNDQ3Mjk4ODcwMjU3MTMyNzQxODk2Nzg2MDMyMDc2MjAxMTc3Mzc4MTkxNTk4IiwKICAiODYwNzQ4ODg1NDU2MzYyODcxOTczMjA1MzE5Mjc2OTU4NzE5MzYzNTg5MjY5Mzg0OTg2NDc5MzE5NTk5MjIzNDQ4NzI4Njk2NTczNiIsCiAgIjEiCiBdLAogInBpX2IiOiBbCiAgWwogICAiMTE3MjgxMzk5MTIxOTk5NzIzMzAwMjMxMzY4MDM2MDM2Njc0MjQ3MzI1ODcxNTM4NTEzMzE2ODA4MTM2MTIxNTMxNTUyMzI0MjA0NjQiLAogICAiMTEyMDAwMzg3MTUxNTQ1NzE3NjU2Njk2MDA1NzM0MjQ0NDI5OTg2MzY1MDE4NTg4NzE5ODYwOTU3NTMzODU5MTY3NjY0MjMzMzIyMzgiCiAgXSwKICBbCiAgICIxNTg5ODgyNzA5NzcxOTk1NDQ5MDI2NDY0NjkyMzEwNzU1MjQ2MzgxNzEyMjQ1MjE3NTQ4MDE5ODYzMzYwNzI5MDEzNzQ5MTQ3MDIzNCIsCiAgICIzOTMwOTQ4MDMxOTU3NjA3MzIxNTY0NDgwNzUzMDMwNzc5MjU2Njg2NDA3NjYxMDQzMTAyMjY4MTExNDY4ODkwNzE5NTMzMDAwNSIKICBdLAogIFsKICAgIjEiLAogICAiMCIKICBdCiBdLAogInBpX2MiOiBbCiAgIjIwNTY5NzM3NTU4MTc4NDc5MzM0MTAwMDEwOTE5MDgzMjMxMjM1MTE1NTM4MDExNjg0MzExOTk3NTQzOTk1Mjk1NzI2NzU1OTM4MzU0IiwKICAiNzQwNzExMzM5NzA3NjY0NzY4MDAwODExNDEyNDgxMzg2NDc2MzgzMzAzMjczNDA1Nzk2OTI0Nzc3NTcyNzk4ODE3MTMzNDUwNzI2MCIsCiAgIjEiCiBdLAogInByb3RvY29sIjogImdyb3RoMTYiLAogImN1cnZlIjogImJuMTI4Igp9
public_inputs:
- "2"
signature:
scheme: ed25519
value: Fo0PsKt0yw5QZ4UW7eErcJIgz0FAgOiTdp8+dErnml189GSmyida8gq9DijaIzQnfRNLa/Y6LojEPKcIGx/WAw==
---
# GA4 Purchase Event

Send a `purchase` event when an order is confirmed.

| param | source |
|---|---|
| value | order.total |
| currency | order.currency |
32 changes: 32 additions & 0 deletions fixtures/signed/with-zk-attestation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: GA4 Purchase Event
resource: https://example.com/okf/ga4/purchase
description: Server-side GA4 purchase event mapping for the storefront.
tags:
- analytics
- ga4
provenance:
spec_version: verifiable-okf/0.3
issuer: did:key:z6MkneMkZqwqRiU5mJzSG3kDwzt9P8C59N4NGTfBLfSGE7c7
content_hash: sha256:94ea40b78f963160edd233a2ad2aa10c5da2d945eb40e4059dd80399d4ccbd00
valid_from: 2026-01-01T00:00:00Z
valid_until: 2027-01-01T00:00:00Z
attestations:
- claim: contains_no_pii
scheme: zk-groth16
circuit: contains_no_pii
proof: ewogInBpX2EiOiBbCiAgIjE3NDYxOTM5NzMwNzgyODUyNDM4NzcwOTgwOTM4ODYyNDQ3Mjk4ODcwMjU3MTMyNzQxODk2Nzg2MDMyMDc2MjAxMTc3Mzc4MTkxNTk4IiwKICAiODYwNzQ4ODg1NDU2MzYyODcxOTczMjA1MzE5Mjc2OTU4NzE5MzYzNTg5MjY5Mzg0OTg2NDc5MzE5NTk5MjIzNDQ4NzI4Njk2NTczNiIsCiAgIjEiCiBdLAogInBpX2IiOiBbCiAgWwogICAiMTE3MjgxMzk5MTIxOTk5NzIzMzAwMjMxMzY4MDM2MDM2Njc0MjQ3MzI1ODcxNTM4NTEzMzE2ODA4MTM2MTIxNTMxNTUyMzI0MjA0NjQiLAogICAiMTEyMDAwMzg3MTUxNTQ1NzE3NjU2Njk2MDA1NzM0MjQ0NDI5OTg2MzY1MDE4NTg4NzE5ODYwOTU3NTMzODU5MTY3NjY0MjMzMzIyMzgiCiAgXSwKICBbCiAgICIxNTg5ODgyNzA5NzcxOTk1NDQ5MDI2NDY0NjkyMzEwNzU1MjQ2MzgxNzEyMjQ1MjE3NTQ4MDE5ODYzMzYwNzI5MDEzNzQ5MTQ3MDIzNCIsCiAgICIzOTMwOTQ4MDMxOTU3NjA3MzIxNTY0NDgwNzUzMDMwNzc5MjU2Njg2NDA3NjYxMDQzMTAyMjY4MTExNDY4ODkwNzE5NTMzMDAwNSIKICBdLAogIFsKICAgIjEiLAogICAiMCIKICBdCiBdLAogInBpX2MiOiBbCiAgIjIwNTY5NzM3NTU4MTc4NDc5MzM0MTAwMDEwOTE5MDgzMjMxMjM1MTE1NTM4MDExNjg0MzExOTk3NTQzOTk1Mjk1NzI2NzU1OTM4MzU0IiwKICAiNzQwNzExMzM5NzA3NjY0NzY4MDAwODExNDEyNDgxMzg2NDc2MzgzMzAzMjczNDA1Nzk2OTI0Nzc3NTcyNzk4ODE3MTMzNDUwNzI2MCIsCiAgIjEiCiBdLAogInByb3RvY29sIjogImdyb3RoMTYiLAogImN1cnZlIjogImJuMTI4Igp9
public_inputs:
- "1"
signature:
scheme: ed25519
value: dt9g6B7vZx5hM6j3BZnBJDu5F2d1OjHkoZ7ZkQ7i78Fx1CjzlezpZ5A4YM8ipISCHEF+m0PcM1jo8wPaN+sXAA==
---
# GA4 Purchase Event

Send a `purchase` event when an order is confirmed.

| param | source |
|---|---|
| value | order.total |
| currency | order.currency |
26 changes: 26 additions & 0 deletions packages/attest-zk-groth16/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@verifiable-okf/attest-zk-groth16",
"version": "0.1.0",
"description": "Verifiable OKF attestation verifier for the zk-groth16 scheme (Groth16/BN254 via snarkjs). Offline proof verification; bundles the contains_no_pii demo verification key.",
"license": "Apache-2.0",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
"files": ["dist"],
"scripts": {
"build": "tsc -p tsconfig.json && cp -r src/vkeys dist/vkeys",
"test": "vitest run",
"lint": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"snarkjs": "^0.7.6"
},
"devDependencies": {
"@verifiable-okf/verifier": "workspace:*",
"@verifiable-okf/signer": "workspace:*",
"@types/node": "^20.14.0",
"typescript": "^5.6.0",
"vitest": "^2.1.0"
}
}
51 changes: 51 additions & 0 deletions packages/attest-zk-groth16/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* @verifiable-okf/attest-zk-groth16 — an AttestationVerifier for the
* `zk-groth16` scheme (Groth16 / BN254 via snarkjs).
*
* Proof verification is fully offline: it needs only the circuit's verification
* key (no proving key, no circom, no network). Pass this to the verifier's
* `attestationVerifiers` map to turn the §10 attestation path into real ZK
* checks. The `contains_no_pii` demo verification key is bundled.
*
* The attestation carries (SPEC §8): `scheme: "zk-groth16"`, `circuit` (id),
* `proof` (base64 of the Groth16 proof JSON), and `public_inputs` (signals).
*/
import { groth16 } from "snarkjs";
import { createRequire } from "node:module";

const require = createRequire(import.meta.url);

export type Vkey = unknown;

/** Build a zk-groth16 AttestationVerifier from a map of circuit id → verification key. */
export function createGroth16Verifier(
vkeys: Readonly<Record<string, Vkey>>,
): (attestation: Record<string, unknown>) => Promise<boolean> {
return async (attestation) => {
const circuit = String(attestation["circuit"] ?? "");
const vkey = vkeys[circuit];
if (!vkey) return false; // unknown circuit → does not verify
const proofB64 = attestation["proof"];
const publicSignals = attestation["public_inputs"];
if (typeof proofB64 !== "string" || !Array.isArray(publicSignals)) return false;
let proof: unknown;
try {
proof = JSON.parse(Buffer.from(proofB64, "base64").toString("utf8"));
} catch {
return false;
}
try {
return await groth16.verify(vkey, publicSignals, proof);
} catch {
return false;
}
};
}

/** Verification keys bundled with this package (by circuit id). */
export const BUNDLED_VKEYS: Readonly<Record<string, Vkey>> = {
contains_no_pii: require("./vkeys/contains_no_pii.vkey.json"),
};

/** Default zk-groth16 verifier preloaded with the bundled demo circuit(s). */
export const groth16AttestationVerifier = createGroth16Verifier(BUNDLED_VKEYS);
5 changes: 5 additions & 0 deletions packages/attest-zk-groth16/src/snarkjs.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare module "snarkjs" {
export const groth16: {
verify(vkey: unknown, publicSignals: unknown, proof: unknown): Promise<boolean>;
};
}
Loading
Loading