-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from waku-org/waku-keystore
Add WAKU-RLN-KEYSTORE
- Loading branch information
Showing
2 changed files
with
300 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,299 @@ | ||
--- | ||
title: WAKU-RLN-KEYSTORE | ||
name: Waku RLN Keystore | ||
category: Standards Track | ||
editor: Jimmy Debe <[email protected]> | ||
contributors: | ||
- Aaryamann Challani <[email protected]> | ||
--- | ||
|
||
## Abstract | ||
This specification describes how RLN, Rate Limit Nullifier, | ||
credentials are securely stored in a JSON schema. | ||
|
||
## Summary | ||
A keystore is a construct to store a user’s keys. | ||
The keys will be encrypted and decrypted based on methods specified in this specification. | ||
The keystore stores a user's credentials locally and | ||
uses [32/RLN-V1](/spec/32/) as a spam-prevention mechanism by generating zero-knowledge proofs. | ||
|
||
## Background | ||
The secure storage of keys is important in peer-to-peer messaging applications. | ||
A `WAKU-RLN-KEYSTORE` uses zero-knowledge proofs for anonymous rate-limiting for messaging frameworks. | ||
Generated credentials by a user are encrypted and stored in the keystore to be retrieved over a network. | ||
With [32/RLN-V1](/spec/32/), sending and receiving | ||
messages will ensure a message rate for a network is being followed while preserving the anonymity of the message owner. | ||
|
||
### Waku RLN Keystore Format: | ||
|
||
A format example of a keystore used by a [17/WAKU2-RLN-Relay](/spec/17/). | ||
|
||
```js | ||
const Keystore { | ||
application: "waku-rln-relay" , | ||
appIdentifier: "string", | ||
version: "string", | ||
credentials: { | ||
"membershipHash": { | ||
crypto: { | ||
cipher: "string", | ||
cipherparams: { | ||
iv: "string", | ||
}, | ||
ciphertext: "string", | ||
kdf: "string", | ||
kdfparams: { | ||
dklen: integer, | ||
c: integer, | ||
prf: "string", | ||
salt: "string", | ||
}, | ||
mac: "string", | ||
} | ||
} | ||
} | ||
} | ||
|
||
``` | ||
|
||
## Specification | ||
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, | ||
“NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). | ||
|
||
The keystore MUST be generated by a cryptographic construction with password verification and decryption. | ||
|
||
Keystore modules MUST include metadata, key derivation function, checksum, cipher, and a membership hash. | ||
|
||
### Metadata: | ||
Information about the keystore SHOULD be stored in the metadata. | ||
|
||
The declaration of `application`, `version`, and `appIdentifier` COULD occur in the metadata. | ||
|
||
- `application` : current application, MUST be a string | ||
- `version` : application version, MUST be a string, SHOULD follow semantic versioning | ||
- `appIdentifier`: application identifier, MUST be a string | ||
|
||
### Credentials: | ||
|
||
After RLN credentials are generated, it MUST be stored in a JSON schema. | ||
The Waku RLN credentials MUST consist of a `membershipHash` and `WakuCredential`. | ||
The `membershipHash` will be an identity hash of the user. | ||
The `WakuCredential` will store to encryption portion of the keystore. | ||
There COULD be multiple credentials stored in a keystore, categorized by the `membershipHash`. | ||
|
||
Each contruct MUST include the keypair: | ||
> key: [`membershipHash`]: pair: [`WakuCredential`] | ||
#### membershipHash | ||
|
||
The `membershipHash` SHOULD be generated by user's participating in a membership group, | ||
as decribed in [32/RLN-V1](/spec/32/). | ||
Each user SHOULD register to the group with an `identity_commitment` stored in a Merkle tree. | ||
A cryptographic hash function that SHOULD be used to generate the `membershipHash` is [SHA256](https://www.rfc-editor.org/rfc/rfc4634.txt), | ||
other hash functions MAY be used. | ||
The hash function that is used, | ||
SHOULD be mentioned in the `verison` attribute. | ||
|
||
To generate the `membershipHash`, | ||
the `treeIndex`, `membershipContract`, `chainId` and `identityCredential` attributes SHOULD be used to create a hexadecimal string. | ||
- it MUST NOT already exist in the keystore. | ||
|
||
##### `treeIndex` | ||
|
||
After a user registers to a group, | ||
a `treeIndex` value of the position in the Merkle tree SHOULD be returned. | ||
- it MUST be a Merkle tree data structure filled with `identity_commitment` from user registrations. | ||
- it MUST be a hexadecimal string | ||
|
||
##### `membershipContract` | ||
|
||
For decentralized membership registrations, | ||
the `membershipContract` SHOULD be a `contractAddress` from a public blockchain using smart contracts. | ||
- it MUST be a string. | ||
|
||
##### `chainId` | ||
It uniquely defines the chain upon which the registration has occurred. | ||
The `chainId` SHOULD be the blockchain identifier used for `membershipcontract`, | ||
as described in [EIP155](https://eips.ethereum.org/EIPS/eip-155). | ||
- it MUST be a string | ||
|
||
##### `identityCredential` | ||
|
||
The `identityCredential` MUST be derived after a succussful decryption of the keystore. | ||
|
||
The `identityCredential` MUST be constructed with the `identity_secret`, `identity_secret_hash`, `identity_commitment` values. | ||
- it MUST be a hash of `identity_commitment` stored in a Merkle tree. | ||
- it MUST be a string. | ||
|
||
###### `identity_secret` | ||
|
||
The `identity_secret` MUST be constructed with `identity_nullifier` + `identity_trapdoor` values. | ||
- `identity_nullifier` : Random 32 byte value | ||
- `identity_trapdoor` : Random 32 byte value | ||
|
||
###### `identity_secret_hash` | ||
|
||
Used to derive the `identity_commitment` of the user, and | ||
as a private input for zero-knowledge proof generation. | ||
- it MUST be created with `identity_secret` as a parameter for the hash function. | ||
- This secret hash SHOULD be kept private by the user. | ||
|
||
###### `identity_commitment` | ||
- it SHOULD be created with `identity_secret_hash` by using the hash function Poseidon, | ||
as described in [Poseidon Paper](https://eprint.iacr.org/2019/458.pdf). | ||
- it MUST be used by a user for group registering. | ||
|
||
#### WakuCredential | ||
|
||
The `WakuCredential` will store values used for encrypting and decrypting user's keystores. | ||
- it MUST be used for password verification. | ||
- it MUST follow [EIP-2335](https://eips.ethereum.org/EIPS/eip-2335) | ||
- it SHOULD use [SHA256](https://www.rfc-editor.org/rfc/rfc4634.txt) as the hash function | ||
|
||
|
||
#### KDF | ||
|
||
The password-based encryption used SHOULD be KDF, key derivation function, | ||
to produce a derived key from a password and other parameters. | ||
The keystore COULD use PBKDF2 password-based encryption, | ||
as described in [RFC 2898](https://www.ietf.org/rfc/rfc2898.txt). | ||
|
||
A `WakuCredential` object MUST include: | ||
| Name | Description | | ||
|----|-----| | ||
| password | used to encrypt keystore and decryption key | | ||
| secret | key to be encrypted | | ||
| pubKey | public key | | ||
| path | HD, hardened derivation, path used to generate the secret | | ||
| checksum | hash function | | ||
| cipher | cipher function | | ||
|
||
```js | ||
|
||
crypto: { | ||
|
||
cipher: "string" // The cipher function | ||
cipherparams: { | ||
iv: "string" // The cipher parameters | ||
}, | ||
ciphertext: "string" // The cipher message, | ||
kdf: "string" // KDF Function, | ||
kdfparams: { | ||
param: integer // Salt value and iteration count, | ||
dklen: integer // Length in octets of derived key, MUST be positive integer, | ||
c: "string" // Iteration count, MUST be positive integer, | ||
prf: "string" // Underlying pseudorandom function, | ||
salt: "string" // Produces a large set of keys based on the password | ||
}, | ||
mac: "string" // Checksum | ||
} | ||
|
||
``` | ||
|
||
#### Decryption | ||
The keystore SHOULD decrypt a user's credentials using a password and the `membershipHash`, | ||
using PBKDF2 that returns the `decryptionKey` key. | ||
The decryption key is used to verify the keystore is correct. | ||
- To generate the `decryptionKey`, it MUST be constructed from a password and KDF, | ||
as desrcibed in [ERC-2335: BLS12-381 Keystore](https://eips.ethereum.org/EIPS/eip-2335). | ||
- The `decryptionKey`, is derived from the cipher function and | ||
cipher parameters described in the KDF used in the keystore. | ||
|
||
### Test Vectors | ||
|
||
RLN uses Poseidon hash algorithm to generate the `identityCredential`, | ||
as described in [Poseidon Paper](https://eprint.iacr.org/2019/458.pdf). | ||
The keystore hash algorithm used is [SHA256](https://www.rfc-editor.org/rfc/rfc4634.txt). | ||
|
||
#### Input: | ||
|
||
- `application`: "waku-rln-relay" | ||
- `appIdentifier`: "01234567890abcdef" | ||
- `version`: "0.2" | ||
- `hashFunction`: "poseidonHash" | ||
- `password`: "sup3rsecure" | ||
|
||
```js | ||
|
||
identityCredential = { | ||
IDTrapdoor: [ | ||
211, 23, 66, 42, 179, 130, 131, 111, 201, 205, 244, 34, 27, 238, 244, | ||
216, 131, 240, 188, 45, 193, 172, 4, 168, 225, 225, 43, 197, 114, 176, | ||
126, 9, | ||
], | ||
IDNullifier: [ | ||
238, 168, 239, 65, 73, 63, 105, 19, 132, 62, 213, 205, 191, 255, 209, 9, | ||
178, 155, 239, 201, 131, 125, 233, 136, 246, 217, 9, 237, 55, 89, 81, | ||
42, | ||
], | ||
IDSecretHash: [ | ||
150, 54, 194, 28, 18, 216, 138, 253, 95, 139, 120, 109, 98, 129, 146, | ||
101, 41, 194, 36, 36, 96, 152, 152, 89, 151, 160, 118, 15, 222, 124, | ||
187, 4, | ||
], | ||
IDCommitment: [ | ||
112, 216, 27, 89, 188, 135, 203, 19, 168, 211, 117, 13, 231, 135, 229, | ||
58, 94, 20, 246, 8, 33, 65, 238, 37, 112, 97, 65, 241, 255, 93, 171, 15, | ||
], | ||
|
||
} | ||
|
||
membership = { | ||
chainId: "0xAA36A7", | ||
treeIndex: 8, | ||
address: "0x8e1F3742B987d8BA376c0CBbD7357fE1F003ED71", | ||
} | ||
|
||
``` | ||
|
||
#### Output: | ||
|
||
```js | ||
application: "waku-rln-relay", | ||
appIdentifier: "01234567890abcdef", | ||
version: "0.2", | ||
credentials: { | ||
"9DB2B4718A97485B9F70F68D1CC19F4E10F0B4CE943418838E94956CB8E57548": { | ||
crypto: { | ||
cipher: "aes-128-ctr", | ||
cipherparams: { | ||
iv: "fd6b39eb71d44c59f6bf5ff3d8945c80", | ||
}, | ||
ciphertext: "9c72f47ce95de03ed34502d0288e7576b66b51b9e7d5ae882c27bd89f94e6a03c2c44c2ddf0c982e72003d67212105f1b64614f57cabb0ceadab7e07be165eee1121ad6b81951368a9f3be2dd99ea294515f6013d5f2bd4702a40e36cfde2ea298b23b31e5ce719d8040c3331f73d6bf44f88bca39bac0e917d8bf545500e4f40d321c235426a80f315ac70666acbd3bdf803fbc1e7e7103fed466525ed332b25d72b2dbedf6fa383b2305987c1fe276b029570519b3e79930edf08c1029868d05c2c08ab61d7c64f63c054b4f6a5a12d43cdc79751b6fe58d3ed26b69443eb7c9f7efce27912340129c91b6b813ac94efd5776a40b1dda896d61357de208c7c47a14af911cc231355c8093ee6626e89c07e1037f9e0b22c690e3e049014399ca0212c509cb04c71c7860d1b17a0c47711c490c27bad2825926148a1f15a507f36ba2cdaa04897fce2914e53caed0beaf1bebd2a83af76511cc15bff2165ff0860ad6eca1f30022d7739b2a6b6a72f2feeef0f5941183cda015b4631469e1f4cf27003cab9a90920301cb30d95e4554686922dc5a05c13dfb575cdf113c700d607896011970e6ee7d6edb61210ab28ac8f0c84c606c097e3e300f0a5f5341edfd15432bef6225a498726b62a98283829ad51023b2987f30686cfb4ea3951f3957654035ec291f9b0964a3a8665d81b16cec20fb40f944d5f9bf03ac1e444ad45bae3fa85e7465ce620c0966d8148d6e2856f676c4fbbe3ebe470453efb4bbda1866680037917e37765f680e3da96ef3991f3fe5cda80c523996c2234758bf5f7b6d052dc6942f5a92c8b8eec5d2d8940203bbb6b1cba7b7ebc1334334ca69cdb509a5ea58ec6b2ebaea52307589eaae9430eb15ad234c0c39c83accdf3b77e52a616e345209c5bc9b442f9f0fa96836d9342f983a7", | ||
kdf: "pbkdf2", | ||
kdfparams: { | ||
dklen: 32, | ||
c: 1000000, | ||
prf: "hmac-sha256", | ||
salt: "60f0aa92fbf63a8356dfdbed2ab18058", | ||
}, | ||
mac: "51a227ac6db7f2797c63925880b3db664e034231a4c68daa919ab42d8df38bc6", | ||
}, | ||
} | ||
|
||
``` | ||
## Security Considerations | ||
### 1.) Add a Password | ||
An attacker can identify which credential belongs to a combination of `chainId` and | ||
`contractAddress` pair by brute forcing the `treeIndex` iteratively to find a hash match. | ||
The RECOMMENDED solution is to add a password to the construction of `membershipHash` to prevent this attack. | ||
The RECOMMENDED `membershipHash` Construction: | ||
- `membershipHash` SHOULD be constructed with `treeIndex`, `membershipContract`, `identityCredential`, `membershipPassword` | ||
- `membershipPassword` : a new password created to private attacks compromising keystore credentials. | ||
- The user MUST store the `membershipPassword` privately. | ||
## Copyright | ||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). | ||
## References | ||
1. [32/RLN-V1](/spec/32/) | ||
2. [17/WAKU2-RLN-RELAY](/spec/17/) | ||
3. [SHA256](https://www.rfc-editor.org/rfc/rfc4634.txt) | ||
4. [EIP155](https://eips.ethereum.org/EIPS/eip-155) | ||
5. [Poseidon Paper](https://eprint.iacr.org/2019/458.pdf) | ||
6. [EIP-2335](https://eips.ethereum.org/EIPS/eip-2335) | ||
7. [RFC 2898](https://www.ietf.org/rfc/rfc2898.txt) | ||
8. [ERC-2335: BLS12-381 Keystore](https://eips.ethereum.org/EIPS/eip-2335) |