@pvium/p2id-verifier 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -0
- package/dist/environments.d.ts +25 -0
- package/dist/environments.js +42 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +8 -0
- package/dist/publicInputs.d.ts +49 -0
- package/dist/publicInputs.js +55 -0
- package/dist/signer.d.ts +7 -0
- package/dist/signer.js +24 -0
- package/dist/verify.d.ts +19 -0
- package/dist/verify.js +53 -0
- package/dist/verifyIdentity.d.ts +50 -0
- package/dist/verifyIdentity.js +125 -0
- package/dist/vk.d.ts +6 -0
- package/dist/vk.js +7 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# @pvium/p2id-verifier
|
|
2
|
+
|
|
3
|
+
Pvium's implementation for verifying identity attestations. It imports
|
|
4
|
+
[`@pvium/p2id-core`](https://www.npmjs.com/package/@pvium/p2id-core) for the protocol identity
|
|
5
|
+
types and hashing rules, then checks the attestation's zero-knowledge proof locally using a
|
|
6
|
+
bundled circuit verification key and configured trusted signing keys. `p2id-core` is installed
|
|
7
|
+
automatically as this package's runtime dependency.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
yarn add @pvium/p2id-verifier
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Node.js 20+.
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { IdentityType } from '@pvium/p2id-core';
|
|
21
|
+
import { verifyIdentity, AttestationSigner, shutdown } from '@pvium/p2id-verifier';
|
|
22
|
+
|
|
23
|
+
const result = await verifyIdentity({
|
|
24
|
+
attestation, // proof, public inputs and wallet
|
|
25
|
+
signer: AttestationSigner.Production, // trusted Privy signing keys
|
|
26
|
+
identityType: IdentityType.Email, // identity type from the original request
|
|
27
|
+
identityValue: 'you@example.com', // identity value from the original request
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (result.valid) {
|
|
31
|
+
console.log(result.wallet); // wallet linked to the email in the signed token
|
|
32
|
+
console.log(result.issuedAt); // token issue time, in Unix seconds
|
|
33
|
+
} else {
|
|
34
|
+
console.log(result.reason);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await shutdown(); // release the backend after verification is complete
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Pass the identity type and value from the original request. Verification checks these values
|
|
41
|
+
and the attestation's wallet against the proof.
|
|
42
|
+
|
|
43
|
+
## Obtain an attestation
|
|
44
|
+
|
|
45
|
+
JSON representation:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"proof": "<base64>",
|
|
50
|
+
"publicInputs": "<base64>",
|
|
51
|
+
"wallet": "0xA01b6E60D51eDB3fEB9f86a62b846f4F90070f98",
|
|
52
|
+
"circuitVersion": 1
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Pvium API or SDK:** recipient resolution can return an attestation summary. Fetch the full
|
|
57
|
+
attestation and pass it to `verifyIdentity` (see
|
|
58
|
+
[With the Pvium SDK](#with-the-pvium-sdk)).
|
|
59
|
+
|
|
60
|
+
**Self-hosted prover:** request an attestation from the
|
|
61
|
+
[prover service](https://github.com/pvium/zkid/tree/main/http-prover):
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
curl -s https://your-prover/attestations \
|
|
65
|
+
-H "authorization: Bearer $AUTH_TOKEN" -H 'content-type: application/json' \
|
|
66
|
+
-d '{ "identityType": "email", "identityValue": "you@example.com",
|
|
67
|
+
"wallet": "0xA01b6E60D51eDB3fEB9f86a62b846f4F90070f98", "jwt": "<Privy identity token>" }'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Pass the response to `verifyIdentity` with `signer` set to your Privy app's JWKS URL.
|
|
71
|
+
|
|
72
|
+
## Reading the result
|
|
73
|
+
|
|
74
|
+
| `result` | Meaning |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `{ valid: true, wallet, issuedAt }` | The signed token links the requested identity and wallet. `issuedAt` is the token's issue time. |
|
|
77
|
+
| `reason: 'identity value mismatch'` / `'identity type mismatch'` | The public inputs do not match the requested identity. |
|
|
78
|
+
| `reason: 'wallet mismatch'` | The wallet does not match the public inputs. |
|
|
79
|
+
| `reason: 'attestation carries no wallet'` | The public inputs contain no wallet commitment. |
|
|
80
|
+
| `reason: 'not signed by a trusted key'` | The signing key is not accepted by the configured `signer`. |
|
|
81
|
+
| `reason: 'invalid proof'` | The proof does not verify. |
|
|
82
|
+
| `reason: 'attestation is for circuit version N; …'` or `'… different circuit build …'` | The declared circuit version or verification-key hash differs from this release (see [Versioning](#versioning)). |
|
|
83
|
+
| `reason: 'malformed attestation: …'` | The public inputs could not be decoded. |
|
|
84
|
+
| `reason: 'unknown identity type "…"'` / `'unrecognised signer: …'` | The identity type or signer argument is unsupported. |
|
|
85
|
+
|
|
86
|
+
These failures return `{ valid: false, reason }`. Malformed input and backend initialization
|
|
87
|
+
errors can also throw; callers should handle rejected promises.
|
|
88
|
+
|
|
89
|
+
The package does not enforce an attestation age. Apply an application-specific limit to
|
|
90
|
+
`issuedAt`; this example uses 30 days:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const MAX_AGE = 30 * 24 * 3600;
|
|
94
|
+
if (!result.valid) throw new Error(result.reason);
|
|
95
|
+
const age = Date.now() / 1000 - result.issuedAt;
|
|
96
|
+
if (age < 0 || age > MAX_AGE) throw new Error('attestation outside the accepted time window');
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## In a server
|
|
100
|
+
|
|
101
|
+
Using Express 5:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import express from 'express';
|
|
105
|
+
import { verifyIdentity, AttestationSigner, IdentityType, shutdown } from '@pvium/p2id-verifier';
|
|
106
|
+
|
|
107
|
+
const app = express().use(express.json({ limit: '64kb' }));
|
|
108
|
+
|
|
109
|
+
app.post('/verify-identity', async (req, res) => {
|
|
110
|
+
const { email, attestation } = req.body;
|
|
111
|
+
const check = await verifyIdentity({ attestation, signer: AttestationSigner.Production, identityType: IdentityType.Email, identityValue: email });
|
|
112
|
+
res.status(check.valid ? 200 : 422).json(check);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
process.on('SIGTERM', () => shutdown());
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The Barretenberg backend initializes on first proof verification and is reused. Startup time
|
|
119
|
+
depends on the runtime and backend. Browser builds use WASM and workers.
|
|
120
|
+
|
|
121
|
+
## Reading a claim without verifying it
|
|
122
|
+
|
|
123
|
+
`decodeClaim` reads public inputs without checking the proof. Treat the decoded fields as
|
|
124
|
+
unverified until verification succeeds.
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { decodeClaim } from '@pvium/p2id-verifier';
|
|
128
|
+
|
|
129
|
+
const claim = decodeClaim(Buffer.from(attestation.publicInputs, 'base64'));
|
|
130
|
+
claim.identityType; // numeric identity type ID
|
|
131
|
+
claim.identityHash; // commitment to (type, value)
|
|
132
|
+
claim.wallet; // reported EVM wallet, or null
|
|
133
|
+
claim.iat; // reported token issue time
|
|
134
|
+
claim.signer; // { x, y }: reported P-256 signing key
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Trusted signers
|
|
138
|
+
|
|
139
|
+
`signer` selects the trusted token-signing keys.
|
|
140
|
+
|
|
141
|
+
| `signer` | Use |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `AttestationSigner.Production` (or `'production'`) | Pvium's production Privy keys, pinned in `PVIUM_ENVIRONMENTS.production.keys`. Unknown keys trigger a lookup against the app's JWKS. |
|
|
144
|
+
| `AttestationSigner.Sandbox` (or `'sandbox'`) | Pvium's sandbox Privy keys, with the same pinning and lookup behavior. |
|
|
145
|
+
| A JWKS URL string, or `{ jwksUrl }` | Accept P-256 keys from the specified JWKS. |
|
|
146
|
+
| PEM string or `{ x, y }` | Accept one specified P-256 key. |
|
|
147
|
+
|
|
148
|
+
`PVIUM_ENVIRONMENTS` exposes app IDs, JWKS URLs and pinned keys. Pinned keys remain trusted
|
|
149
|
+
until removed in a package release. JWKS responses are cached for ten minutes.
|
|
150
|
+
|
|
151
|
+
## With the Pvium SDK
|
|
152
|
+
|
|
153
|
+
Use `payout.getAttestation` to fetch a proof from a recipient's attestation summary. Verify it
|
|
154
|
+
against the identity in the original request:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { PviumSdk } from '@pvium/sdk';
|
|
158
|
+
import { verifyIdentity, AttestationSigner } from '@pvium/p2id-verifier';
|
|
159
|
+
|
|
160
|
+
const pvium = new PviumSdk({ apiKey: process.env.PVIUM_API_KEY!, environment: 'production' });
|
|
161
|
+
const recipient = { identityType: 'email' as const, identityValue: 'you@example.com' };
|
|
162
|
+
const { data } = await pvium.payout.resolveRecipients(batchId, [recipient]);
|
|
163
|
+
const summary = data.resolved[0]?.attestation;
|
|
164
|
+
if (!summary) throw new Error('attestation unavailable');
|
|
165
|
+
|
|
166
|
+
const attestation = await pvium.payout.getAttestation(summary);
|
|
167
|
+
const result = await verifyIdentity({
|
|
168
|
+
attestation,
|
|
169
|
+
signer: AttestationSigner.Production,
|
|
170
|
+
identityType: recipient.identityType,
|
|
171
|
+
identityValue: recipient.identityValue,
|
|
172
|
+
});
|
|
173
|
+
if (!result.valid) throw new Error(result.reason);
|
|
174
|
+
// Apply the freshness policy, then use result.wallet as the payment destination.
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
An absent attestation does not establish an identity-to-wallet binding. Wallet payees do not
|
|
178
|
+
require one; for identity payees, retry resolution when the proof is available.
|
|
179
|
+
|
|
180
|
+
## Exports
|
|
181
|
+
|
|
182
|
+
| Export | Purpose |
|
|
183
|
+
| --- | --- |
|
|
184
|
+
| `verifyIdentity(input)` | Verify an identity-to-wallet attestation |
|
|
185
|
+
| `AttestationSigner`, `PVIUM_ENVIRONMENTS` | Signer presets, app IDs, JWKS URLs and pinned keys |
|
|
186
|
+
| `decodeClaim(publicInputs)`, `toPublicInputFields`, `PUBLIC_INPUT_COUNT` | Decode and format public inputs without proof verification |
|
|
187
|
+
| `shutdown()` | Release the verification backend |
|
|
188
|
+
| `CIRCUIT_VERSION`, `VK_SHA256` | Supported circuit version and verification-key hash |
|
|
189
|
+
| `IdentityType`, `IdentityTypeName` | Identity types re-exported from `@pvium/p2id-core` |
|
|
190
|
+
|
|
191
|
+
Proof bytes and public inputs accept `Uint8Array` or base64 strings. Public inputs also accept
|
|
192
|
+
an array of hexadecimal fields.
|
|
193
|
+
|
|
194
|
+
## Versioning
|
|
195
|
+
|
|
196
|
+
Each release includes a verification key for one circuit version (`CIRCUIT_VERSION`).
|
|
197
|
+
`@aztec/bb.js` is pinned to the matching Barretenberg version (`5.0.0-nightly.20260522`).
|
|
198
|
+
Preserve `circuitVersion` and `vkHash` when passing attestations so mismatches can be reported
|
|
199
|
+
before proof verification. Circuit changes require a new package release; use the release
|
|
200
|
+
matching the attestation's circuit version and verification key.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { P256PublicKey } from './publicInputs.js';
|
|
2
|
+
/**
|
|
3
|
+
* Pvium environments. Each is a Privy app: attestations are made from tokens that app signed,
|
|
4
|
+
* so the trust anchor per environment is that app's signing keys.
|
|
5
|
+
*
|
|
6
|
+
* `keys` are the P-256 keys pinned at release time (from `jwksUrl`), so verification works with
|
|
7
|
+
* no network call. `jwksUrl` is the live source: if an attestation's signer is not among the
|
|
8
|
+
* pinned keys, the JWKS is fetched once (cached) to cover a key Privy rotated in after this
|
|
9
|
+
* release. Rotation *out* is deliberate: a pinned key stays trusted until an SDK release drops it.
|
|
10
|
+
*/
|
|
11
|
+
export interface PviumEnvironment {
|
|
12
|
+
name: 'production' | 'sandbox';
|
|
13
|
+
privyAppId: string;
|
|
14
|
+
jwksUrl: string;
|
|
15
|
+
keys: Array<P256PublicKey & {
|
|
16
|
+
kid: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
export declare const PVIUM_ENVIRONMENTS: Record<'production' | 'sandbox', PviumEnvironment>;
|
|
20
|
+
export type PviumEnvironmentName = keyof typeof PVIUM_ENVIRONMENTS;
|
|
21
|
+
/** Typed choice of trusted signer: a Pvium environment (keys pinned in the SDK). */
|
|
22
|
+
export declare enum AttestationSigner {
|
|
23
|
+
Production = "production",
|
|
24
|
+
Sandbox = "sandbox"
|
|
25
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const PVIUM_ENVIRONMENTS = {
|
|
2
|
+
production: {
|
|
3
|
+
name: 'production',
|
|
4
|
+
privyAppId: 'cmjzq9okg01kwjm0cq4b0xs74',
|
|
5
|
+
jwksUrl: 'https://auth.privy.io/api/v1/apps/cmjzq9okg01kwjm0cq4b0xs74/jwks.json',
|
|
6
|
+
keys: [
|
|
7
|
+
{
|
|
8
|
+
kid: 'gtxaE3pGoA51jLX-_Oeo75Zif0R3k-tBo7NsQpAy4RI',
|
|
9
|
+
x: 0x95b9d9d18fbed9f5c3b8aab466271793539be2cbc86a7090dd92f4df600850den,
|
|
10
|
+
y: 0x3acff34d3319d44a03afdd818474d3551e2b37ee2ff029ceac1ee74924eeb813n,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
kid: 'w43bEjqKgP4wGge2dXiLR18B83IgjT4rAjjDcXXd7E0',
|
|
14
|
+
x: 0xf638cd1c8ae040434c3325c1fdfb4d313496b9e4d266017266c803c4e474cadan,
|
|
15
|
+
y: 0x93d01d1f671d945cd44a46142a5c35e9b06c27bdac8d65d77c6996112509d029n,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
sandbox: {
|
|
20
|
+
name: 'sandbox',
|
|
21
|
+
privyAppId: 'cmhc6t92u001tju0cxkxg34on',
|
|
22
|
+
jwksUrl: 'https://auth.privy.io/api/v1/apps/cmhc6t92u001tju0cxkxg34on/jwks.json',
|
|
23
|
+
keys: [
|
|
24
|
+
{
|
|
25
|
+
kid: 'Lp_q4NY6tg5jSdiD790AIm01JR7GIJ_xmDVZZ6MVAfM',
|
|
26
|
+
x: 0x487140ed4fa91d71559b5e015286c9b76c270883f59a5ae9febac9b291584cd1n,
|
|
27
|
+
y: 0xd4cf5625205d28cbe6c8108a695929208c08f3bdc7790ab7023abe17dcdefc04n,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
kid: '1NtmKuIrWxOltnhgo5yNIyuFFmi-cQDW1Ucqv2185Og',
|
|
31
|
+
x: 0x84e87db1d90fb3ef5079e6fbe8a50e8f2aee7f8ad66c13b837cc93e016e2b9aan,
|
|
32
|
+
y: 0x27a0949bf003bb64d5a622a05737d2547efa8980267daf35cc75276cb30c9de5n,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/** Typed choice of trusted signer: a Pvium environment (keys pinned in the SDK). */
|
|
38
|
+
export var AttestationSigner;
|
|
39
|
+
(function (AttestationSigner) {
|
|
40
|
+
AttestationSigner["Production"] = "production";
|
|
41
|
+
AttestationSigner["Sandbox"] = "sandbox";
|
|
42
|
+
})(AttestationSigner || (AttestationSigner = {}));
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { verifyIdentity } from './verifyIdentity.js';
|
|
2
|
+
export type { Attestation, Signer, VerifyIdentityInput, VerifyIdentityResult } from './verifyIdentity.js';
|
|
3
|
+
export { decodeClaim, toPublicInputFields, PUBLIC_INPUT_COUNT } from './publicInputs.js';
|
|
4
|
+
export type { IdentityClaim, P256PublicKey, PublicInputs } from './publicInputs.js';
|
|
5
|
+
export { PVIUM_ENVIRONMENTS, AttestationSigner } from './environments.js';
|
|
6
|
+
export type { PviumEnvironment, PviumEnvironmentName } from './environments.js';
|
|
7
|
+
export { shutdown, VerifierUnavailableError } from './verify.js';
|
|
8
|
+
export { VK_SHA256, CIRCUIT_VERSION } from './vk.js';
|
|
9
|
+
export { IdentityType } from '@pvium/p2id-core';
|
|
10
|
+
export type { IdentityTypeName } from '@pvium/p2id-core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Verify Pvium identity attestations off-chain. Address derivation and identity hashing are in
|
|
2
|
+
// @pvium/p2id-core; the identity types are re-exported here for convenience.
|
|
3
|
+
export { verifyIdentity } from './verifyIdentity.js';
|
|
4
|
+
export { decodeClaim, toPublicInputFields, PUBLIC_INPUT_COUNT } from './publicInputs.js';
|
|
5
|
+
export { PVIUM_ENVIRONMENTS, AttestationSigner } from './environments.js';
|
|
6
|
+
export { shutdown, VerifierUnavailableError } from './verify.js';
|
|
7
|
+
export { VK_SHA256, CIRCUIT_VERSION } from './vk.js';
|
|
8
|
+
export { IdentityType } from '@pvium/p2id-core';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Number of public inputs the circuit emits: 2 inputs + 9 outputs. */
|
|
2
|
+
export declare const PUBLIC_INPUT_COUNT = 11;
|
|
3
|
+
/** Positions in the public input array (see circuit/src/main.nr). */
|
|
4
|
+
export declare const PI: {
|
|
5
|
+
readonly identityType: 0;
|
|
6
|
+
readonly wallet: 1;
|
|
7
|
+
readonly signerXHi: 2;
|
|
8
|
+
readonly signerXLo: 3;
|
|
9
|
+
readonly signerYHi: 4;
|
|
10
|
+
readonly signerYLo: 5;
|
|
11
|
+
readonly iat: 6;
|
|
12
|
+
readonly identityHashHi: 7;
|
|
13
|
+
readonly identityHashLo: 8;
|
|
14
|
+
readonly walletHashHi: 9;
|
|
15
|
+
readonly walletHashLo: 10;
|
|
16
|
+
};
|
|
17
|
+
export interface P256PublicKey {
|
|
18
|
+
x: bigint;
|
|
19
|
+
y: bigint;
|
|
20
|
+
}
|
|
21
|
+
/** What a proof asserts, decoded from its public inputs. */
|
|
22
|
+
export interface IdentityClaim {
|
|
23
|
+
identityType: number;
|
|
24
|
+
/**
|
|
25
|
+
* EVM address of the wallet linked in the same token, lowercase hex, checked in-circuit
|
|
26
|
+
* against the address read from the token. Null for a non-EVM wallet or no wallet.
|
|
27
|
+
*/
|
|
28
|
+
wallet: `0x${string}` | null;
|
|
29
|
+
/** P-256 public key the token was signed with. */
|
|
30
|
+
signer: P256PublicKey;
|
|
31
|
+
/**
|
|
32
|
+
* When Privy issued the token this proof was made from, unix seconds. This is the attestation
|
|
33
|
+
* time; whether it is fresh enough is the caller's policy.
|
|
34
|
+
*/
|
|
35
|
+
iat: number;
|
|
36
|
+
/** sha256(prefix || type || normalize(value)); see identityHash(). */
|
|
37
|
+
identityHash: `0x${string}`;
|
|
38
|
+
/**
|
|
39
|
+
* identityHash(IdentityType.Wallet, address) for a wallet linked in the same token, proving
|
|
40
|
+
* the identity and the wallet belong to the same Privy user. Null when the proof has no wallet.
|
|
41
|
+
*/
|
|
42
|
+
walletHash: `0x${string}` | null;
|
|
43
|
+
}
|
|
44
|
+
/** Accepts the raw 320-byte `public_inputs` file or an array of 32-byte hex fields. */
|
|
45
|
+
export type PublicInputs = Uint8Array | readonly string[];
|
|
46
|
+
/** Normalise public inputs to the `string[]` form bb.js expects (0x-prefixed, 32 bytes each). */
|
|
47
|
+
export declare function toPublicInputFields(inputs: PublicInputs): string[];
|
|
48
|
+
/** Decode the claim carried by a proof's public inputs. Does not verify anything. */
|
|
49
|
+
export declare function decodeClaim(inputs: PublicInputs): IdentityClaim;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { toHex } from '@pvium/p2id-core';
|
|
2
|
+
/** Number of public inputs the circuit emits: 2 inputs + 9 outputs. */
|
|
3
|
+
export const PUBLIC_INPUT_COUNT = 11;
|
|
4
|
+
/** Positions in the public input array (see circuit/src/main.nr). */
|
|
5
|
+
export const PI = {
|
|
6
|
+
identityType: 0,
|
|
7
|
+
wallet: 1,
|
|
8
|
+
signerXHi: 2,
|
|
9
|
+
signerXLo: 3,
|
|
10
|
+
signerYHi: 4,
|
|
11
|
+
signerYLo: 5,
|
|
12
|
+
iat: 6,
|
|
13
|
+
identityHashHi: 7,
|
|
14
|
+
identityHashLo: 8,
|
|
15
|
+
walletHashHi: 9,
|
|
16
|
+
walletHashLo: 10,
|
|
17
|
+
};
|
|
18
|
+
/** Normalise public inputs to the `string[]` form bb.js expects (0x-prefixed, 32 bytes each). */
|
|
19
|
+
export function toPublicInputFields(inputs) {
|
|
20
|
+
if (inputs instanceof Uint8Array) {
|
|
21
|
+
if (inputs.length !== PUBLIC_INPUT_COUNT * 32) {
|
|
22
|
+
throw new Error(`expected ${PUBLIC_INPUT_COUNT * 32} bytes of public inputs, got ${inputs.length}`);
|
|
23
|
+
}
|
|
24
|
+
const out = [];
|
|
25
|
+
for (let i = 0; i < inputs.length; i += 32)
|
|
26
|
+
out.push(toHex(inputs.subarray(i, i + 32)));
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
if (inputs.length !== PUBLIC_INPUT_COUNT) {
|
|
30
|
+
throw new Error(`expected ${PUBLIC_INPUT_COUNT} public inputs, got ${inputs.length}`);
|
|
31
|
+
}
|
|
32
|
+
return inputs.map((f) => {
|
|
33
|
+
const h = f.startsWith('0x') ? f.slice(2) : f;
|
|
34
|
+
if (!/^[0-9a-fA-F]{1,64}$/.test(h))
|
|
35
|
+
throw new Error(`bad public input field: ${f}`);
|
|
36
|
+
return `0x${h.toLowerCase().padStart(64, '0')}`;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** Decode the claim carried by a proof's public inputs. Does not verify anything. */
|
|
40
|
+
export function decodeClaim(inputs) {
|
|
41
|
+
const f = toPublicInputFields(inputs).map((h) => BigInt(h));
|
|
42
|
+
const join = (hi, lo) => (hi << 128n) | lo;
|
|
43
|
+
const hex32 = (v) => `0x${v.toString(16).padStart(64, '0')}`;
|
|
44
|
+
return {
|
|
45
|
+
identityType: Number(f[PI.identityType]),
|
|
46
|
+
wallet: f[PI.wallet] === 0n ? null : `0x${f[PI.wallet].toString(16).padStart(40, '0')}`,
|
|
47
|
+
signer: { x: join(f[PI.signerXHi], f[PI.signerXLo]), y: join(f[PI.signerYHi], f[PI.signerYLo]) },
|
|
48
|
+
iat: Number(f[PI.iat]),
|
|
49
|
+
identityHash: hex32(join(f[PI.identityHashHi], f[PI.identityHashLo])),
|
|
50
|
+
walletHash: (() => {
|
|
51
|
+
const w = join(f[PI.walletHashHi], f[PI.walletHashLo]);
|
|
52
|
+
return w === 0n ? null : hex32(w);
|
|
53
|
+
})(),
|
|
54
|
+
};
|
|
55
|
+
}
|
package/dist/signer.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { P256PublicKey } from './publicInputs.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a P-256 public key from a SubjectPublicKeyInfo PEM (what Privy's dashboard shows as the
|
|
4
|
+
* app verification key). The DER ends with the 65-byte uncompressed point 0x04 || x || y, so no
|
|
5
|
+
* ASN.1 parser is needed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseP256PublicKeyPem(pem: string): P256PublicKey;
|
package/dist/signer.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a P-256 public key from a SubjectPublicKeyInfo PEM (what Privy's dashboard shows as the
|
|
3
|
+
* app verification key). The DER ends with the 65-byte uncompressed point 0x04 || x || y, so no
|
|
4
|
+
* ASN.1 parser is needed.
|
|
5
|
+
*/
|
|
6
|
+
export function parseP256PublicKeyPem(pem) {
|
|
7
|
+
const body = pem
|
|
8
|
+
.split('\n')
|
|
9
|
+
.filter((l) => !l.startsWith('-----'))
|
|
10
|
+
.join('')
|
|
11
|
+
.trim();
|
|
12
|
+
const der = Uint8Array.from(atob(body), (c) => c.charCodeAt(0));
|
|
13
|
+
if (der.length < 65 || der[der.length - 65] !== 0x04) {
|
|
14
|
+
throw new Error('expected an uncompressed P-256 public key in SPKI PEM');
|
|
15
|
+
}
|
|
16
|
+
const point = der.subarray(der.length - 65);
|
|
17
|
+
return { x: bytesToBigInt(point.subarray(1, 33)), y: bytesToBigInt(point.subarray(33, 65)) };
|
|
18
|
+
}
|
|
19
|
+
function bytesToBigInt(b) {
|
|
20
|
+
let v = 0n;
|
|
21
|
+
for (const x of b)
|
|
22
|
+
v = (v << 8n) | BigInt(x);
|
|
23
|
+
return v;
|
|
24
|
+
}
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type PublicInputs } from './publicInputs.js';
|
|
2
|
+
export interface ProofBundle {
|
|
3
|
+
/** Raw proof bytes as produced by `bb prove -t evm` (or bb.js with verifierTarget 'evm'). */
|
|
4
|
+
proof: Uint8Array;
|
|
5
|
+
publicInputs: PublicInputs;
|
|
6
|
+
}
|
|
7
|
+
/** The circuit's UltraHonk verification key (EVM / keccak transcript), bundled with the package. */
|
|
8
|
+
export declare function verificationKey(): Uint8Array;
|
|
9
|
+
/** Release the Barretenberg WASM instance. Call when your process is done verifying. */
|
|
10
|
+
export declare function shutdown(): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Cryptographic check only: is this a valid proof for the Pvium identity circuit with these
|
|
13
|
+
* public inputs? Says nothing about who signed the token or whether it has expired.
|
|
14
|
+
*/
|
|
15
|
+
export declare function verifyProof(bundle: ProofBundle): Promise<boolean>;
|
|
16
|
+
/** The proof could not be checked at all: the Barretenberg WASM backend failed in this environment. */
|
|
17
|
+
export declare class VerifierUnavailableError extends Error {
|
|
18
|
+
constructor(cause: unknown);
|
|
19
|
+
}
|
package/dist/verify.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Barretenberg, UltraHonkVerifierBackend } from '@aztec/bb.js';
|
|
2
|
+
import { VK_BASE64 } from './vk.js';
|
|
3
|
+
import { toPublicInputFields } from './publicInputs.js';
|
|
4
|
+
let vkCache;
|
|
5
|
+
/** The circuit's UltraHonk verification key (EVM / keccak transcript), bundled with the package. */
|
|
6
|
+
export function verificationKey() {
|
|
7
|
+
if (!vkCache)
|
|
8
|
+
vkCache = Uint8Array.from(atob(VK_BASE64), (c) => c.charCodeAt(0));
|
|
9
|
+
return vkCache;
|
|
10
|
+
}
|
|
11
|
+
let apiPromise;
|
|
12
|
+
async function backend() {
|
|
13
|
+
apiPromise ??= Barretenberg.new({ threads: 1 });
|
|
14
|
+
return new UltraHonkVerifierBackend(await apiPromise);
|
|
15
|
+
}
|
|
16
|
+
/** Release the Barretenberg WASM instance. Call when your process is done verifying. */
|
|
17
|
+
export async function shutdown() {
|
|
18
|
+
if (apiPromise) {
|
|
19
|
+
const api = await apiPromise;
|
|
20
|
+
apiPromise = undefined;
|
|
21
|
+
await api.destroy();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Cryptographic check only: is this a valid proof for the Pvium identity circuit with these
|
|
26
|
+
* public inputs? Says nothing about who signed the token or whether it has expired.
|
|
27
|
+
*/
|
|
28
|
+
export async function verifyProof(bundle) {
|
|
29
|
+
const publicInputs = toPublicInputFields(bundle.publicInputs);
|
|
30
|
+
let v;
|
|
31
|
+
try {
|
|
32
|
+
v = await backend();
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
throw new VerifierUnavailableError(e);
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
return await v.verifyProof({ proof: bundle.proof, publicInputs, verificationKey: verificationKey() }, { verifierTarget: 'evm' });
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// bb.js throws on a malformed or tampered proof (e.g. a point that is not on the curve)
|
|
42
|
+
// as well as returning false: either way the proof did not verify. Environment failures
|
|
43
|
+
// (WASM cannot load, no threads or shared memory) surface when the backend is created above.
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** The proof could not be checked at all: the Barretenberg WASM backend failed in this environment. */
|
|
48
|
+
export class VerifierUnavailableError extends Error {
|
|
49
|
+
constructor(cause) {
|
|
50
|
+
super(`proof verifier unavailable in this environment: ${cause?.message ?? String(cause)}`);
|
|
51
|
+
this.name = 'VerifierUnavailableError';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { IdentityType, type IdentityTypeName } from '@pvium/p2id-core';
|
|
2
|
+
import { type P256PublicKey, type PublicInputs } from './publicInputs.js';
|
|
3
|
+
import { AttestationSigner, type PviumEnvironmentName } from './environments.js';
|
|
4
|
+
/** What Pvium's resolution API returns for an identity: the proof plus the address it binds. */
|
|
5
|
+
export interface Attestation {
|
|
6
|
+
/** Proof bytes, or base64 of them. */
|
|
7
|
+
proof: Uint8Array | string;
|
|
8
|
+
/** Public inputs: raw bytes, base64 of them, or the hex fields. */
|
|
9
|
+
publicInputs: PublicInputs | string;
|
|
10
|
+
/** The wallet address the proof binds to the identity, exactly as linked in Privy. */
|
|
11
|
+
wallet: string;
|
|
12
|
+
/** Circuit version that produced the proof, as reported by the prover. Checked when present. */
|
|
13
|
+
circuitVersion?: number;
|
|
14
|
+
vkHash?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Who is trusted to have signed the token behind an attestation.
|
|
18
|
+
*
|
|
19
|
+
* - `AttestationSigner.Production` / `AttestationSigner.Sandbox` (or the strings `'production'` /
|
|
20
|
+
* `'sandbox'`): Pvium's Privy apps, keys pinned in the SDK, JWKS consulted only for a key
|
|
21
|
+
* rotated in after this release (see environments.ts).
|
|
22
|
+
* - a JWKS URL (`https://…/jwks.json`) or `{ jwksUrl }`: any JWKS; every P-256 key it serves
|
|
23
|
+
* is accepted. For self-hosted provers under your own Privy app.
|
|
24
|
+
* - a SPKI PEM string, or raw `{ x, y }`: one specific key.
|
|
25
|
+
*/
|
|
26
|
+
export type Signer = AttestationSigner | PviumEnvironmentName | string | {
|
|
27
|
+
jwksUrl: string;
|
|
28
|
+
} | P256PublicKey;
|
|
29
|
+
export interface VerifyIdentityInput {
|
|
30
|
+
attestation: Attestation;
|
|
31
|
+
signer: Signer;
|
|
32
|
+
identityType: IdentityType | IdentityTypeName;
|
|
33
|
+
identityValue: string;
|
|
34
|
+
}
|
|
35
|
+
export type VerifyIdentityResult = {
|
|
36
|
+
valid: true;
|
|
37
|
+
/** The wallet address, now proven to be linked to the identity in the same Privy account. */
|
|
38
|
+
wallet: string;
|
|
39
|
+
/** When Privy issued the token the proof was made from (unix seconds). Freshness is your policy. */
|
|
40
|
+
issuedAt: number;
|
|
41
|
+
} | {
|
|
42
|
+
valid: false;
|
|
43
|
+
reason: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Does this attestation prove that `identityValue` (of `identityType`) and `attestation.wallet`
|
|
47
|
+
* belong to the same Privy user, signed by `signer`? On success the wallet is returned as a
|
|
48
|
+
* trusted value together with the attestation time.
|
|
49
|
+
*/
|
|
50
|
+
export declare function verifyIdentity(input: VerifyIdentityInput): Promise<VerifyIdentityResult>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { IdentityType, identityHash, resolveIdentityType } from '@pvium/p2id-core';
|
|
2
|
+
import { decodeClaim } from './publicInputs.js';
|
|
3
|
+
import { parseP256PublicKeyPem } from './signer.js';
|
|
4
|
+
import { verifyProof } from './verify.js';
|
|
5
|
+
import { PVIUM_ENVIRONMENTS } from './environments.js';
|
|
6
|
+
import { CIRCUIT_VERSION, VK_SHA256 } from './vk.js';
|
|
7
|
+
/**
|
|
8
|
+
* Does this attestation prove that `identityValue` (of `identityType`) and `attestation.wallet`
|
|
9
|
+
* belong to the same Privy user, signed by `signer`? On success the wallet is returned as a
|
|
10
|
+
* trusted value together with the attestation time.
|
|
11
|
+
*/
|
|
12
|
+
export async function verifyIdentity(input) {
|
|
13
|
+
let type;
|
|
14
|
+
try {
|
|
15
|
+
type = resolveIdentityType(input.identityType);
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
return { valid: false, reason: e.message };
|
|
19
|
+
}
|
|
20
|
+
const att = input.attestation;
|
|
21
|
+
if (att.circuitVersion !== undefined && att.circuitVersion !== CIRCUIT_VERSION) {
|
|
22
|
+
return { valid: false, reason: `attestation is for circuit version ${att.circuitVersion}; this SDK verifies version ${CIRCUIT_VERSION}` };
|
|
23
|
+
}
|
|
24
|
+
if (att.vkHash !== undefined && att.vkHash.toLowerCase() !== VK_SHA256) {
|
|
25
|
+
return { valid: false, reason: 'attestation was produced by a different circuit build than this SDK verifies' };
|
|
26
|
+
}
|
|
27
|
+
const proof = typeof input.attestation.proof === 'string' ? fromBase64(input.attestation.proof) : input.attestation.proof;
|
|
28
|
+
const publicInputs = typeof input.attestation.publicInputs === 'string' ? fromBase64(input.attestation.publicInputs) : input.attestation.publicInputs;
|
|
29
|
+
let claim;
|
|
30
|
+
try {
|
|
31
|
+
claim = decodeClaim(publicInputs);
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
return { valid: false, reason: `malformed attestation: ${e.message}` };
|
|
35
|
+
}
|
|
36
|
+
let trusted;
|
|
37
|
+
try {
|
|
38
|
+
trusted = await trustedKeys(input.signer, claim.signer);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
return { valid: false, reason: e.message };
|
|
42
|
+
}
|
|
43
|
+
if (!trusted.some((k) => k.x === claim.signer.x && k.y === claim.signer.y)) {
|
|
44
|
+
return { valid: false, reason: 'not signed by a trusted key' };
|
|
45
|
+
}
|
|
46
|
+
if (claim.identityType !== type)
|
|
47
|
+
return { valid: false, reason: 'identity type mismatch' };
|
|
48
|
+
if (identityHash(type, input.identityValue) !== claim.identityHash) {
|
|
49
|
+
return { valid: false, reason: 'identity value mismatch' };
|
|
50
|
+
}
|
|
51
|
+
if (claim.walletHash === null)
|
|
52
|
+
return { valid: false, reason: 'attestation carries no wallet' };
|
|
53
|
+
if (identityHash(IdentityType.Wallet, input.attestation.wallet) !== claim.walletHash) {
|
|
54
|
+
return { valid: false, reason: 'wallet mismatch' };
|
|
55
|
+
}
|
|
56
|
+
// For an EVM wallet the circuit also exposes the address it read from the token; it must agree.
|
|
57
|
+
const w = input.attestation.wallet;
|
|
58
|
+
if (w.toLowerCase().startsWith('0x') && claim.wallet !== w.toLowerCase()) {
|
|
59
|
+
return { valid: false, reason: 'wallet mismatch' };
|
|
60
|
+
}
|
|
61
|
+
let proofOk;
|
|
62
|
+
try {
|
|
63
|
+
proofOk = await verifyProof({ proof, publicInputs });
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
return { valid: false, reason: e.message }; // the backend failed; says so, not "invalid proof"
|
|
67
|
+
}
|
|
68
|
+
if (!proofOk)
|
|
69
|
+
return { valid: false, reason: 'invalid proof' };
|
|
70
|
+
return { valid: true, wallet: input.attestation.wallet, issuedAt: claim.iat };
|
|
71
|
+
}
|
|
72
|
+
async function trustedKeys(signer, claimSigner) {
|
|
73
|
+
if (typeof signer === 'string') {
|
|
74
|
+
if (signer in PVIUM_ENVIRONMENTS) {
|
|
75
|
+
const env = PVIUM_ENVIRONMENTS[signer];
|
|
76
|
+
const pinned = env.keys;
|
|
77
|
+
// Pinned keys answer without a network call; fall back to the live JWKS only when the
|
|
78
|
+
// attestation names a key we do not know (rotated in after this SDK release).
|
|
79
|
+
if (!claimSigner || pinned.some((k) => k.x === claimSigner.x && k.y === claimSigner.y))
|
|
80
|
+
return pinned;
|
|
81
|
+
try {
|
|
82
|
+
return [...pinned, ...(await fetchJwks(env.jwksUrl))];
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return pinned;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (/^https?:\/\//i.test(signer))
|
|
89
|
+
return fetchJwks(signer);
|
|
90
|
+
if (signer.includes('-----BEGIN'))
|
|
91
|
+
return [parseP256PublicKeyPem(signer)];
|
|
92
|
+
throw new Error(`unrecognised signer: expected AttestationSigner.Production/Sandbox, a JWKS URL, a PEM, or {x, y}`);
|
|
93
|
+
}
|
|
94
|
+
if ('jwksUrl' in signer)
|
|
95
|
+
return fetchJwks(signer.jwksUrl);
|
|
96
|
+
return [signer];
|
|
97
|
+
}
|
|
98
|
+
const jwksCache = new Map();
|
|
99
|
+
const JWKS_TTL_MS = 10 * 60 * 1000;
|
|
100
|
+
async function fetchJwks(url) {
|
|
101
|
+
const hit = jwksCache.get(url);
|
|
102
|
+
if (hit && Date.now() - hit.at < JWKS_TTL_MS)
|
|
103
|
+
return hit.keys;
|
|
104
|
+
const res = await fetch(url);
|
|
105
|
+
if (!res.ok)
|
|
106
|
+
throw new Error(`JWKS fetch failed: ${res.status}`);
|
|
107
|
+
const body = (await res.json());
|
|
108
|
+
const keys = (body.keys ?? [])
|
|
109
|
+
.filter((k) => k.kty === 'EC' && k.crv === 'P-256' && k.x && k.y)
|
|
110
|
+
.map((k) => ({ x: b64urlToBigInt(k.x), y: b64urlToBigInt(k.y) }));
|
|
111
|
+
if (keys.length === 0)
|
|
112
|
+
throw new Error('JWKS has no P-256 keys');
|
|
113
|
+
jwksCache.set(url, { at: Date.now(), keys });
|
|
114
|
+
return keys;
|
|
115
|
+
}
|
|
116
|
+
function fromBase64(s) {
|
|
117
|
+
const std = s.replace(/-/g, '+').replace(/_/g, '/');
|
|
118
|
+
return Uint8Array.from(atob(std.padEnd(std.length + ((4 - (std.length % 4)) % 4), '=')), (c) => c.charCodeAt(0));
|
|
119
|
+
}
|
|
120
|
+
function b64urlToBigInt(s) {
|
|
121
|
+
let v = 0n;
|
|
122
|
+
for (const b of fromBase64(s))
|
|
123
|
+
v = (v << 8n) | BigInt(b);
|
|
124
|
+
return v;
|
|
125
|
+
}
|
package/dist/vk.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** UltraHonk verification key for circuit/src/main.nr (keccak / EVM transcript). */
|
|
2
|
+
export declare const VK_BASE64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFK5zXF15qgIWxKqH++n94C0tZrqo9d617DIAlbbFl3AoZvXeX9VqtfnDcJpUPOYMyAU60SkPXtNOsy3Py+4HnKh1hmPKJIn3Bgyl/i/y0L4xmq/jnhiYNKeCjYhiceUqMF8mf72zAl+fvbtu1NgLYlzwqA3lniVuvxv53HIhXZ7YMD70rFGCgsLzLYtRSf5FSOyFnaQ6Yl2FEIWw0Ld/VSxFUTa8bzczXTVXXy4Qwi2f4GLVWX7WIWWDsXwgujcggKNvuOTl5etGw9JRyYBIKlglbxvii78J9xOfG9bB7ipcvduuZmh8QvFygxaLKlE4tlXWFmmYLBoo1pM12IlisEw24IjqIi0IkD5UJB1XJhn7DhPU+onAYA4pZMazlM/azCkd4NqFkMc03rPB1DjThGUwJMy1IMjkMet4vsSZHMxEgZby5ck+e6VKkGkHjl31hsZIAbt3cAMF3LhEsG8A+SBkBaJB7fVjIzKlb8jXKUQs1XJUa3suPu3MnJnzPUt9CHyHZ9HJZ1VBx8h4KYAzOEb5QtiYem5Ef/axvWfYhXisnRfX24bUURRUoQK3rBJXCQrcRhdZNf7zegX/u7WeZPC+knjRIVFLEd+MZzguYGVdCtL2zoPdaNfH5hxH7ZVLVBi1cFBBRhoTz6Ft+DXXSKDeN8sXMU98QbqcXDeNuKsIL6gz7aNyPgfLCRKY0QxnLJ++49xCaO6HcULo0X2qhXx/qnV0e2hKaQOVjRuA2e8tG6C+uOoR+61lcSniTBRpsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2Ehu/aLL+WzukcV5yAnz0fQEuC2LnFF9Nd3Lz66iDxF3Ip1cq8sW9efKmlb82RKzRcmNniuJooPgVWsYVwO98Eo2Fn9+H6A7LTtfhfUt3f7y7W7R6J6rnG1qeAejDBpRHFGBt/Xc6oDUxE/OSgZcaO0mBJ6S9U4zfPWcaS+EpMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAllEQ5xXBttNS/F9DNA/cb4Cr9kfm/Ejk5mx3BpEV8wBoV8wWDWQc0P3PABe1WrE64g9D1LD+9CfqalJjrUQ5iL+0tZrnZfmoQHSjG03k2oBMQO8ZDm23t7qBUdsKnh0MW7Oa6fdX026TAQoUsGGDWXP1C4qulluBW6gtYYuyq3QKYI8wrTeVG1ygWJzjCP+X4VlTy8mISbDjB9EqU9hWfH4+ktXV1EZLn5GNtOoBMVz8INbwNWZWHtGZdfBbt3BMVvTwB6mV+1TMvrH9dJJ7Sq5X/dhKZj6Ltl6GOGRGushvFHC1PwBT0szR9oQv3coz55bu+SgP7+Ott7xir05dDJtZ8Jy3vAScIK+d8MeCZqfmBGjVxK2uxsNhqnai6hoIjk1QABtTfgznBoBiJzUNphplSkXgTguWc5TY8nZHWDBLm/i342fW2SBf6qPET2yfeFOgAodwv83tIOP0YAjd3AYtYdo2v4rKMrlQxazDOKKcPfSV9I7P4XZDDaILp+REmPHUybNif3QD7RDpLF7Mu6ILUJ9ElS9tl3c7iqwWaSR/58AbuLqlcbIsAO29/Fgfgb1I05mbQ5h+3gKrTbtheAzZlD22xukek7qdw1XR2pCb8NrfDNYLpTfz8xq2P3SwQEWwHk5B9rWZITRwHHcRiEzgSxVYqr6NFKhpW7OJ3jgMD8vrp1Ca8rPA6MyTzZauFpWrA3hI5jVKnklrQmnk0IARG1gPuAaSqTdGu6gY6wwLE1vreQhLKBDz/ta2xcJMvqogOIYFvboT5pjxpvS9kHM9Go8BG7fW/2NqURarvAht5rZd40F3tRfliZT3YshQ6/1yWKmD7hlY152/Sa+0oH4kOiBLMNjcrxvLgL3LYJsZzWzi2L08ZR2r7sUi0YSQpEYC6wwepo1DtMm8PjCPcyO+7Lys3wAzebQ8qDIuruBI24CEjlbJs2Fd3E2AIzEyvAGYDfHArMBvssODDXXf0K9JvWztA1Kek2Cd2k/SH1Fi9X3hDB+k7M95t4CbbE2YqVs5B9rC+E7nCZ0diG4Ie7oGyOoh/KZBJsUwR6YRg1hqpjy3j3dpUfY9t5Ocl3tWCfWM4x4ZWwNEsoa6m7yx8FCMjRMJkjlZNuG3XL4TLVDUSDvtLoAldolNDx1/jDdwkIG4f2OZLow6xxvcgdI/QLvjZFalpWW+uvsW5i9BhTA==";
|
|
3
|
+
/** sha256 of the raw vk bytes, for sanity-checking against the deployed verifier's build. */
|
|
4
|
+
export declare const VK_SHA256 = "0x1c799113fea7ad5b9814605a3cda138e441cee6778ff29d62940e61ebcef822b";
|
|
5
|
+
/** The circuit version this SDK release verifies. Attestations from other versions are rejected. */
|
|
6
|
+
export declare const CIRCUIT_VERSION = 1;
|
package/dist/vk.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// GENERATED by scripts/embed-vk.mjs — do not edit.
|
|
2
|
+
/** UltraHonk verification key for circuit/src/main.nr (keccak / EVM transcript). */
|
|
3
|
+
export const VK_BASE64 = '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';
|
|
4
|
+
/** sha256 of the raw vk bytes, for sanity-checking against the deployed verifier's build. */
|
|
5
|
+
export const VK_SHA256 = '0x1c799113fea7ad5b9814605a3cda138e441cee6778ff29d62940e61ebcef822b';
|
|
6
|
+
/** The circuit version this SDK release verifies. Attestations from other versions are rejected. */
|
|
7
|
+
export const CIRCUIT_VERSION = 1;
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pvium/p2id-verifier",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pvium's off-chain verifier for P2ID identity attestations (Noir/UltraHonk zero-knowledge proofs)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "node scripts/embed-vk.mjs && tsc -p tsconfig.json",
|
|
22
|
+
"test": "yarn build && node --test --import tsx test/*.test.ts",
|
|
23
|
+
"sync": "sh scripts/sync-circuit.sh",
|
|
24
|
+
"prepublishOnly": "yarn test"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@aztec/bb.js": "5.0.0-nightly.20260522",
|
|
28
|
+
"@pvium/p2id-core": "^0.1.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"tsx": "^4.19.0",
|
|
33
|
+
"typescript": "^5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=20"
|
|
37
|
+
}
|
|
38
|
+
}
|