@zkpassport/sdk 0.3.3 → 0.4.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/dist/cjs/assets/abi/ZKPassportVerifier.json +14 -14
- package/dist/cjs/index.d.ts +10 -5
- package/dist/cjs/index.js +85 -104
- package/dist/esm/assets/abi/ZKPassportVerifier.json +14 -14
- package/dist/esm/index.d.ts +10 -5
- package/dist/esm/index.js +86 -105
- package/package.json +3 -2
- package/src/assets/abi/ZKPassportVerifier.json +14 -14
- package/src/index.ts +94 -119
- package/src/encryption.ts +0 -45
- package/src/json-rpc.ts +0 -61
- package/src/mobile.ts +0 -186
- package/src/websocket.ts +0 -16
package/src/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomBytes } from "crypto"
|
|
2
1
|
import { Alpha3Code, getAlpha3Code, registerLocale } from "i18n-iso-countries"
|
|
3
2
|
import {
|
|
4
3
|
type DisclosableIDCredential,
|
|
@@ -57,11 +56,9 @@ import {
|
|
|
57
56
|
ProofData,
|
|
58
57
|
getScopeFromOuterProof,
|
|
59
58
|
getSubscopeFromOuterProof,
|
|
59
|
+
getServiceScopeHash,
|
|
60
60
|
} from "@zkpassport/utils"
|
|
61
61
|
import { bytesToHex } from "@noble/ciphers/utils"
|
|
62
|
-
import { getWebSocketClient, WebSocketClient } from "./websocket"
|
|
63
|
-
import { createEncryptedJsonRpcRequest } from "./json-rpc"
|
|
64
|
-
import { decrypt, generateECDHKeyPair, getSharedSecret } from "./encryption"
|
|
65
62
|
import { noLogger as logger } from "./logger"
|
|
66
63
|
import { inflate } from "pako"
|
|
67
64
|
import i18en from "i18n-iso-countries/langs/en.json"
|
|
@@ -70,6 +67,7 @@ import { sha256 } from "@noble/hashes/sha2"
|
|
|
70
67
|
import { hexToBytes } from "@noble/hashes/utils"
|
|
71
68
|
import ZKPassportVerifierAbi from "./assets/abi/ZKPassportVerifier.json"
|
|
72
69
|
import { RegistryClient } from "@zkpassport/registry"
|
|
70
|
+
import { Bridge, BridgeInterface } from "@obsidion/bridge"
|
|
73
71
|
|
|
74
72
|
const DEFAULT_DATE_VALUE = new Date(1111, 10, 11)
|
|
75
73
|
|
|
@@ -124,6 +122,24 @@ export type SolidityVerifierParameters = {
|
|
|
124
122
|
|
|
125
123
|
export type EVMChain = "ethereum_sepolia" | "local_anvil"
|
|
126
124
|
|
|
125
|
+
function getChainIdFromEVMChain(chain: EVMChain): number {
|
|
126
|
+
if (chain === "ethereum_sepolia") {
|
|
127
|
+
return 11155111
|
|
128
|
+
} else if (chain === "local_anvil") {
|
|
129
|
+
return 31337
|
|
130
|
+
}
|
|
131
|
+
throw new Error(`Unsupported chain: ${chain}`)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function getEVMChainFromChainId(chainId: number): EVMChain {
|
|
135
|
+
if (chainId === 11155111) {
|
|
136
|
+
return "ethereum_sepolia"
|
|
137
|
+
} else if (chainId === 31337) {
|
|
138
|
+
return "local_anvil"
|
|
139
|
+
}
|
|
140
|
+
throw new Error(`Unsupported chain ID: ${chainId}`)
|
|
141
|
+
}
|
|
142
|
+
|
|
127
143
|
registerLocale(i18en)
|
|
128
144
|
|
|
129
145
|
function hasRequestedAccessToField(credentialsRequest: Query, field: IDCredential): boolean {
|
|
@@ -351,13 +367,12 @@ export class ZKPassport {
|
|
|
351
367
|
devMode: boolean
|
|
352
368
|
}
|
|
353
369
|
> = {}
|
|
354
|
-
private
|
|
355
|
-
private
|
|
356
|
-
private topicToSharedSecret: Record<string, Uint8Array> = {}
|
|
370
|
+
private topicToPublicKey: Record<string, string> = {}
|
|
371
|
+
private topicToBridge: Record<string, BridgeInterface> = {}
|
|
357
372
|
private topicToRequestReceived: Record<string, boolean> = {}
|
|
358
373
|
private topicToService: Record<
|
|
359
374
|
string,
|
|
360
|
-
{ name: string; logo: string; purpose: string; scope?: string }
|
|
375
|
+
{ name: string; logo: string; purpose: string; scope?: string; chainId?: number }
|
|
361
376
|
> = {}
|
|
362
377
|
private topicToProofs: Record<string, Array<ProofResult>> = {}
|
|
363
378
|
private topicToExpectedProofCount: Record<string, number> = {}
|
|
@@ -400,6 +415,9 @@ export class ZKPassport {
|
|
|
400
415
|
queryResult: result,
|
|
401
416
|
validity: this.topicToLocalConfig[topic]?.validity,
|
|
402
417
|
scope: this.topicToService[topic]?.scope,
|
|
418
|
+
evmChain: this.topicToService[topic]?.chainId
|
|
419
|
+
? getEVMChainFromChainId(this.topicToService[topic]?.chainId)
|
|
420
|
+
: undefined,
|
|
403
421
|
devMode: this.topicToLocalConfig[topic]?.devMode,
|
|
404
422
|
})
|
|
405
423
|
delete this.topicToProofs[topic]
|
|
@@ -498,11 +516,7 @@ export class ZKPassport {
|
|
|
498
516
|
* @param request The request.
|
|
499
517
|
* @param outerRequest The outer request.
|
|
500
518
|
*/
|
|
501
|
-
private async handleEncryptedMessage(
|
|
502
|
-
topic: string,
|
|
503
|
-
request: JsonRpcRequest,
|
|
504
|
-
outerRequest: JsonRpcRequest,
|
|
505
|
-
) {
|
|
519
|
+
private async handleEncryptedMessage(topic: string, request: JsonRpcRequest) {
|
|
506
520
|
logger.debug("Received encrypted message:", request)
|
|
507
521
|
if (request.method === "accept") {
|
|
508
522
|
logger.debug(`User accepted the request and is generating a proof`)
|
|
@@ -512,31 +526,9 @@ export class ZKPassport {
|
|
|
512
526
|
await Promise.all(this.onRejectCallbacks[topic].map((callback) => callback()))
|
|
513
527
|
} else if (request.method === "proof") {
|
|
514
528
|
logger.debug(`User generated proof`)
|
|
515
|
-
|
|
516
|
-
const bytesProof = Buffer.from(request.params.proof, "base64")
|
|
517
|
-
const bytesCommittedInputs = request.params.committedInputs
|
|
518
|
-
? Buffer.from(request.params.committedInputs, "base64")
|
|
519
|
-
: null
|
|
520
|
-
const uncompressedProof = inflate(bytesProof)
|
|
521
|
-
const uncompressedCommittedInputs = bytesCommittedInputs
|
|
522
|
-
? inflate(bytesCommittedInputs)
|
|
523
|
-
: null
|
|
524
|
-
// The gzip lib in the app compress the proof as ASCII
|
|
525
|
-
// and since the app passes the proof as a hex string, we can
|
|
526
|
-
// just decode the bytes as hex characters using the TextDecoder
|
|
527
|
-
const hexProof = new TextDecoder().decode(uncompressedProof)
|
|
528
|
-
const processedProof: ProofResult = {
|
|
529
|
-
proof: hexProof,
|
|
530
|
-
vkeyHash: request.params.vkeyHash,
|
|
531
|
-
name: request.params.name,
|
|
532
|
-
version: request.params.version,
|
|
533
|
-
committedInputs: uncompressedCommittedInputs
|
|
534
|
-
? JSON.parse(new TextDecoder().decode(uncompressedCommittedInputs))
|
|
535
|
-
: undefined,
|
|
536
|
-
}
|
|
537
|
-
this.topicToProofs[topic].push(processedProof)
|
|
529
|
+
this.topicToProofs[topic].push(request.params)
|
|
538
530
|
await Promise.all(
|
|
539
|
-
this.onProofGeneratedCallbacks[topic].map((callback) => callback(
|
|
531
|
+
this.onProofGeneratedCallbacks[topic].map((callback) => callback(request.params)),
|
|
540
532
|
)
|
|
541
533
|
// If the results were received before all the proofs were generated,
|
|
542
534
|
// we can handle the result now
|
|
@@ -651,7 +643,7 @@ export class ZKPassport {
|
|
|
651
643
|
const base64Service = Buffer.from(JSON.stringify(this.topicToService[topic])).toString(
|
|
652
644
|
"base64",
|
|
653
645
|
)
|
|
654
|
-
const pubkey =
|
|
646
|
+
const pubkey = this.topicToPublicKey[topic]
|
|
655
647
|
this.setExpectedProofCount(topic)
|
|
656
648
|
return {
|
|
657
649
|
url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[topic].mode}`,
|
|
@@ -675,7 +667,7 @@ export class ZKPassport {
|
|
|
675
667
|
onReject: (callback: () => void) => this.onRejectCallbacks[topic].push(callback),
|
|
676
668
|
onError: (callback: (error: string) => void) =>
|
|
677
669
|
this.onErrorCallbacks[topic].push(callback),
|
|
678
|
-
isBridgeConnected: () => this.
|
|
670
|
+
isBridgeConnected: () => this.topicToBridge[topic].isBridgeConnected(),
|
|
679
671
|
requestReceived: () => this.topicToRequestReceived[topic] === true,
|
|
680
672
|
}
|
|
681
673
|
},
|
|
@@ -690,6 +682,7 @@ export class ZKPassport {
|
|
|
690
682
|
* @param scope Scope this request to a specific use case
|
|
691
683
|
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
692
684
|
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
685
|
+
* @param evmChain The EVM chain to use for the request (if using the proof onchain)
|
|
693
686
|
* @returns The query builder object.
|
|
694
687
|
*/
|
|
695
688
|
public async request({
|
|
@@ -698,6 +691,7 @@ export class ZKPassport {
|
|
|
698
691
|
purpose,
|
|
699
692
|
scope,
|
|
700
693
|
mode,
|
|
694
|
+
evmChain,
|
|
701
695
|
validity,
|
|
702
696
|
devMode,
|
|
703
697
|
topicOverride,
|
|
@@ -708,21 +702,27 @@ export class ZKPassport {
|
|
|
708
702
|
purpose: string
|
|
709
703
|
scope?: string
|
|
710
704
|
mode?: ProofMode
|
|
705
|
+
evmChain?: EVMChain
|
|
711
706
|
validity?: number
|
|
712
707
|
devMode?: boolean
|
|
713
708
|
topicOverride?: string
|
|
714
709
|
keyPairOverride?: { privateKey: Uint8Array; publicKey: Uint8Array }
|
|
715
710
|
}): Promise<QueryBuilder> {
|
|
716
|
-
const
|
|
711
|
+
const bridge = await Bridge.create({
|
|
712
|
+
keyPair: keyPairOverride,
|
|
713
|
+
bridgeId: topicOverride,
|
|
714
|
+
})
|
|
717
715
|
|
|
718
|
-
const
|
|
719
|
-
this.topicToKeyPair[topic] = {
|
|
720
|
-
privateKey: keyPair.privateKey,
|
|
721
|
-
publicKey: keyPair.publicKey,
|
|
722
|
-
}
|
|
716
|
+
const topic = bridge.connection.getBridgeId()
|
|
723
717
|
|
|
724
718
|
this.topicToConfig[topic] = {}
|
|
725
|
-
this.topicToService[topic] = {
|
|
719
|
+
this.topicToService[topic] = {
|
|
720
|
+
name,
|
|
721
|
+
logo,
|
|
722
|
+
purpose,
|
|
723
|
+
scope,
|
|
724
|
+
chainId: evmChain ? getChainIdFromEVMChain(evmChain) : undefined,
|
|
725
|
+
}
|
|
726
726
|
this.topicToProofs[topic] = []
|
|
727
727
|
this.topicToExpectedProofCount[topic] = 0
|
|
728
728
|
this.topicToLocalConfig[topic] = {
|
|
@@ -740,68 +740,22 @@ export class ZKPassport {
|
|
|
740
740
|
this.onRejectCallbacks[topic] = []
|
|
741
741
|
this.onErrorCallbacks[topic] = []
|
|
742
742
|
|
|
743
|
-
|
|
744
|
-
this.topicToWebSocketClient[topic] = wsClient
|
|
745
|
-
wsClient.onopen = async () => {
|
|
746
|
-
logger.info("[frontend] WebSocket connection established")
|
|
747
|
-
await Promise.all(this.onBridgeConnectCallbacks[topic].map((callback) => callback()))
|
|
748
|
-
}
|
|
749
|
-
wsClient.addEventListener("message", async (event: any) => {
|
|
750
|
-
logger.debug("[frontend] Received message:", event.data)
|
|
751
|
-
try {
|
|
752
|
-
const data: JsonRpcRequest = JSON.parse(event.data)
|
|
753
|
-
// Handshake happens when the mobile app scans the QR code and connects to the bridge
|
|
754
|
-
if (data.method === "handshake") {
|
|
755
|
-
logger.debug("[frontend] Received handshake:", event.data)
|
|
756
|
-
|
|
757
|
-
this.topicToRequestReceived[topic] = true
|
|
758
|
-
this.topicToSharedSecret[topic] = await getSharedSecret(
|
|
759
|
-
bytesToHex(keyPair.privateKey),
|
|
760
|
-
data.params.pubkey,
|
|
761
|
-
)
|
|
762
|
-
logger.debug(
|
|
763
|
-
"[frontend] Shared secret:",
|
|
764
|
-
Buffer.from(this.topicToSharedSecret[topic]).toString("hex"),
|
|
765
|
-
)
|
|
766
|
-
|
|
767
|
-
const encryptedMessage = await createEncryptedJsonRpcRequest(
|
|
768
|
-
"hello",
|
|
769
|
-
null,
|
|
770
|
-
this.topicToSharedSecret[topic],
|
|
771
|
-
topic,
|
|
772
|
-
)
|
|
773
|
-
logger.debug("[frontend] Sending encrypted message:", encryptedMessage)
|
|
774
|
-
wsClient.send(JSON.stringify(encryptedMessage))
|
|
775
|
-
|
|
776
|
-
await Promise.all(this.onRequestReceivedCallbacks[topic].map((callback) => callback()))
|
|
777
|
-
return
|
|
778
|
-
}
|
|
743
|
+
this.topicToPublicKey[topic] = bridge.getPublicKey()
|
|
779
744
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
} catch (error) {
|
|
794
|
-
logger.error("[frontend] Error decrypting message:", error)
|
|
795
|
-
}
|
|
796
|
-
return
|
|
797
|
-
}
|
|
798
|
-
} catch (error) {
|
|
799
|
-
logger.error("[frontend] Error:", error)
|
|
800
|
-
}
|
|
745
|
+
this.topicToBridge[topic] = bridge
|
|
746
|
+
bridge.onConnect(async (reconnection: boolean) => {
|
|
747
|
+
logger.debug("Bridge connected")
|
|
748
|
+
logger.debug("Is reconnection:", reconnection)
|
|
749
|
+
await Promise.all(this.onBridgeConnectCallbacks[topic].map((callback) => callback()))
|
|
750
|
+
})
|
|
751
|
+
bridge.onSecureChannelEstablished(async () => {
|
|
752
|
+
logger.debug("Secure channel established")
|
|
753
|
+
await Promise.all(this.onRequestReceivedCallbacks[topic].map((callback) => callback()))
|
|
754
|
+
})
|
|
755
|
+
bridge.onSecureMessage(async (message: any) => {
|
|
756
|
+
logger.debug("Received message:", message)
|
|
757
|
+
this.handleEncryptedMessage(topic, message)
|
|
801
758
|
})
|
|
802
|
-
wsClient.onerror = (error: Event) => {
|
|
803
|
-
logger.error("[frontend] WebSocket error:", error)
|
|
804
|
-
}
|
|
805
759
|
return this.getZkPassportRequest(topic)
|
|
806
760
|
}
|
|
807
761
|
|
|
@@ -1780,13 +1734,17 @@ export class ZKPassport {
|
|
|
1780
1734
|
queryResultErrors: QueryResultErrors,
|
|
1781
1735
|
key: string,
|
|
1782
1736
|
scope?: string,
|
|
1737
|
+
chainId?: number,
|
|
1783
1738
|
) {
|
|
1784
1739
|
let isCorrect = true
|
|
1785
|
-
if (
|
|
1740
|
+
if (
|
|
1741
|
+
this.domain &&
|
|
1742
|
+
getServiceScopeHash(this.domain, chainId) !== BigInt(proofData.publicInputs[1])
|
|
1743
|
+
) {
|
|
1786
1744
|
console.warn("The proof comes from a different domain than the one expected")
|
|
1787
1745
|
isCorrect = false
|
|
1788
1746
|
queryResultErrors[key as keyof QueryResultErrors].scope = {
|
|
1789
|
-
expected: `Scope: ${
|
|
1747
|
+
expected: `Scope: ${getServiceScopeHash(this.domain, chainId).toString()}`,
|
|
1790
1748
|
received: `Scope: ${BigInt(proofData.publicInputs[1]).toString()}`,
|
|
1791
1749
|
message: "The proof comes from a different domain than the one expected",
|
|
1792
1750
|
}
|
|
@@ -1840,6 +1798,7 @@ export class ZKPassport {
|
|
|
1840
1798
|
queryResult: QueryResult,
|
|
1841
1799
|
validity?: number,
|
|
1842
1800
|
scope?: string,
|
|
1801
|
+
chainId?: number,
|
|
1843
1802
|
) {
|
|
1844
1803
|
let commitmentIn: bigint | undefined
|
|
1845
1804
|
let commitmentOut: bigint | undefined
|
|
@@ -1945,11 +1904,14 @@ export class ZKPassport {
|
|
|
1945
1904
|
message: "The proof does not verify all the requested conditions and information",
|
|
1946
1905
|
}
|
|
1947
1906
|
}
|
|
1948
|
-
if (
|
|
1907
|
+
if (
|
|
1908
|
+
this.domain &&
|
|
1909
|
+
getServiceScopeHash(this.domain, chainId) !== getScopeFromOuterProof(proofData)
|
|
1910
|
+
) {
|
|
1949
1911
|
console.warn("The proof comes from a different domain than the one expected")
|
|
1950
1912
|
isCorrect = false
|
|
1951
1913
|
queryResultErrors.outer.scope = {
|
|
1952
|
-
expected: `Scope: ${
|
|
1914
|
+
expected: `Scope: ${getServiceScopeHash(this.domain, chainId).toString()}`,
|
|
1953
1915
|
received: `Scope: ${getScopeFromOuterProof(proofData).toString()}`,
|
|
1954
1916
|
message: "The proof comes from a different domain than the one expected",
|
|
1955
1917
|
}
|
|
@@ -2646,6 +2608,9 @@ export class ZKPassport {
|
|
|
2646
2608
|
* @param proofs The proofs to verify.
|
|
2647
2609
|
* @param queryResult The query result to verify against
|
|
2648
2610
|
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
2611
|
+
* @param scope Scope this request to a specific use case
|
|
2612
|
+
* @param evmChain The EVM chain to use for the verification (if using the proof onchain)
|
|
2613
|
+
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
2649
2614
|
* @returns An object containing the unique identifier associated to the user
|
|
2650
2615
|
* and a boolean indicating whether the proofs were successfully verified.
|
|
2651
2616
|
*/
|
|
@@ -2654,18 +2619,28 @@ export class ZKPassport {
|
|
|
2654
2619
|
queryResult,
|
|
2655
2620
|
validity,
|
|
2656
2621
|
scope,
|
|
2622
|
+
evmChain,
|
|
2657
2623
|
devMode = false,
|
|
2658
2624
|
}: {
|
|
2659
2625
|
proofs: Array<ProofResult>
|
|
2660
2626
|
queryResult: QueryResult
|
|
2661
2627
|
validity?: number
|
|
2662
2628
|
scope?: string
|
|
2629
|
+
evmChain?: EVMChain
|
|
2663
2630
|
devMode?: boolean
|
|
2664
2631
|
}): Promise<{
|
|
2665
2632
|
uniqueIdentifier: string | undefined
|
|
2666
2633
|
verified: boolean
|
|
2667
2634
|
queryResultErrors?: QueryResultErrors
|
|
2668
2635
|
}> {
|
|
2636
|
+
// If no proofs were generated, the results can't be trusted.
|
|
2637
|
+
// We still return it but verified will be false
|
|
2638
|
+
if (!proofs || proofs.length === 0) {
|
|
2639
|
+
return {
|
|
2640
|
+
uniqueIdentifier: undefined,
|
|
2641
|
+
verified: false,
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2669
2644
|
const formattedResult: QueryResult = queryResult
|
|
2670
2645
|
// Make sure to reconvert the dates to Date objects
|
|
2671
2646
|
if (formattedResult.birthdate && formattedResult.birthdate.disclose) {
|
|
@@ -2684,11 +2659,12 @@ export class ZKPassport {
|
|
|
2684
2659
|
let verified = true
|
|
2685
2660
|
let uniqueIdentifier: string | undefined
|
|
2686
2661
|
let queryResultErrors: QueryResultErrors | undefined
|
|
2662
|
+
const chainId = evmChain ? getChainIdFromEVMChain(evmChain) : undefined
|
|
2687
2663
|
const {
|
|
2688
2664
|
isCorrect,
|
|
2689
2665
|
uniqueIdentifier: uniqueIdentifierFromPublicInputs,
|
|
2690
2666
|
queryResultErrors: queryResultErrorsFromPublicInputs,
|
|
2691
|
-
} = await this.checkPublicInputs(proofs, formattedResult, validity, scope)
|
|
2667
|
+
} = await this.checkPublicInputs(proofs, formattedResult, validity, scope, chainId)
|
|
2692
2668
|
uniqueIdentifier = uniqueIdentifierFromPublicInputs
|
|
2693
2669
|
verified = isCorrect
|
|
2694
2670
|
queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs
|
|
@@ -2782,7 +2758,7 @@ export class ZKPassport {
|
|
|
2782
2758
|
if (network === "ethereum_sepolia") {
|
|
2783
2759
|
return {
|
|
2784
2760
|
...baseConfig,
|
|
2785
|
-
address: "
|
|
2761
|
+
address: "0xDfE02DFd5c208854884B58bFf6522De5c42F73E3",
|
|
2786
2762
|
}
|
|
2787
2763
|
} else if (network === "local_anvil") {
|
|
2788
2764
|
return {
|
|
@@ -2951,7 +2927,7 @@ export class ZKPassport {
|
|
|
2951
2927
|
* @returns The URL of the request.
|
|
2952
2928
|
*/
|
|
2953
2929
|
public getUrl(requestId: string) {
|
|
2954
|
-
const pubkey =
|
|
2930
|
+
const pubkey = this.topicToPublicKey[requestId]
|
|
2955
2931
|
const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString(
|
|
2956
2932
|
"base64",
|
|
2957
2933
|
)
|
|
@@ -2966,14 +2942,13 @@ export class ZKPassport {
|
|
|
2966
2942
|
* @param requestId The request ID.
|
|
2967
2943
|
*/
|
|
2968
2944
|
public cancelRequest(requestId: string) {
|
|
2969
|
-
if (this.
|
|
2970
|
-
this.
|
|
2971
|
-
delete this.
|
|
2945
|
+
if (this.topicToBridge[requestId]) {
|
|
2946
|
+
this.topicToBridge[requestId].close()
|
|
2947
|
+
delete this.topicToBridge[requestId]
|
|
2972
2948
|
}
|
|
2973
|
-
delete this.
|
|
2949
|
+
delete this.topicToPublicKey[requestId]
|
|
2974
2950
|
delete this.topicToConfig[requestId]
|
|
2975
2951
|
delete this.topicToLocalConfig[requestId]
|
|
2976
|
-
delete this.topicToSharedSecret[requestId]
|
|
2977
2952
|
delete this.topicToProofs[requestId]
|
|
2978
2953
|
delete this.topicToExpectedProofCount[requestId]
|
|
2979
2954
|
delete this.topicToFailedProofCount[requestId]
|
|
@@ -2990,7 +2965,7 @@ export class ZKPassport {
|
|
|
2990
2965
|
* @notice Clears all requests.
|
|
2991
2966
|
*/
|
|
2992
2967
|
public clearAllRequests() {
|
|
2993
|
-
for (const requestId in this.
|
|
2968
|
+
for (const requestId in this.topicToBridge) {
|
|
2994
2969
|
this.cancelRequest(requestId)
|
|
2995
2970
|
}
|
|
2996
2971
|
}
|
package/src/encryption.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { gcm } from "@noble/ciphers/aes"
|
|
2
|
-
import { utf8ToBytes } from "@noble/ciphers/utils"
|
|
3
|
-
|
|
4
|
-
async function sha256Truncate(topic: string): Promise<Uint8Array> {
|
|
5
|
-
const encoder = new TextEncoder()
|
|
6
|
-
const data = encoder.encode(topic)
|
|
7
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data)
|
|
8
|
-
const fullHashArray = new Uint8Array(hashBuffer)
|
|
9
|
-
const truncatedHashArray = fullHashArray.slice(0, 12)
|
|
10
|
-
return truncatedHashArray
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export async function generateECDHKeyPair() {
|
|
14
|
-
const secp256k1 = await import("@noble/secp256k1")
|
|
15
|
-
const privKey = secp256k1.utils.randomPrivateKey()
|
|
16
|
-
const pubKey = secp256k1.getPublicKey(privKey)
|
|
17
|
-
return {
|
|
18
|
-
privateKey: privKey,
|
|
19
|
-
publicKey: pubKey,
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export async function getSharedSecret(privateKey: string, publicKey: string) {
|
|
24
|
-
const secp256k1 = await import("@noble/secp256k1")
|
|
25
|
-
const sharedSecret = secp256k1.getSharedSecret(privateKey, publicKey)
|
|
26
|
-
return sharedSecret.slice(0, 32)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function encrypt(message: string, sharedSecret: Uint8Array, topic: string) {
|
|
30
|
-
// Nonce must be 12 bytes
|
|
31
|
-
const nonce = await sha256Truncate(topic)
|
|
32
|
-
const aes = gcm(sharedSecret, nonce)
|
|
33
|
-
const data = utf8ToBytes(message)
|
|
34
|
-
const ciphertext = aes.encrypt(data)
|
|
35
|
-
return ciphertext
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function decrypt(ciphertext: Uint8Array, sharedSecret: Uint8Array, topic: string) {
|
|
39
|
-
// Nonce must be 12 bytes
|
|
40
|
-
const nonce = await sha256Truncate(topic)
|
|
41
|
-
const aes = gcm(sharedSecret, nonce)
|
|
42
|
-
const data = aes.decrypt(ciphertext)
|
|
43
|
-
const dataString = new TextDecoder().decode(data)
|
|
44
|
-
return dataString
|
|
45
|
-
}
|
package/src/json-rpc.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { randomBytes } from "crypto"
|
|
2
|
-
import type { JsonRpcRequest, JsonRpcResponse } from "@zkpassport/utils"
|
|
3
|
-
import { encrypt } from "./encryption"
|
|
4
|
-
import { WebSocketClient } from "./websocket"
|
|
5
|
-
import { noLogger as logger } from "./logger"
|
|
6
|
-
|
|
7
|
-
export function createJsonRpcRequest(method: string, params: any): JsonRpcRequest {
|
|
8
|
-
return {
|
|
9
|
-
jsonrpc: "2.0",
|
|
10
|
-
id: randomBytes(16).toString("hex"),
|
|
11
|
-
method,
|
|
12
|
-
params,
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function createEncryptedJsonRpcRequest(
|
|
17
|
-
method: string,
|
|
18
|
-
params: any,
|
|
19
|
-
sharedSecret: Uint8Array,
|
|
20
|
-
topic: string,
|
|
21
|
-
): Promise<JsonRpcRequest> {
|
|
22
|
-
const encryptedMessage = await encrypt(
|
|
23
|
-
JSON.stringify({ method, params: params || {} }),
|
|
24
|
-
sharedSecret,
|
|
25
|
-
topic,
|
|
26
|
-
)
|
|
27
|
-
return createJsonRpcRequest("encryptedMessage", {
|
|
28
|
-
payload: Buffer.from(encryptedMessage).toString("base64"),
|
|
29
|
-
})
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function sendEncryptedJsonRpcRequest(
|
|
33
|
-
method: string,
|
|
34
|
-
params: any,
|
|
35
|
-
sharedSecret: Uint8Array,
|
|
36
|
-
topic: string,
|
|
37
|
-
wsClient: WebSocketClient,
|
|
38
|
-
): Promise<boolean> {
|
|
39
|
-
try {
|
|
40
|
-
const message = { method, params: params || {} }
|
|
41
|
-
const encryptedMessage = await encrypt(JSON.stringify(message), sharedSecret, topic)
|
|
42
|
-
const request = createJsonRpcRequest("encryptedMessage", {
|
|
43
|
-
payload: Buffer.from(encryptedMessage).toString("base64"),
|
|
44
|
-
})
|
|
45
|
-
logger.debug("Sending encrypted message (original):", message)
|
|
46
|
-
logger.debug("Sending encrypted message (encrypted):", request)
|
|
47
|
-
wsClient.send(JSON.stringify(request))
|
|
48
|
-
return true
|
|
49
|
-
} catch (error) {
|
|
50
|
-
logger.error("Error sending encrypted message:", error)
|
|
51
|
-
return false
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function createJsonRpcResponse(id: string, result: any): JsonRpcResponse {
|
|
56
|
-
return {
|
|
57
|
-
jsonrpc: "2.0",
|
|
58
|
-
id,
|
|
59
|
-
result,
|
|
60
|
-
}
|
|
61
|
-
}
|