GitBook: No commit message

This commit is contained in:
kichongtran
2023-02-02 19:13:17 +00:00
committed by gitbook-bot
parent b6b782fd8c
commit 5acbe75f1e
13 changed files with 616 additions and 0 deletions

29
README.md Normal file
View File

@@ -0,0 +1,29 @@
---
description: (formerly ZK Keeper)
---
# 👋 Welcome to CryptKeeper
## Quick links
{% content-ref url="what-is-cryptkeeper/overview.md" %}
[overview.md](what-is-cryptkeeper/overview.md)
{% endcontent-ref %}
{% content-ref url="what-is-cryptkeeper/features.md" %}
[features.md](what-is-cryptkeeper/features.md)
{% endcontent-ref %}
{% embed url="https://github.com/privacy-scaling-explorations/crypt-keeper-extension" %}
## Guides
We've put together some helpful guides for you to get setup quickly and easily.
{% content-ref url="guides/quick-setup.md" %}
[quick-setup.md](guides/quick-setup.md)
{% endcontent-ref %}
{% content-ref url="guides/integrating-cryptkeeper/" %}
[integrating-cryptkeeper](guides/integrating-cryptkeeper/)
{% endcontent-ref %}

23
SUMMARY.md Normal file
View File

@@ -0,0 +1,23 @@
# Table of contents
* [👋 Welcome to CryptKeeper](README.md)
## What is CryptKeeper?
* [💡 Overview](what-is-cryptkeeper/overview.md)
* [✨ Features](what-is-cryptkeeper/features.md)
## Guides
* [🛠 Quick setup](guides/quick-setup.md)
* [🧱 Integrating CryptKeeper](guides/integrating-cryptkeeper/README.md)
* [🔌 Connect to extension](guides/integrating-cryptkeeper/connect-to-extension.md)
* [🎯 Inject client](guides/integrating-cryptkeeper/inject-client.md)
* [🆔 Create identity](guides/integrating-cryptkeeper/create-identity.md)
* [⚡ Generate proof](guides/integrating-cryptkeeper/generate-proof.md)
* [🐰 Zkitter examples](guides/integrating-cryptkeeper/zkitter-examples.md)
## References
* [📘 Terms](references/terms.md)
* [🤔 FAQ](references/faq.md)

View File

@@ -0,0 +1,31 @@
---
description: >-
This section will show you how to integrate your dapp with the CryptKeeper
extension.
---
# 🧱 Integrating CryptKeeper
Before you begin, make sure you reviewed the [quick setup guide](../quick-setup.md)
{% content-ref url="connect-to-extension.md" %}
[connect-to-extension.md](connect-to-extension.md)
{% endcontent-ref %}
{% content-ref url="inject-client.md" %}
[inject-client.md](inject-client.md)
{% endcontent-ref %}
{% content-ref url="create-identity.md" %}
[create-identity.md](create-identity.md)
{% endcontent-ref %}
{% content-ref url="generate-proof.md" %}
[generate-proof.md](generate-proof.md)
{% endcontent-ref %}
{% content-ref url="zkitter-examples.md" %}
[zkitter-examples.md](zkitter-examples.md)
{% endcontent-ref %}
##

View File

@@ -0,0 +1,41 @@
# 🔌 Connect to extension
## Connect to extension
Use `zkpr.connect` to access the extension.
Initialize the `zkpr.connect` function, which returns a promise that resolves to the client object.
 Once you have the client object, you can use it to make RPC calls to the extension.
 [@src/contentscripts/injected.ts](https://github.com/privacy-scaling-explorations/crypt-keeper-extension/blob/master/src/contentscripts/injected.ts)
```typescript
async function connect() {
let result
try {
const approved = await tryInject(window.location.origin)
if (approved) {
await addHost(window.location.origin)
result = client
}
} catch (err) {
// eslint-disable-next-line no-console
console.log('Err: ', err)
result = null
}
await post({ method: RPCAction.CLOSE_POPUP })
return result
}
declare global {
interface Window {
zkpr: {
connect: () => any
}
}
}// Some code
```

View File

@@ -0,0 +1,59 @@
# 🆔 Create identity
By default, CryptKeeper uses MetaMask to create a new Semaphore Identity \[link].
1. In MetaMask, a user signs a message with the private key of their Ethereum account.
2. In your dapp, the user creates a deterministic identity \[link] with the signed message.
3. The user can now recreate their Semaphore identity whenever they want by signing the same message with their Ethereum account in Metamask.
Semaphore identities can also be created using other data (such as web2 reputations from [Interep](https://interep.link/)) or using random numbers.
[@src/background/zk-keeper.ts](https://github.com/privacy-scaling-explorations/crypt-keeper-extension/blob/master/src/background/zk-keeper.ts)
```typescript
this.add(
RPCAction.CREATE_IDENTITY,
LockService.ensure,
async (payload: NewIdentityRequest) => {
try {
const { strategy, messageSignature, options } = payload
if (!strategy) throw new Error('strategy not provided')
const numOfIdentites = await this.identityService.getNumOfIdentites()
const config: any = {
...options,
name: options?.name || `Account # ${numOfIdentites}`
}
if (strategy === 'interrep') {
console.log("CREATE_IDENTITY: 1")
config.messageSignature = messageSignature;
console.log("CREATE_IDENTITY: 2")
}
const identity: ZkIdentityWrapper | undefined = await identityFactory(strategy, config)
console.log("CREATE_IDENTITY: 4", identity);
if (!identity) {
throw new Error('Identity not created, make sure to check strategy')
}
await this.identityService.insert(identity)
return true
} catch (error: any) {
console.log("CREATE_IDENTITY: Error", error);
throw new Error(error.message)
}
}
)
```
 
1. Import the `ZkKeeperController` class and initialize an instance of it.
2. Use the `RPCAction.CREATE_IDENTITY` method, which is a function provided by the `ZkKeeperController` class, and pass in the necessary parameters as the payload. The payload should include a `strategy`, `messageSignature`, and `options` object.
3. The `strategy` parameter is a string that specifies the type of identity you want to create.
4. The `messageSignature` parameter is a string that is the signature of some message that is used to verify the authenticity of the client.
5. The `options` object contains additional options for creating the identity including an `identityName` field which is a string that sets the name of the newly created identity.
6. The function returns a promise that resolves to the new identity's commitment. Once the identity is created, it can be used to generate ZK proofs

View File

@@ -0,0 +1,70 @@
# ⚡ Generate proof
CryptKeeper lets dapps outsource ZK proof generation via the Semaphore and RLN protocols. This section will review Semaphore proofs.
 [@src/contentscripts/injected.ts](https://github.com/privacy-scaling-explorations/crypt-keeper-extension/blob/master/src/contentscripts/injected.ts)
```typescript
async function semaphoreProof(
externalNullifier: string,
signal: string,
circuitFilePath: string,
zkeyFilePath: string,
merkleProofArtifactsOrStorageAddress: string | MerkleProofArtifacts,
merkleProof?: MerkleProof
) {
```
 The `semaphoreProof` function allows the user to create a proof of a signal to be sent to the Semaphore contract. CryptKeeper uses values provided by the dapp and the private user values to generate the proof and returns it to the dapp for verification.  
Your dapp should pass in public values (`externalNullifier` and `signal`).
Any circuit files (`circuitFilePath` and `zkeyFilePath`) can be used, but ready-to-use and audited circuit files can be found [here](http://www.trusted-setup-pse.org/) and [here](https://drive.google.com/file/d/1Yi14jwly70VwMSuqJrPCc3j15MWeE7mc/view?usp=sharing).
Merkle witness or Merkle tree components (`merkleProofArtifactsOrStorageAddress` can be provided by a smart contract or as a structured input: 
* `StorageAddress` points to a Merkle tree on-chain
* `merkleProofArtifact` is a Merkle tree in a specific format
* [@src/types/index.ts](https://github.com/privacy-scaling-explorations/crypt-keeper-extension/blob/817ec0e1f336ab61c9c70f4918853e7c279dd21d/src/types/index.ts#L56)
* ```typescript
export type MerkleProofArtifacts = {
leaves: string[]
depth: number
leavesPerNode: number
}
```
`SemaphoreProof` requires the following arguments:
`externalNullifier`
* a unique identifier for the signal to prevent double signaling
* public input provided by dapp
`signal`
* hash of the content of the message
* public input provided by dapp
`circuitFilePath`
* location of the circuit file
* used to generate the ZK proof in the browser
`zkeyFilePath`
* the zkeyFile contains the trusted setup \[link] files and is the starting point for creating a circuit
* location of the zkey file
`merkleProofArtifactsOrStorageAddress`
* object providing Merkle witness <mark style="color:blue;"></mark> directly (`merkleProofArtifact`) or the secure service address from which the Merkle witness should be obtained (`merkleStorageAddress`)&#x20;
* To generate a proof, a user must provide the Merkle path that leads to the users identity in the Merkle tree&#x20;
&#x20;
**RLN Proofs**
[RLN (Rate-Limiting Nullifier)](https://rate-limiting-nullifier.github.io/rln-docs/) is a ZK gadget or mechanism that enables spam prevention for anonymous environments. RLN Proofs are the same as Semaphore proofs except they generate proofs for a specific epoch (time period) and require an additional argument: `rlnIdentifier`.

View File

@@ -0,0 +1,45 @@
# 🎯 Inject client
CryptKeeper injects an object called `zkpr` into the browser. This is similar to how
MetaMask injects a [global API](https://docs.metamask.io/guide/ethereum-provider.html#ethereum-provider-api) into websites visited by its users at `window.ethereum` (formerly [window.web3](https://docs.metamask.io/guide/provider-migration.html#replacing-window-web3))
The injected client serves as bridge between frontend and backend processes. It is passed into the web page by the extension through an API allows the web page to make calls to CryptKeeper. From here you can manually generate and verify proofs, create identities, and set permissions.
You should be able to access the injected clients functions and permissions once connected:
* `getIdentityCommitments`: Retrieves the commitments of the current identity.
* `getActiveIdentity`: Retrieves the currently active identity.
* `getHostPermissions`: Retrieves the permissions for the specified host.
* `setHostPermissions`: Sets the permissions for the specified host.
* `createIdentity`: Creates a new identity and returns the result.
* `createDummyRequest`: Creates a dummy request and returns the result.
* `semaphoreProof`: Sends a Semaphore proof request and returns the result.
* `rlnProof`: Sends a RLN proof request and returns the result.
* `clearApproved`: Clears the approved list of requests.
* `getApproved`: Retrieves the approved list of requests.
* `openPopup`: Opens the extension's popup window.
* `closePopup`: Closes the extension's popup window.
* `post`: Sends a request to the extension and returns the result.
[@src/contentscripts/injected.ts](https://github.com/privacy-scaling-explorations/crypt-keeper-extension/blob/817ec0e1f336ab61c9c70f4918853e7c279dd21d/src/contentscripts/injected.ts)
```typescript
const client = {
openPopup,
getIdentityCommitments,
getActiveIdentity,
createIdentity,
getHostPermissions,
setHostPermissions,
semaphoreProof,
rlnProof,
on,
off,
// dev-only
clearApproved,
createDummyRequest
}
```

View File

@@ -0,0 +1,199 @@
# 🐰 Zkitter examples
[Zkitter](https://www.zkitter.com/explore/) is an example of how a live dapp integrates with CryptKeeper and connects to the extension. &#x20;
The `connectZKPR` function connects to an existing identity by using the `zkpr.connect()` method. The returned client is then used to instantiate a new `ZKPR` object. Then it listens for events such as `logout` and `identityChanged` and dispatches actions accordingly.
```typescript
export const connectZKPR =
() => async (dispatch: ThunkDispatch<any, any, any>, getState: () => AppRootState) => {
dispatch(setLoading(true));
try {
let id: Identity | null = null;
// @ts-ignore
if (typeof window.zkpr !== 'undefined') {
// @ts-ignore
const zkpr: any = window.zkpr;
const client = await zkpr.connect();
const zkprClient = new ZKPR(client);
zkprClient.on('logout', async data => {
const {
worker: { selected, identities },
} = getState();
dispatch(disconnectZKPR());
const [defaultId] = identities;
if (defaultId) {
postWorkerMessage(
selectIdentity(
defaultId.type === 'gun' ? defaultId.publicKey : defaultId.identityCommitment
)
);
} else {
postWorkerMessage(setIdentity(null));
}
});
zkprClient.on('identityChanged', async data => {
const idCommitment = data && BigInt('0x' + data).toString();
const {
worker: { identities },
} = getState();
dispatch(setIdCommitment(''));
if (idCommitment) {
// @ts-ignore
dispatch(setIdCommitment(idCommitment));
// @ts-ignore
const id: any = await maybeSetZKPRIdentity(idCommitment);
if (!id) {
const [defaultId] = identities;
if (defaultId) {
postWorkerMessage(
selectIdentity(
defaultId.type === 'gun' ? defaultId.publicKey : defaultId.identityCommitment
)
);
} else {
postWorkerMessage(setIdentity(null));
}
}
}
});
localStorage.setItem('ZKPR_CACHED', '1');
const idCommitmentHex = await zkprClient.getActiveIdentity();
const idCommitment = idCommitmentHex && BigInt('0x' + idCommitmentHex).toString();
if (idCommitment) {
dispatch(setIdCommitment(idCommitment));
id = await maybeSetZKPRIdentity(idCommitment);
}
dispatch(setZKPR(zkprClient));
}
dispatch(setLoading(false));
return id;
} catch (e) {
dispatch(setLoading(false));
throw e;
}
};
```
{% embed url="https://github.com/zkitter/ui/blob/main/src/ducks/zkpr.ts#L45-L126" %}
&#x20;
&#x20;
An example of how Zkitter generates proofs:&#x20;
```typescript
const identityPathIndex = merkleProof!.pathIndices;
if (
!identityCommitment ||
!identityPathElements ||
!identityPathIndex ||
!identityTrapdoor ||
!identityNullifier
) {
return null;
}
const { messageId, hash, ...json } = post.toJSON();
const epoch = getEpoch();
const externalNullifier = genExternalNullifier(epoch);
const signal = messageId;
const rlnIdentifier = await sha256('zkpost');
const xShare = RLN.genSignalHash(signal);
const witness = RLN.genWitness(
identitySecretHash!,
merkleProof!,
externalNullifier,
signal,
BigInt('0x' + rlnIdentifier)
);
const { proof, publicSignals } = await RLN.genProof(
witness,
`${config.indexerAPI}/circuits/rln/wasm`,
`${config.indexerAPI}/circuits/rln/zkey`
);
```
{% embed url="https://github.com/zkitter/ui/blob/main/src/ducks/drafts.ts#L139-L169" %}
&#x20;&#x20;
An example of how Zkitter verifies proofs:&#x20;
```typescript
if (result) {
logger.debug('post already exist', {
messageId,
origin: 'gun',
});
return;
}
if (!creator && !data) {
return;
}
try {
if (data) {
const proof = JSON.parse(data.proof);
const publicSignals = JSON.parse(data.publicSignals);
let verified = false;
if (!data.x_share) {
verified = await Semaphore.verifyProof(vKey as any, {
proof,
publicSignals,
});
if (!verified) return;
} else {
verified = await this.call('zkchat', 'verifyRLNProof', {
epoch: data.epoch,
proof,
publicSignals,
x_share: data.x_share,
});
```
{% embed url="https://github.com/zkitter/zkitterd/blob/main/src/services/gun.ts#L277-L305" %}
&#x20;

7
guides/quick-setup.md Normal file
View File

@@ -0,0 +1,7 @@
# 🛠 Quick setup
## Development
1. Install the necessary packages by running the command `npm install` in your terminal.
2. Start the development server by running the command `npm run dev`
3. In your browser, navigate to the extensions settings and load the `dist` directory as an unpacked extension.

2
references/faq.md Normal file
View File

@@ -0,0 +1,2 @@
# 🤔 FAQ

89
references/terms.md Normal file
View File

@@ -0,0 +1,89 @@
---
description: Background information on relevant concepts
---
# 📘 Terms
### Circuits
A circuit is a mathematical representation of the computations that need to be performed to prove a statement. A circuit consists of a set of wires that carry values (inputs) and connect them to addition and multiplication gates, which restrict specific operations on the inputs. The prover generates a proof, which is a succinct representation of the input and output of the circuit, and sends it to the verifier, who can then efficiently verify the proof without knowing the input values.
&#x20;For more background information on how circuits work, check out this link: [https://docs.circom.io/background/background/](https://docs.circom.io/background/background/)
&#x20;Ready-to-use and audited circuit files can be found [here](http://www.trusted-setup-pse.org/) and [here](https://drive.google.com/file/d/1Yi14jwly70VwMSuqJrPCc3j15MWeE7mc/view?usp=sharing).
&#x20;
### Merkle Tree
A Merkle tree is a type of data structure used to efficiently verify the integrity of large data sets.&#x20;
It is constructed by repeatedly hashing pairs of data until a single hash, called the root, is left. Each pair of child nodes in the tree is connected to its parent node by a hash link, and each leaf node in the tree contains a piece of the original data. When verifying the integrity of the data, only the leaf node and the corresponding hash links leading to the root need to be checked, reducing the amount of data that needs to be examined.&#x20;
In the [Semaphore](https://semaphore.appliedzkp.org/docs/introduction) protocol, Merkle trees are used to keep track of the members of a group in the protocol. When a user joins a group in Semaphore, their public identity commitment is added to that group's Merkle tree. This allows the protocol to check that the user is a member of the group without revealing their identity.
&#x20;&#x20;
### Merkle Witness
A Merkle witness is a small piece of data that can be used to prove the authenticity of a larger piece of data. Specifically, they are a set of values that are used to prove the authenticity of a particular leaf node in a Merkle tree. The witness includes the leaf value and the values of the hashes in the path from the leaf node to the root of the tree. The authenticity of the leaf node is proven by recomputing the hashes along the path and comparing them to the included witness values.
&#x20;
### RLN
[RLN (Rate-Limiting Nullifier)](https://rate-limiting-nullifier.github.io/rln-docs/) is a ZK gadget or mechanism that enables spam prevention for anonymous environments.
&#x20;Anonymity opens up the possibility for spam and Sybil attack vectors for certain applications, which could seriously degrade the user experience and the overall functioning of the application. For example, imagine a chat application where users are anonymous. Now, everyone can write an unlimited number of spam messages, but we don't have the ability to kick this member because the spammer is anonymous.
&#x20;RLN helps us identify and "kick" the spammer. Moreover, RLN can be used to limit users in the number of actions they are allowed to make within a given timeframe.
&#x20;&#x20;
### Semaphore Protocol
CryptKeeper can be thought of as a browser extension for [Semaphore, ](https://semaphore.appliedzkp.org/docs/introduction)allowing users to use basic Semaphore functions separately from the app layer.
&#x20;[Semaphore](https://github.com/semaphore-protocol/semaphore) is a [zero-knowledge](https://z.cash/technology/zksnarks) protocol that allows you to cast a signal (for example, a vote or endorsement) as a provable group member without revealing your identity. Additionally, it provides a simple mechanism to prevent double-signaling.
&#x20;With Semaphore, you can allow your users to do the following:
1. [Create a Semaphore identity](https://semaphore.appliedzkp.org/docs/guides/identities).
2. [Add their Semaphore identity to a group (i.e. _Merkle tree_)](https://semaphore.appliedzkp.org/docs/guides/groups).
3. [Send a verifiable, anonymous signal (e.g a vote or endorsement)](https://semaphore.appliedzkp.org/docs/guides/proofs).
&#x20;&#x20;
### Semaphore Identities
A [Semaphore identity](https://semaphore.appliedzkp.org/docs/guides/identities) is an object that is used to join a Semaphore group. It contains two private values that are generated when the identity is created: an \`identity trapdoor\` and an \`identity nullifier\`. These values are kept secret by the identity owner and are used to generate ZK proofs and authenticate signals.
&#x20;The identity also has a public value called the "identity commitment", which is similar to a public Ethereum address and is used to represent the identity of a group member. Identities can be created randomly or deterministically from a secret message and can be saved and reused later as a JSON string.
&#x20;
### Semaphore proofs
A Semaphore proof is a zero-knowledge proof that is used to anonymously signal a message from within a Semaphore group. The proof is generated by passing in a user's Semaphore identity, the group the user belongs to, an external nullifier to prevent double-signaling, and the message the user wants to send.
&#x20;Developers can use Semaphore for the following:
* [Generate a proof off-chain](https://semaphore.appliedzkp.org/docs/guides/proofs#generate-a-proof-off-chain)
* [Verify a proof off-chain](https://semaphore.appliedzkp.org/docs/guides/proofs#verify-a-proof-off-chain)
* [Verify a proof on-chain](https://semaphore.appliedzkp.org/docs/guides/proofs#verify-a-proof-on-chain)
&#x20;&#x20;
### Trusted setup
A trusted setup is a procedure in which secret information is used to generate a piece of data that is necessary for the proper functioning of certain cryptographic protocols. This data, once generated, is then made public and the secret information used to generate it is discarded, so that no further participation from the creators of the ceremony is required.
&#x20;The trust in a trusted setup comes from the fact that only a minimum number of honest people need participate in the ceremony in order to ensure the security of the final output. Trusted setups can have a 1-of-N trust model where only one honest participant is required out of a group.
&#x20;Learn more about trusted setups in here: [https://vitalik.ca/general/2022/03/14/trustedsetup.html](https://vitalik.ca/general/2022/03/14/trustedsetup.html)
&#x20;Verify the files from Semaphores [trusted setup ceremony](https://storage.googleapis.com/trustedsetup-a86f4.appspot.com/semaphore/semaphore\_top\_index.html).
&#x20;
&#x20;

View File

@@ -0,0 +1,12 @@
# ✨ Features
The following features are supported currently:
* Identity secret and Identity commitment generation
* Semaphore ZK-Proof generation
* RLN ZK-Proof generation
Proof generation is enabled in two ways:
* by providing Merkle witness (Merkle tree components) directly as a [structured input](https://github.com/privacy-scaling-explorations/crypt-keeper-extension/blob/817ec0e1f336ab61c9c70f4918853e7c279dd21d/src/types/index.ts#L56):&#x20;
* by providing a secure service (i.e. smart contract) address from which the Merkle witness should be obtained

View File

@@ -0,0 +1,9 @@
# 💡 Overview
CryptKeeper is a browser extension for managing zero-knowledge identities. It securely separates ZK identity and ZK proof generation from the application layer. Specifically, it manages identities created using the [Semaphore](https://semaphore.appliedzkp.org/) protocol and allows proof generation from [RLN](https://github.com/Rate-Limiting-Nullifier/) and Semaphore.
&#x20;This plugin is still in development and uses the [zk-kit library](https://github.com/appliedzkp/zk-kit).
## Video overview
{% embed url="https://www.youtube.com/watch?index=16&list=PLV91V4b0yVqRQ62Mv0nUgWxJhi4E67XSY&v=F5ic66_eNAs" %}