@zkpassport/sdk 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 ADDED
@@ -0,0 +1,141 @@
1
+ # zkpassport-sdk
2
+
3
+ ## Installation
4
+
5
+ ```
6
+ npm install https://github.com/zkpassport/zkpassport-sdk.git
7
+ ```
8
+
9
+ ## How to use
10
+
11
+ ```ts
12
+ import { ZkPassport } from 'zkpassport-sdk'
13
+
14
+ // Replace with your domain
15
+ const zkPassport = new ZkPassport('demo.zkpassport.id')
16
+
17
+ // Specify your app name, logo and the purpose of the request
18
+ // you'll send to your visitors or users
19
+ const queryBuilder = await zkPassport.request({
20
+ name: 'ZKpassport',
21
+ logo: 'https://zkpassport.id/logo.png',
22
+ purpose: 'Proof of country and first name',
23
+ })
24
+
25
+ // Specify the data you want to disclose
26
+ // Then you can call the `done` method to get the url and the callbacks to follow the progress
27
+ // and get back the result along with the proof
28
+ const { url, requestId, onQRCodeScanned, onGeneratingProof, onProofGenerated, onReject, onError } = queryBuilder
29
+ .disclose('nationality')
30
+ .disclose('firstname')
31
+ .done()
32
+
33
+ // Generate a url with the url and let your user scan it
34
+ // or transform it into a button if the user is on mobile
35
+
36
+ onQRCodeScanned(() => {
37
+ // The user scanned the QR code or clicked the button
38
+ // Essentially, this means the request popup is now opened
39
+ // on the user phone
40
+ console.log('QR code scanned')
41
+ })
42
+
43
+ onGeneratingProof(() => {
44
+ // The user accepted the request and the proof is being generated
45
+ console.log('Generating proof')
46
+ })
47
+
48
+ onProofGenerated((result: ProofResult) => {
49
+ // The proof has been generated
50
+ // You can retrieve the proof, the verification key and the result of your query
51
+ // Note: the verify function will soon be added to the SDK so you can verify the proof
52
+ // directly
53
+ console.log('Proof', result.proof)
54
+ console.log('Verification key', result.verificationKey)
55
+ console.log('Query result', result.queryResult)
56
+ console.log('firstname', result.queryResult.firstname.disclose.result)
57
+ console.log('nationality', result.queryResult.nationality.disclose.result)
58
+ })
59
+ ```
60
+ ### Using with Next.js
61
+
62
+ You can integrate `zkpassport-sdk` into a Next.js application by creating a backend API route and calling it from your frontend.
63
+
64
+ #### **Backend (API Route)**
65
+
66
+ **App Router:** `app/api/zkpassport/route.ts`
67
+ ```typescript
68
+ import { NextResponse } from 'next/server';
69
+ import { ZkPassport } from 'zkpassport-sdk';
70
+
71
+ export async function GET() {
72
+ const zkPassport = new ZkPassport('demo.zkpassport.id'); // Replace with your domain
73
+ const queryBuilder = await zkPassport.request({
74
+ name: 'ZKpassport Demo',
75
+ logo: 'https://via.placeholder.com/150',
76
+ purpose: 'Verify user nationality and first name',
77
+ });
78
+ const { url } = queryBuilder.disclose('nationality').disclose('firstname').done();
79
+ return NextResponse.json({ url });
80
+ }
81
+ ```
82
+
83
+ #### **Frontend Example**
84
+
85
+ **App Router:** `app/page.tsx`
86
+ ```tsx
87
+ 'use client';
88
+ import { useEffect, useState } from 'react';
89
+
90
+ export default function Home() {
91
+ const [verificationUrl, setVerificationUrl] = useState<string | null>(null);
92
+
93
+ useEffect(() => {
94
+ fetch('/api/zkpassport')
95
+ .then(res => res.json())
96
+ .then(data => setVerificationUrl(data.url))
97
+ .catch(console.error);
98
+ }, []);
99
+
100
+ return (
101
+ <div>
102
+ <h1>ZKPassport Demo</h1>
103
+ {verificationUrl ? (
104
+ <a href={verificationUrl} target="_blank" rel="noopener noreferrer">
105
+ <button>Verify Identity</button>
106
+ </a>
107
+ ) : (
108
+ <p>Loading...</p>
109
+ )}
110
+ </div>
111
+ );
112
+ }
113
+
114
+ ```
115
+
116
+ ## Local installation
117
+
118
+ ### Clone the repository
119
+
120
+ ```sh
121
+ git clone https://github.com/zkpassport/zkpassport-sdk.git
122
+ cd zkpassport-sdk
123
+ ```
124
+
125
+ ### Install dependencies
126
+
127
+ ```sh
128
+ bun install
129
+ ```
130
+
131
+ ### Run Tests
132
+
133
+ ```sh
134
+ bun test
135
+ ```
136
+
137
+ ### Simulate Websocket Messages
138
+
139
+ Simulate mobile websocket messages: `bun run scripts/simulate.ts mobile`
140
+
141
+ Simulate frontend websocket messages: `bun run scripts/simulate.ts frontend`
@@ -0,0 +1,13 @@
1
+ import { CountryName } from '@/types/countries';
2
+ /**
3
+ * List of countries that are sanctioned by the US government.
4
+ */
5
+ export declare const SANCTIONED_COUNTRIES: CountryName[];
6
+ export declare const EU_COUNTRIES: CountryName[];
7
+ declare const constants: {
8
+ countries: {
9
+ EU: CountryName[];
10
+ SANCTIONED: CountryName[];
11
+ };
12
+ };
13
+ export default constants;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EU_COUNTRIES = exports.SANCTIONED_COUNTRIES = void 0;
4
+ /**
5
+ * List of countries that are sanctioned by the US government.
6
+ */
7
+ exports.SANCTIONED_COUNTRIES = [
8
+ 'North Korea',
9
+ 'Iran',
10
+ 'Iraq',
11
+ 'Libya',
12
+ 'Somalia',
13
+ 'Sudan',
14
+ 'Syrian Arab Republic',
15
+ 'Yemen',
16
+ ];
17
+ exports.EU_COUNTRIES = [
18
+ 'Austria',
19
+ 'Belgium',
20
+ 'Bulgaria',
21
+ 'Croatia',
22
+ 'Cyprus',
23
+ 'Czech Republic',
24
+ 'Denmark',
25
+ 'Estonia',
26
+ 'Finland',
27
+ 'France',
28
+ 'Germany',
29
+ 'Greece',
30
+ 'Hungary',
31
+ 'Ireland',
32
+ 'Italy',
33
+ 'Latvia',
34
+ 'Lithuania',
35
+ 'Luxembourg',
36
+ 'Malta',
37
+ 'Netherlands',
38
+ 'Poland',
39
+ 'Portugal',
40
+ 'Romania',
41
+ 'Slovakia',
42
+ 'Slovenia',
43
+ 'Spain',
44
+ 'Sweden',
45
+ ];
46
+ var constants = {
47
+ countries: {
48
+ EU: exports.EU_COUNTRIES,
49
+ SANCTIONED: exports.SANCTIONED_COUNTRIES,
50
+ },
51
+ };
52
+ exports.default = constants;
@@ -0,0 +1,7 @@
1
+ export declare function generateECDHKeyPair(): Promise<{
2
+ privateKey: Uint8Array;
3
+ publicKey: Uint8Array;
4
+ }>;
5
+ export declare function getSharedSecret(privateKey: string, publicKey: string): Promise<Uint8Array>;
6
+ export declare function encrypt(message: string, sharedSecret: Uint8Array, topic: string): Promise<Uint8Array>;
7
+ export declare function decrypt(ciphertext: Uint8Array, sharedSecret: Uint8Array, topic: string): Promise<string>;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.generateECDHKeyPair = generateECDHKeyPair;
40
+ exports.getSharedSecret = getSharedSecret;
41
+ exports.encrypt = encrypt;
42
+ exports.decrypt = decrypt;
43
+ var aes_1 = require("@noble/ciphers/aes");
44
+ var utils_1 = require("@noble/ciphers/utils");
45
+ function sha256Truncate(topic) {
46
+ return __awaiter(this, void 0, void 0, function () {
47
+ var encoder, data, hashBuffer, fullHashArray, truncatedHashArray;
48
+ return __generator(this, function (_a) {
49
+ switch (_a.label) {
50
+ case 0:
51
+ encoder = new TextEncoder();
52
+ data = encoder.encode(topic);
53
+ return [4 /*yield*/, crypto.subtle.digest('SHA-256', data)];
54
+ case 1:
55
+ hashBuffer = _a.sent();
56
+ fullHashArray = new Uint8Array(hashBuffer);
57
+ truncatedHashArray = fullHashArray.slice(0, 12);
58
+ return [2 /*return*/, truncatedHashArray];
59
+ }
60
+ });
61
+ });
62
+ }
63
+ function generateECDHKeyPair() {
64
+ return __awaiter(this, void 0, void 0, function () {
65
+ var secp256k1, privKey, pubKey;
66
+ return __generator(this, function (_a) {
67
+ switch (_a.label) {
68
+ case 0: return [4 /*yield*/, import('@noble/secp256k1')];
69
+ case 1:
70
+ secp256k1 = _a.sent();
71
+ privKey = secp256k1.utils.randomPrivateKey();
72
+ pubKey = secp256k1.getPublicKey(privKey);
73
+ return [2 /*return*/, {
74
+ privateKey: privKey,
75
+ publicKey: pubKey,
76
+ }];
77
+ }
78
+ });
79
+ });
80
+ }
81
+ function getSharedSecret(privateKey, publicKey) {
82
+ return __awaiter(this, void 0, void 0, function () {
83
+ var secp256k1, sharedSecret;
84
+ return __generator(this, function (_a) {
85
+ switch (_a.label) {
86
+ case 0: return [4 /*yield*/, import('@noble/secp256k1')];
87
+ case 1:
88
+ secp256k1 = _a.sent();
89
+ sharedSecret = secp256k1.getSharedSecret(privateKey, publicKey);
90
+ return [2 /*return*/, sharedSecret.slice(0, 32)];
91
+ }
92
+ });
93
+ });
94
+ }
95
+ function encrypt(message, sharedSecret, topic) {
96
+ return __awaiter(this, void 0, void 0, function () {
97
+ var nonce, aes, data, ciphertext;
98
+ return __generator(this, function (_a) {
99
+ switch (_a.label) {
100
+ case 0: return [4 /*yield*/, sha256Truncate(topic)];
101
+ case 1:
102
+ nonce = _a.sent();
103
+ aes = (0, aes_1.gcm)(sharedSecret, nonce);
104
+ data = (0, utils_1.utf8ToBytes)(message);
105
+ ciphertext = aes.encrypt(data);
106
+ return [2 /*return*/, ciphertext];
107
+ }
108
+ });
109
+ });
110
+ }
111
+ function decrypt(ciphertext, sharedSecret, topic) {
112
+ return __awaiter(this, void 0, void 0, function () {
113
+ var nonce, aes, data, dataString;
114
+ return __generator(this, function (_a) {
115
+ switch (_a.label) {
116
+ case 0: return [4 /*yield*/, sha256Truncate(topic)];
117
+ case 1:
118
+ nonce = _a.sent();
119
+ aes = (0, aes_1.gcm)(sharedSecret, nonce);
120
+ data = aes.decrypt(ciphertext);
121
+ dataString = new TextDecoder().decode(data);
122
+ return [2 /*return*/, dataString];
123
+ }
124
+ });
125
+ });
126
+ }
@@ -0,0 +1,79 @@
1
+ import { DisclosableIDCredential, IDCredential, IDCredentialValue, NumericalIDCredential } from '@/types/credentials';
2
+ import { ProofResult } from '@/types/query-result';
3
+ export * from '@/constants';
4
+ export * from '@/types';
5
+ export declare class ZkPassport {
6
+ private domain;
7
+ private topicToConfig;
8
+ private topicToKeyPair;
9
+ private topicToWebSocketClient;
10
+ private topicToSharedSecret;
11
+ private topicToQRCodeScanned;
12
+ private onQRCodeScannedCallbacks;
13
+ private onGeneratingProofCallbacks;
14
+ private onBridgeConnectCallbacks;
15
+ private onProofGeneratedCallbacks;
16
+ private onRejectCallbacks;
17
+ private onErrorCallbacks;
18
+ private topicToService;
19
+ constructor(_domain?: string);
20
+ /**
21
+ * @notice Handle an encrypted message.
22
+ * @param request The request.
23
+ * @param outerRequest The outer request.
24
+ */
25
+ private handleEncryptedMessage;
26
+ private getZkPassportRequest;
27
+ /**
28
+ * @notice Create a new request.
29
+ * @returns The query builder object.
30
+ */
31
+ request({ name, logo, purpose, topicOverride, keyPairOverride, }: {
32
+ name: string;
33
+ logo: string;
34
+ purpose: string;
35
+ topicOverride?: string;
36
+ keyPairOverride?: {
37
+ privateKey: Uint8Array;
38
+ publicKey: Uint8Array;
39
+ };
40
+ }): Promise<{
41
+ eq: <T extends IDCredential>(key: T, value: IDCredentialValue<T>) => any;
42
+ gte: <T extends NumericalIDCredential>(key: T, value: IDCredentialValue<T>) => any;
43
+ gt: <T extends NumericalIDCredential>(key: T, value: IDCredentialValue<T>) => any;
44
+ lte: <T extends NumericalIDCredential>(key: T, value: IDCredentialValue<T>) => any;
45
+ lt: <T extends NumericalIDCredential>(key: T, value: IDCredentialValue<T>) => any;
46
+ range: <T extends NumericalIDCredential>(key: T, start: IDCredentialValue<T>, end: IDCredentialValue<T>) => any;
47
+ in: <T extends IDCredential>(key: T, value: IDCredentialValue<T>[]) => any;
48
+ out: <T extends IDCredential>(key: T, value: IDCredentialValue<T>[]) => any;
49
+ disclose: (key: DisclosableIDCredential) => any;
50
+ done: () => {
51
+ url: string;
52
+ requestId: string;
53
+ onQRCodeScanned: (callback: () => void) => number;
54
+ onGeneratingProof: (callback: () => void) => number;
55
+ onBridgeConnect: (callback: () => void) => number;
56
+ onProofGenerated: (callback: (result: ProofResult) => void) => number;
57
+ onReject: (callback: () => void) => number;
58
+ onError: (callback: (error: string) => void) => number;
59
+ isBridgeConnected: () => boolean;
60
+ isQRCodeScanned: () => boolean;
61
+ };
62
+ }>;
63
+ /**
64
+ * @notice Verifies a proof.
65
+ * @param proof The proof to verify.
66
+ * @returns True if the proof is valid, false otherwise.
67
+ */
68
+ /**
69
+ * @notice Returns the URL of the request.
70
+ * @param requestId The request ID.
71
+ * @returns The URL of the request.
72
+ */
73
+ getUrl(requestId: string): string;
74
+ /**
75
+ * @notice Cancels a request by closing the WebSocket connection and deleting the associated data.
76
+ * @param requestId The request ID.
77
+ */
78
+ cancelRequest(requestId: string): void;
79
+ }