Compare commits
40 Commits
codex-disp
...
mix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
972e3f5589 | ||
|
|
733a838117 | ||
|
|
5f74739b52 | ||
|
|
0b4d151638 | ||
|
|
15c785f74e | ||
|
|
1348711a48 | ||
|
|
2f8c313b28 | ||
|
|
bfb0c86fd3 | ||
|
|
1e5be60c72 | ||
|
|
372f91a847 | ||
|
|
71f12e637d | ||
|
|
13aaae37d1 | ||
|
|
c1c9303b37 | ||
|
|
751a01ece4 | ||
|
|
aab7f88ac6 | ||
|
|
ef7f7155ba | ||
|
|
f11559814d | ||
|
|
7dfd46e8ee | ||
|
|
ed2c68f072 | ||
|
|
9260222e19 | ||
|
|
eb25cd06d6 | ||
|
|
0dcabbad2b | ||
|
|
bbf1c3614d | ||
|
|
0bc8636bf3 | ||
|
|
e7895d8ba3 | ||
|
|
1a3877809b | ||
|
|
e2913c9fd0 | ||
|
|
3becf63795 | ||
|
|
ec3a6676b4 | ||
|
|
77029a2e64 | ||
|
|
a5b24ac0a2 | ||
|
|
89cac77ae4 | ||
|
|
a189a72146 | ||
|
|
cbefa483fc | ||
|
|
5064ded998 | ||
|
|
7b443c1aab | ||
|
|
99be3b9745 | ||
|
|
7e3a625812 | ||
|
|
e234e9d5a3 | ||
|
|
e5b859abfb |
13
.github/workflows/markdown-lint.yml
vendored
@@ -16,17 +16,8 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Get changed files
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "CHANGED_FILES<<EOF" >> $GITHUB_ENV
|
||||
gh pr diff ${{ github.event.number }} --name-only | sed -e 's|$|,|' | xargs -i echo "{}" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
- name: Markdown Linter
|
||||
uses: DavidAnson/markdownlint-cli2-action@v15
|
||||
with:
|
||||
globs: ${{ env.CHANGED_FILES }}
|
||||
configFile: .github/workflows/markdownlint.json
|
||||
globs: '**/*.md'
|
||||
|
||||
1
.github/workflows/markdownlint.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
41
.github/workflows/website-sync.yml
vendored
@@ -1,41 +0,0 @@
|
||||
name: Website Sync
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
if: github.event.pull_request.merged == true
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Clone Website Repo
|
||||
run: |
|
||||
git clone git@github.com:vacp2p/rfc-website.git
|
||||
cd rfc-website
|
||||
git config --local user.email "actions@github.com"
|
||||
git config --local user.name "GitHub Actions"
|
||||
|
||||
- name: List of changed files
|
||||
id: changed_files
|
||||
run: |
|
||||
echo "::set-output name=files::$(git diff --name-only ${{ github.event.before }} ${{ github.sha }})"
|
||||
|
||||
- name: Copy changed files to Website Repo
|
||||
run: |
|
||||
for file in ${{ steps.changed_files.outputs.files }}; do
|
||||
cp --parents "$file" rfc-website/
|
||||
done
|
||||
|
||||
- name: Push changes to Website Repo
|
||||
run: |
|
||||
cd rfc-website
|
||||
git add .
|
||||
git commit -m "Sync website"
|
||||
git push origin main
|
||||
@@ -1,3 +1,4 @@
|
||||
# Codex RFCs
|
||||
|
||||
Codex specifications related to a decentralised data storage platform.
|
||||
Specifications related the Codex decentralised data storage platform.
|
||||
Visit [Codex specs](https://github.com/codex-storage/codex-spec) to view the new Codex specifications currently under discussion.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Nomos Request For Comments(RFC)
|
||||
# Nomos RFCs
|
||||
|
||||
Nomos is building secure, flexible, and scalable infrastructure for developers creating applications for the network state.
|
||||
Nomos is building a secure, flexible, and
|
||||
scalable infrastructure for developers creating applications for the network state.
|
||||
To learn more about Nomos current protocols under discussion,
|
||||
head over to [Nomos Specs](https://github.com/logos-co/nomos-specs).
|
||||
|
||||
@@ -6,7 +6,7 @@ status: draft
|
||||
category: Standards Track
|
||||
tags: waku-application
|
||||
description: A chat protocol to send public and private messages to a single recipient by the Status app.
|
||||
editor: Aaryamann Challani <aaryamann@status.im>
|
||||
editor: Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
contributors:
|
||||
- Andrea Piana <andreap@status.im>
|
||||
- Pedro Pombeiro <pedro@status.im>
|
||||
@@ -54,7 +54,7 @@ There are two phases in the initial negotiation of a 1:1 chat:
|
||||
A QR code serves two purposes simultaneously - identity verification and initial key material retrieval;
|
||||
1. **Asynchronous initial key exchange**
|
||||
|
||||
For more information on account generation and trust establishment, see [65/ACCOUNT-ADDRESS](../65/account-address.md)
|
||||
For more information on account generation and trust establishment, see [65/STATUS-ACCOUNT-ADDRESS](../65/account-address.md)
|
||||
|
||||
### Post Negotiation
|
||||
|
||||
@@ -213,7 +213,7 @@ Copyright and related rights waived via [CC0](https://creativecommons.org/public
|
||||
|
||||
1. [53/WAKU2-X3DH](../../waku/standards/application/53/x3dh.md)
|
||||
2. [WAKU2-NOISE](https://github.com/waku-org/specs/blob/master/standards/application/noise.md)
|
||||
3. [65/STATUS-ACCOUNT](../65/account-address.md)
|
||||
3. [65/STATUS-ACCOUNT-ADDRESS](../65/account-address.md)
|
||||
4. [54/WAKU2-X3DH-SESSIONS](../../waku/standards/application/54/x3dh-sessions.md)
|
||||
5. [WAKU2-NOISE-SESSIONS](https://github.com/waku-org/specs/blob/master/standards/application/noise-sessions.md)
|
||||
6. [56/STATUS-COMMUNITIES](../56/communities.md)
|
||||
|
||||
@@ -6,7 +6,7 @@ status: draft
|
||||
category: Standards Track
|
||||
tags: waku-application
|
||||
description: Status Communities allow multiple users to communicate in a discussion space. This is a key feature of the Status application.
|
||||
editor: Aaryamann Challani <aaryamann@status.im>
|
||||
editor: Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
contributors:
|
||||
- Andrea Piana <andreap@status.im>
|
||||
---
|
||||
|
||||
@@ -5,7 +5,7 @@ name: Status Keycard Usage
|
||||
status: draft
|
||||
category: Standards Track
|
||||
description: Describes how an application can use the Status Keycard to create, store and transact with different account addresses.
|
||||
editor: Aaryamann Challani <aaryamann@status.im>
|
||||
editor: Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
contributors:
|
||||
- Jimmy Debe <jimmy@status.im>
|
||||
---
|
||||
|
||||
@@ -5,7 +5,7 @@ name: Status Account Address
|
||||
status: draft
|
||||
category: Standards Track
|
||||
description: Details of what a Status account address is and how account addresses are created and used.
|
||||
editor: Aaryamann Challani <aaryamann@status.im>
|
||||
editor: Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
contributors:
|
||||
- Corey Petty <corey@status.im>
|
||||
- Oskar Thorén <oskarth@titanproxy.com>
|
||||
|
||||
@@ -367,7 +367,7 @@ Operators can freely choose how they want to generate, and distribute the public
|
||||
|
||||
The following concepts are introduced:
|
||||
* `private-key-topic`: A private key of 32 bytes, that allows the holder to sign messages and it's mapped to a `protected-pubsub-topic`.
|
||||
* `app-message-hash`: Application `WakuMessage` hash, calculated as `sha256(concat(pubsubTopic, payload, contentTopic))` with all elements in bytes.
|
||||
* `app-message-hash`: Application `WakuMessage` hash, calculated as `sha256(concat(pubsubTopic, payload, contentTopic, timestamp, ephemeral))` with all elements in bytes.
|
||||
* `message-signature`: ECDSA signature of `application-message-hash` using a given `private-key-topic`, 64 bytes.
|
||||
* `public-key-topic`: The equivalent public key of `private-key-topic`.
|
||||
* `protected-pubsub-topic`: Pubsub topic that only accepts messages that were signed with `private-key-topic`, where `verify(message-signature, app-message-hash, public-key-topic)` is only correct if the `message-signature` was produced by `private-key-topic`. See ECDSA signature verification algorithm.
|
||||
|
||||
126
status/raw/status-mvds.md
Normal file
@@ -0,0 +1,126 @@
|
||||
---
|
||||
title: STATUS-MVDS-USAGE
|
||||
name: MVDS Usage in Status
|
||||
status: raw
|
||||
category: Best Current Practice
|
||||
description: Defines how MVDS protocol used by different message types in Status.
|
||||
editor: Kaichao Sun <kaichao@status.im>
|
||||
contributors:
|
||||
---
|
||||
|
||||
## Abstract
|
||||
|
||||
This document lists the types of messages that are using [MVDS](/vac/2/mvds.md) in the Status application.
|
||||
|
||||
## Background
|
||||
|
||||
Status app uses MVDS to ensure messages going through Waku are acknolwedged by the recipient. This is to ensure that the messages are not missed by any interested parties.
|
||||
|
||||
|
||||
## Message types
|
||||
|
||||
Various Message Types contain distinct information defined by the app to facilitate convenient serialization and deserialization.
|
||||
|
||||
E2E reliability is a feature that ensures messages are delivered to the recipient. This is initially achieved by using MVDS in Status.
|
||||
|
||||
Chat Type specifies the category of chat that a message belongs to. It can be OneToOne (aka Direct Message), GroupChat, or CommunityChat. These are the three main types of chats in Status.
|
||||
|
||||
| Message Type | Use MVDS | Need e2e reliability | Chat Type |
|
||||
|----------------------------------------------------------------------------|-------------------------------------|----------------------|-------------------------|
|
||||
| ApplicationMetadataMessage_UNKNOWN | No | No | Not Applied |
|
||||
| ApplicationMetadataMessage_CHAT_MESSAGE | Yes for OneToOne & PrivateGroupChat | Yes | One & Group & Community |
|
||||
| ApplicationMetadataMessage_CONTACT_UPDATE | Yes | Yes | OneToOne |
|
||||
| ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE | No | Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_SYNC_PAIR_INSTALLATION | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_DEPRECATED_SYNC_INSTALLATION | No | No | Pair |
|
||||
| ApplicationMetadataMessage_REQUEST_ADDRESS_FOR_TRANSACTION | Yes for OneToOne | Yes | One & Group & Community |
|
||||
| ApplicationMetadataMessage_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION | Yes for OneToOne | Yes | One & Group & Community |
|
||||
| ApplicationMetadataMessage_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION | Yes for OneToOne | Yes | One & Group & Community |
|
||||
| ApplicationMetadataMessage_REQUEST_TRANSACTION | Yes for OneToOne | Yes | OneToOne & GroupChat |
|
||||
| ApplicationMetadataMessage_SEND_TRANSACTION | Yes for OneToOne | Yes | One & Group & Community |
|
||||
| ApplicationMetadataMessage_DECLINE_REQUEST_TRANSACTION | Yes for OneToOne | Yes | One & Group & Community |
|
||||
| ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT_V2 | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_INSTALLATION_ACCOUNT | No | No | Not Applied |
|
||||
| ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT | No | No | Not Applied |
|
||||
| ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION | No | No | One & Group & Community |
|
||||
| ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE | No | No | One & Group & Community |
|
||||
| ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY | No | No | One & Group & Community |
|
||||
| ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE | No | No | One & Group & Community |
|
||||
| ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST | No | No | One & Group & Community |
|
||||
| ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE | No | No | One & Group & Community |
|
||||
| ApplicationMetadataMessage_EMOJI_REACTION | No | Yes | One & Group & Community |
|
||||
| ApplicationMetadataMessage_GROUP_CHAT_INVITATION | Yes | Yes | GroupChat |
|
||||
| ApplicationMetadataMessage_CHAT_IDENTITY | No | No | OneToOne |
|
||||
| ApplicationMetadataMessage_COMMUNITY_DESCRIPTION | No | Weak Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_COMMUNITY_INVITATION | No | Weak Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_COMMUNITY_REQUEST_TO_JOIN | No | Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_PIN_MESSAGE | Yes for OneToOne & PrivateGroupChat | Yes | One & Group & Community |
|
||||
| ApplicationMetadataMessage_EDIT_MESSAGE | Yes for OneToOne & PrivateGroupChat | Yes | One & Group & Community |
|
||||
| ApplicationMetadataMessage_STATUS_UPDATE | No | No | Not Applied |
|
||||
| ApplicationMetadataMessage_DELETE_MESSAGE | Yes for OneToOne & PrivateGroupChat | Yes | One & Group & Community |
|
||||
| ApplicationMetadataMessage_SYNC_INSTALLATION_COMMUNITY | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_ANONYMOUS_METRIC_BATCH | No | No | Not Applied |
|
||||
| ApplicationMetadataMessage_SYNC_CHAT_REMOVED | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_CHAT_MESSAGES_READ | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_BACKUP | No | No | Not Applied |
|
||||
| ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_READ | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_ACCEPTED | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_DISMISSED | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_BOOKMARK | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_CLEAR_HISTORY | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_SETTING | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_COMMUNITY_MESSAGE_ARCHIVE_MAGNETLINK | No | No | CommunityChat |
|
||||
| ApplicationMetadataMessage_SYNC_PROFILE_PICTURES | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_ACCOUNT | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_ACCEPT_CONTACT_REQUEST | Yes | Yes | OneToOne |
|
||||
| ApplicationMetadataMessage_RETRACT_CONTACT_REQUEST | Yes | Yes | OneToOne |
|
||||
| ApplicationMetadataMessage_COMMUNITY_REQUEST_TO_JOIN_RESPONSE | No | Weak Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_SYNC_COMMUNITY_SETTINGS | Yes | Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_REQUEST_CONTACT_VERIFICATION | Yes | Yes | OneToOne |
|
||||
| ApplicationMetadataMessage_ACCEPT_CONTACT_VERIFICATION | Yes | Yes | OneToOne |
|
||||
| ApplicationMetadataMessage_DECLINE_CONTACT_VERIFICATION | Yes | Yes | OneToOne |
|
||||
| ApplicationMetadataMessage_SYNC_TRUSTED_USER | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_VERIFICATION_REQUEST | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_CONTACT_REQUEST_DECISION | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_COMMUNITY_REQUEST_TO_LEAVE | No | Weak Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_SYNC_DELETE_FOR_ME_MESSAGE | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_SAVED_ADDRESS | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_COMMUNITY_CANCEL_REQUEST_TO_JOIN | No | Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_CANCEL_CONTACT_VERIFICATION | Yes | Yes | OneToOne |
|
||||
| ApplicationMetadataMessage_SYNC_KEYPAIR | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_SOCIAL_LINKS | No | No | Not Applied |
|
||||
| ApplicationMetadataMessage_SYNC_ENS_USERNAME_DETAIL | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_COMMUNITY_EVENTS_MESSAGE | No | No | CommunityChat |
|
||||
| ApplicationMetadataMessage_COMMUNITY_EDIT_SHARED_ADDRESSES | No | No | CommunityChat |
|
||||
| ApplicationMetadataMessage_SYNC_ACCOUNT_CUSTOMIZATION_COLOR | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_ACCOUNTS_POSITIONS | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_COMMUNITY_PRIVILEGED_USER_SYNC_MESSAGE | No | No | CommunityChat |
|
||||
| ApplicationMetadataMessage_COMMUNITY_SHARD_KEY | Yes | Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_SYNC_CHAT | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_DELETED | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_UNREAD | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_COMMUNITY_REQUEST_DECISION | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_SYNC_TOKEN_PREFERENCES | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_COMMUNITY_PUBLIC_SHARD_INFO | No | No | CommunityChat |
|
||||
| ApplicationMetadataMessage_SYNC_COLLECTIBLE_PREFERENCES | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_COMMUNITY_USER_KICKED | No | No | CommunityChat |
|
||||
| ApplicationMetadataMessage_SYNC_PROFILE_SHOWCASE_PREFERENCES | Yes | Yes | Pair |
|
||||
| ApplicationMetadataMessage_COMMUNITY_PUBLIC_STORENODES_INFO | No | Weak Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_COMMUNITY_REEVALUATE_PERMISSIONS_REQUEST | No | Weak Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_DELETE_COMMUNITY_MEMBER_MESSAGES | No | Weak Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_COMMUNITY_UPDATE_GRANT | No | Weak Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_COMMUNITY_ENCRYPTION_KEYS_REQUEST | No | Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_COMMUNITY_TOKEN_ACTION | No | Weak Yes | CommunityChat |
|
||||
| ApplicationMetadataMessage_COMMUNITY_SHARED_ADDRESSES_REQUEST | No | No | CommunityChat |
|
||||
| ApplicationMetadataMessage_COMMUNITY_SHARED_ADDRESSES_RESPONSE | No | No | CommunityChat |
|
||||
|
||||
|
||||
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
## References
|
||||
|
||||
- [MVDS](/vac/2/mvds.md)
|
||||
@@ -4,7 +4,7 @@ name: Status Waku2 Usage
|
||||
status: raw
|
||||
category: Best Current Practice
|
||||
description: Defines how the Status application uses the Waku protocols.
|
||||
editor: Aaryamann Challani <aaryamann@status.im>
|
||||
editor: Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
contributors:
|
||||
- Jimmy Debe <jimmy@status.im>
|
||||
|
||||
|
||||
@@ -4,14 +4,15 @@ title: 1/COSS
|
||||
name: Consensus-Oriented Specification System
|
||||
status: draft
|
||||
category: Best Current Practice
|
||||
editor: Oskar Thoren <oskarth@titanproxy.com>
|
||||
editor: Daniel Kaiser <danielkaiser@status.im>
|
||||
contributors:
|
||||
- Oskar Thoren <oskarth@titanproxy.com>
|
||||
- Pieter Hintjens <ph@imatix.com>
|
||||
- André Rebentisch <andre@openstandards.de>
|
||||
- Alberto Barrionuevo <abarrio@opentia.es>
|
||||
- Chris Puttick <chris.puttick@thehumanjourney.net>
|
||||
- Yurii Rashkovskii <yrashk@gmail.com>
|
||||
- Daniel Kaiser <danielkaiser@status.im>
|
||||
- Jimmy Debe <jimmy@status.im>
|
||||
---
|
||||
|
||||
This document describes a consensus-oriented specification system (COSS) for building interoperable technical specifications.
|
||||
@@ -22,21 +23,23 @@ It is equivalent except for some areas:
|
||||
|
||||
- recommending the use of a permissive licenses, such as CC0 (with the exception of this document);
|
||||
- miscellaneous metadata, editor, and format/link updates;
|
||||
- more inheritance from the [IETF Standards Process][https://www.rfc-editor.org/rfc/rfc2026.txt],
|
||||
- more inheritance from the [IETF Standards Process](https://www.rfc-editor.org/rfc/rfc2026.txt),
|
||||
e.g. using RFC categories: Standards Track, Informational, and Best Common Practice;
|
||||
- standards track specifications SHOULD follow a specific structure that both streamlines editing,
|
||||
and helps implementers to quickly comprehend the specification
|
||||
- specifications MUST feature a header providing specific meta information
|
||||
- raw specifications will not be assigned numbers
|
||||
- section explaining the [IFT](https://free.technology/) Request For Comments specification process managed by the Vac service department
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2008-22 the Editor and Contributors.
|
||||
Copyright (c) 2008-24 the Editor and Contributors.
|
||||
|
||||
This Specification is free software;
|
||||
you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation;
|
||||
either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This Specification is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
This specification is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
@@ -70,7 +73,6 @@ Principles:
|
||||
* The process should allow deprecation of old specifications.
|
||||
|
||||
Specifications should take minutes to explain, hours to design, days to write, weeks to prove, months to become mature, and years to replace.
|
||||
|
||||
Specifications have no special status except that accorded by the community.
|
||||
|
||||
## Architecture
|
||||
@@ -78,31 +80,47 @@ Specifications have no special status except that accorded by the community.
|
||||
COSS is designed around fast, easy to use communications tools.
|
||||
Primarily, COSS uses a wiki model for editing and publishing specifications texts.
|
||||
|
||||
* The *domain* is the conservancy for a set of specifications in a certain area.
|
||||
* Each domain is implemented as an Internet domain, hosting a wiki and optionally other communications tools.
|
||||
* Each specification is a set of wiki pages, together with comments, attached files, and other resources.
|
||||
* Important specifications may also exist as subdomains, i.e. child wikis.
|
||||
* The *domain* is the conservancy for a set of specifications.
|
||||
* The *domain* is implemented as an Internet domain.
|
||||
* Each specification is a document together with references and attached resources.
|
||||
* A *sub-domain* is a initiative under a specific domain.
|
||||
|
||||
Individuals can become members of the domain by completing the necessary legal clearance.
|
||||
Individuals can become members of the *domain* by completing the necessary legal clearance.
|
||||
The copyright, patent, and trademark policies of the domain must be clarified in an Intellectual Property policy that applies to the domain.
|
||||
|
||||
Specifications exist as multiple pages, one page per version of the specification (see "Branching and Merging", below), which may be assigned URIs that include an incremental number.
|
||||
Thus, we refer to a specification by specifying its domain, number, and short name.
|
||||
New versions of the same specification will have new numbers.
|
||||
Specifications exist as multiple pages, one page per version,
|
||||
(discussed below in "Branching and Merging"),
|
||||
which should be assigned URIs that MAY include an number identifier.
|
||||
|
||||
Thus, we refer to new specifications by specifying its domain, its sub-domain and short name.
|
||||
The syntax for a new specification reference is:
|
||||
|
||||
<domain>/<sub-domain>/<shortname>
|
||||
|
||||
For example, this specification should be **rfc.vac.dev/vac/COSS**,
|
||||
if the status were **raw**.
|
||||
|
||||
A number will be assigned to the specification when obtaining **draft** status.
|
||||
New versions of the same specification will be assigned a new number.
|
||||
The syntax for a specification reference is:
|
||||
|
||||
<domain>/spec/<number>/<shortname>
|
||||
<domain>/<sub-domain>/<number>/<shortname>
|
||||
|
||||
For example, this specification is **rfc.vac.dev/spec/1/COSS**.
|
||||
For example, this specification is **rfc.vac.dev/vac/1/COSS**.
|
||||
The short form **1/COSS** may be used when referring to the specification from other specifications in the same domain.
|
||||
|
||||
Every specification (including branches) carries a different number.
|
||||
Specifications (excluding raw specifications) carries a different number including branches.
|
||||
|
||||
## COSS Lifecycle
|
||||
|
||||
Every specification has an independent lifecycle that documents clearly its current status.
|
||||
For a specification to receive a lifecycle status,
|
||||
a new specification SHOULD be presented by the team of the sub-domain.
|
||||
After discussion amongst the contributors has reached a rough consensus,
|
||||
as described in [RFC7282](https://www.rfc-editor.org/rfc/rfc7282.html),
|
||||
the specification MAY begin the process to upgrade it's status.
|
||||
|
||||
A specification has six possible states that reflect its maturity and contractual weight:
|
||||
A specification has five possible states that reflect its maturity and contractual weight:
|
||||
|
||||

|
||||
|
||||
@@ -110,12 +128,13 @@ A specification has six possible states that reflect its maturity and contractua
|
||||
|
||||
All new specifications are **raw** specifications.
|
||||
Changes to raw specifications can be unilateral and arbitrary.
|
||||
Those seeking to implement a raw specification should ask for it to be made a draft specification.
|
||||
A sub-domain MAY use the **raw** status for new specifications that live under their domain.
|
||||
Raw specifications have no contractual weight.
|
||||
|
||||
### Draft Specifications
|
||||
|
||||
When raw specifications can be demonstrated, they become **draft** specifications.
|
||||
When raw specifications can be demonstrated,
|
||||
they become **draft** specifications and are assigned numbers.
|
||||
Changes to draft specifications should be done in consultation with users.
|
||||
Draft specifications are contracts between the editors and implementers.
|
||||
|
||||
@@ -143,7 +162,7 @@ Retired specifications have no contractual weight.
|
||||
Deleted specifications are those that have not reached maturity (stable) and were discarded.
|
||||
They should not be used and are only kept for their historical value.
|
||||
Only Raw and Draft specifications can be deleted.
|
||||
|
||||
|
||||
## Editorial control
|
||||
|
||||
A specification MUST have a single responsible editor,
|
||||
@@ -157,12 +176,17 @@ Unlike the original C4 process however, it is RECOMMENDED to use CC0 as a more p
|
||||
We SHOULD NOT use GPL or GPL-like license.
|
||||
One exception is this specification, as this was the original license for this specification.
|
||||
|
||||
The editor is responsible for accurately maintaining the state of specifications and for handling all comments on the specification.
|
||||
The editor is responsible for accurately maintaining the state of specifications,
|
||||
for retiring different versions that may live in other places and
|
||||
for handling all comments on the specification.
|
||||
|
||||
## Branching and Merging
|
||||
|
||||
Any member of the domain MAY branch a specification at any point.
|
||||
This is done by copying the existing text, and creating a new specification with the same name and content, but a new number.
|
||||
Since **raw** specifications are not assigned a number,
|
||||
branching by any member of a sub-domain MAY differentiate specifications based on date, contributors, or
|
||||
version number within the document.
|
||||
The ability to branch a specification is necessary in these circumstances:
|
||||
|
||||
* To change the responsible editor for a specification, with or without the cooperation of the current responsible editor.
|
||||
@@ -203,9 +227,36 @@ This will enable programmatic access to specification metadata.
|
||||
| **editor** | editor name/email | string | Oskar Thoren <oskarth@titanproxy.com> |
|
||||
| **contributors** | contributors | list | - Pieter Hintjens <ph@imatix.com><br> - André Rebentisch <andre@openstandards.de><br> - Alberto Barrionuevo <abarrio@opentia.es><br> - Chris Puttick <chris.puttick@thehumanjourney.net><br> - Yurii Rashkovskii <yrashk@gmail.com> |
|
||||
|
||||
### Specification Template
|
||||
### IFT/Vac RFC Process
|
||||
|
||||
Standards Track specifications SHOULD be based on the [Vac RFC template](./images/template.md).
|
||||
> [!Note]
|
||||
This section is introduced to allow contributors to understand the IFT
|
||||
(Institute of Free Technology) Vac RFC specification process.
|
||||
Other organizations may make changes to this section according to their needs.
|
||||
|
||||
Vac is a department under the IFT organization that provides RFC (Request For Comments) specification services.
|
||||
This service works to help facilitate the RFC process, assuring standards are followed.
|
||||
Contributors within the service SHOULD assist a *sub-domain* in creating a new specification,
|
||||
editing a specification, and promoting the status of a specification along with other tasks.
|
||||
Once a specification reaches some level of maturity by rough consensus,
|
||||
the specification SHOULD enter the [Vac RFC](rfc.vac.dev) process.
|
||||
Similar to the IETF working group adoption described in [RFC6174](https://www.rfc-editor.org/rfc/rfc6174.html),
|
||||
the Vac RFC process SHOULD facilitate all updates to the specification.
|
||||
|
||||
Specifications are introduced by projects,
|
||||
under a specific *domain*, with the intention of becoming technically mature documents.
|
||||
The IFT domain currently houses the following projects:
|
||||
- [Status](status.app)
|
||||
- [Waku](https://waku.org/)
|
||||
- [Codex](https://codex.storage/)
|
||||
- [Nimbus](https://nimbus.team/)
|
||||
- [Nomos](https://nomos.tech/)
|
||||
|
||||
When a specification is promoted to *draft* status,
|
||||
the number that is assigned MAY be incremental
|
||||
or by the *sub-domain* and the Vac RFC process.
|
||||
Standards track specifications MUST be based on the [Vac RFC template](../template.md) before obtaining a new status.
|
||||
All changes, comments, and contributions SHOULD be documented.
|
||||
|
||||
## Conventions
|
||||
|
||||
|
||||
14
vac/2/images/batch.msc
Normal file
@@ -0,0 +1,14 @@
|
||||
# Alice and Bob: batch data sync
|
||||
msc {
|
||||
hscale="2", wordwraparcs=on;
|
||||
|
||||
alice [label="Alice"],
|
||||
bob [label="Bob"];
|
||||
|
||||
--- [label="batch data sync"];
|
||||
alice => alice [label="add messages to payload state"];
|
||||
alice >> bob [label="send payload with messages"];
|
||||
|
||||
bob => bob [label="add acks to payload state"];
|
||||
bob >> alice [label="send payload with acks"];
|
||||
}
|
||||
BIN
vac/2/images/batch.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
20
vac/2/images/interactive.msc
Normal file
@@ -0,0 +1,20 @@
|
||||
# Alice and Bob: interactive data sync
|
||||
msc {
|
||||
hscale="2", wordwraparcs=on;
|
||||
|
||||
alice [label="Alice"],
|
||||
bob [label="Bob"];
|
||||
|
||||
--- [label="interactive data sync"];
|
||||
alice => alice [label="add offers to payload state"];
|
||||
alice >> bob [label="send payload with offers"];
|
||||
|
||||
bob => bob [label="add requests to payload state"];
|
||||
bob >> alice [label="send payload with requests"];
|
||||
|
||||
alice => alice [label="add requested messages to state"];
|
||||
alice >> bob [label="send payload with messages"];
|
||||
|
||||
bob => bob [label="add acks to payload state"];
|
||||
bob >> alice [label="send payload with acks"];
|
||||
}
|
||||
BIN
vac/2/images/interactive.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
@@ -97,7 +97,7 @@ Nodes MAY have two modes with which they can send records: `BATCH` and `INTERACT
|
||||
- All records that require retransmission are added to the payload, given `Send Epoch` has been reached.
|
||||
|
||||
<p align="center">
|
||||
<img src="../assets/mvds/interactive.png" />
|
||||
<img src="./images/interactive.png" />
|
||||
<br />
|
||||
Figure 1: Delivery without retransmissions in interactive mode.
|
||||
</p>
|
||||
@@ -112,7 +112,7 @@ Nodes MAY have two modes with which they can send records: `BATCH` and `INTERACT
|
||||
<!-- diagram -->
|
||||
|
||||
<p align="center">
|
||||
<img src="../assets/mvds/batch.png" />
|
||||
<img src="./images/batch.png" />
|
||||
<br />
|
||||
Figure 2: Delivery without retransmissions in batch mode.
|
||||
</p>
|
||||
|
||||
411
vac/32/rln-v1.md
@@ -2,14 +2,15 @@
|
||||
slug: 32
|
||||
title: 32/RLN-V1
|
||||
name: Rate Limit Nullifier
|
||||
status: raw
|
||||
editor: Rasul Ibragimov <curryrasul@gmail.com>
|
||||
status: draft
|
||||
editor: Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
contributors:
|
||||
- Barry Whitehat <barrywhitehat@protonmail.com>
|
||||
- Sanaz Taheri <sanaz@status.im>
|
||||
- Oskar Thorén <oskarth@titanproxy.com>
|
||||
- Onur Kilic <onurkilic1004@gmail.com>
|
||||
- Blagoj Dimovski <blagoj.dimovski@yandex.com>
|
||||
- Rasul Ibragimov <curryrasul@gmail.com>
|
||||
---
|
||||
|
||||
## Abstract
|
||||
@@ -22,19 +23,25 @@ Anonymity refers to the unlinkability of messages to their owner.
|
||||
|
||||
RLN guarantees a messaging rate is enforced cryptographically while preserving the anonymity of the message owners.
|
||||
A wide range of applications can benefit from RLN and provide desirable security features.
|
||||
For example, an e-voting system can integrate RLN to contain the voting rate while protecting the voters-vote unlinkability.
|
||||
Another use case is to protect an anonymous messaging system against DDoS and spam attacks by containing messaging rate of users.
|
||||
For example,
|
||||
an e-voting system can integrate RLN to contain the voting rate while protecting the voters-vote unlinkability.
|
||||
Another use case is to protect an anonymous messaging system against DDoS and
|
||||
spam attacks by constraining messaging rate of users.
|
||||
This latter use case is explained in [17/WAKU2-RLN-RELAY RFC](../../waku/standards/core/17/rln-relay.md).
|
||||
|
||||
## Wire Format Specification
|
||||
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [2119](https://www.ietf.org/rfc/rfc2119.txt).
|
||||
|
||||
## Flow
|
||||
### Flow
|
||||
|
||||
The users participate in the protocol by first registering to an application-defined group referred by the _membership group_.
|
||||
Registration to the group is mandatory for signaling in the application.
|
||||
After registration, group members can generate Zero-knowledge Proof of membership for their signals and can participate in the application.
|
||||
Usually, the membership requires a financial or social stake which
|
||||
is beneficial for the prevention of Sybil attacks and double-signaling.
|
||||
Group members are allowed to send one signal per external nullifier (an identifier that groups signals and can be thought of as a voting booth).
|
||||
After registration, group members can generate a zero-knowledge proof of membership for their signals and
|
||||
can participate in the application.
|
||||
Usually, the membership requires a financial or
|
||||
social stake which is beneficial for the prevention of inclusion of Sybils within the _membership group_.
|
||||
Group members are allowed to send one signal per external nullifier
|
||||
(an identifier that groups signals and can be thought of as a voting booth).
|
||||
If a user generates more signals than allowed,
|
||||
the user risks being slashed - by revealing his membership secret credentials.
|
||||
If the financial stake is put in place, the user also risks his stake being taken.
|
||||
@@ -45,95 +52,108 @@ Generally the flow can be described by the following steps:
|
||||
2. Signaling
|
||||
3. Verification and slashing
|
||||
|
||||
|
||||
## Registration
|
||||
### Registration
|
||||
|
||||
Depending on the application requirements, the registration can be implemented in different ways, for example:
|
||||
- centralized registrations, by using a central server
|
||||
- decentralized registrations, by using a smart contract
|
||||
|
||||
What is important is that the users' identity commitments (explained in section [User Identity](#user-identity)) are stored in a Merkle tree,
|
||||
The users' identity commitments
|
||||
(explained in section [User Identity](#user-identity)) are stored in a Merkle tree,
|
||||
and the users can obtain a Merkle proof proving that they are part of the group.
|
||||
|
||||
Also depending on the application requirements,
|
||||
usually a financial or social stake is introduced.
|
||||
An example for financial stake is:
|
||||
|
||||
An example for financial stake is: For each registration a certain amount of ETH is required.
|
||||
An example for social stake is using InterRep as a registry -
|
||||
For each registration a certain amount of ETH is required.
|
||||
An example for social stake is using [Interep](https://interep.link/) as a registry -
|
||||
users need to prove that they have a highly reputable social media account.
|
||||
|
||||
### Implementation notes
|
||||
#### Implementation notes
|
||||
|
||||
#### User identity
|
||||
##### User identity
|
||||
|
||||
The user's identity is composed of:
|
||||
|
||||
```
|
||||
```js
|
||||
{
|
||||
identity_secret: [identity_nullifier, identity_trapdoor],
|
||||
identity_secret_hash: poseidonHash(identity_secret),
|
||||
identity_commitment: poseidonHash([identity_secret_hash])
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
For registration, the user needs to submit their `identity_commitment` (along with any additional registration requirements) to the registry.
|
||||
Upon registration, they should receive `leaf_index` value which represents their position in the Merkle tree.
|
||||
For registration, the user MUST submit their `identity_commitment`
|
||||
(along with any additional registration requirements) to the registry.
|
||||
Upon registration, they SHOULD receive `leaf_index` value which represents their position in the Merkle tree.
|
||||
Receiving a `leaf_index` is not a hard requirement and is application specific.
|
||||
The other way around is the users calculating the `leaf_index` themselves upon successful registration.
|
||||
|
||||
## Signaling
|
||||
### Signaling
|
||||
|
||||
After registration,
|
||||
the users can participate in the application by sending signals to the other participants in a decentralised manner or to a centralised server.
|
||||
the users can participate in the application by sending signals to the other participants in a decentralised manner or
|
||||
to a centralised server.
|
||||
Along with their signal,
|
||||
they need to generate a ZK-Proof by using the circuit with the specification described above.
|
||||
they MUST generate a zero-knowledge proof by using the circuit with the specification described above.
|
||||
|
||||
For generating a proof,
|
||||
the users need to obtain the required parameters or compute them themselves,
|
||||
depending on the application implementation and client libraries supported by the application.
|
||||
For example the users can store the membership Merkle tree on their end and
|
||||
For example,
|
||||
the users MAY store the membership Merkle tree on their end and
|
||||
generate a Merkle proof whenever they want to generate a signal.
|
||||
|
||||
### Implementation notes
|
||||
#### Implementation notes
|
||||
|
||||
#### Signal hash
|
||||
##### Signal hash
|
||||
|
||||
The signal hash can be generated by hashing the raw signal (or content) using the `keccak256` hash function.
|
||||
|
||||
#### External nullifier
|
||||
##### External nullifier
|
||||
|
||||
The external nullifier MUST be computed as the Poseidon hash of the current epoch (e.g. a value equal to or derived from the current UNIX timestamp divided by the epoch length) and the RLN identifier.
|
||||
The external nullifier MUST be computed as the Poseidon hash of the current epoch
|
||||
(e.g. a value equal to or derived from the current UNIX timestamp divided by the epoch length) and
|
||||
the RLN identifier.
|
||||
|
||||
```js
|
||||
|
||||
external_nullifier = poseidonHash([epoch, rln_identifier]);
|
||||
|
||||
```
|
||||
external_nullifier = poseidonHash([epoch, rln_identifier])
|
||||
```
|
||||
|
||||
#### Obtaining Merkle proof
|
||||
##### Obtaining Merkle proof
|
||||
|
||||
The Merkle proof should be obtained locally or from a trusted third party.
|
||||
The Merkle proof SHOULD be obtained locally or from a trusted third party.
|
||||
By using the [incremental Merkle tree algorithm](https://github.com/appliedzkp/incrementalquintree/blob/master/ts/IncrementalQuinTree.ts),
|
||||
the Merkle can be obtained by providing the `leaf_index` of the `identity_commitment`.
|
||||
The proof (`Merkle_proof`) is composed of the following fields:
|
||||
|
||||
```
|
||||
```js
|
||||
|
||||
{
|
||||
root: bigint
|
||||
indices: number[]
|
||||
root: bigint,
|
||||
indices: number[],
|
||||
path_elements: bigint[][]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
1. **root** - The root of membership group Merkle tree at the time of publishing the message
|
||||
2. **indices** - The index fields of the leafs in the Merkle tree - used by the Merkle tree algorithm for verification
|
||||
3. **path_elements** - Auxiliary data structure used for storing the path to the leaf - used by the Merkle proof algorithm for verificaton
|
||||
3. **path_elements** - Auxiliary data structure used for storing the path to the leaf -
|
||||
used by the Merkle proof algorithm for verificaton
|
||||
|
||||
|
||||
#### Generating proof
|
||||
##### Generating proof
|
||||
|
||||
For proof generation,
|
||||
the user need to submit the following fields to the circuit:
|
||||
the user MUST submit the following fields to the circuit:
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
{
|
||||
identity_secret: identity_secret_hash,
|
||||
path_elements: Merkle_proof.path_elements,
|
||||
@@ -141,10 +161,10 @@ the user need to submit the following fields to the circuit:
|
||||
x: signal_hash,
|
||||
external_nullifier: external_nullifier
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### Calculating output
|
||||
##### Calculating output
|
||||
|
||||
The proof output is calculated locally,
|
||||
in order for the required fields for proof verification to be sent along with the proof.
|
||||
@@ -152,55 +172,61 @@ The proof output is composed of the `y` share of the secret equation and the `in
|
||||
The `internal_nullifier` represents a unique fingerprint of a user for a given `epoch` and app.
|
||||
The following fields are needed for proof output calculation:
|
||||
|
||||
```
|
||||
```js
|
||||
{
|
||||
identity_secret_hash: bigint,
|
||||
external_nullifier: bigint,
|
||||
x: bigint,
|
||||
x: bigint
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The output `[y, internal_nullifier]` is calculated in the following way:
|
||||
|
||||
```
|
||||
a_0 = identity_secret_hash
|
||||
a_1 = poseidonHash([a0, external_nullifier])
|
||||
```js
|
||||
|
||||
y = a_0 + x * a_1
|
||||
a_0 = identity_secret_hash;
|
||||
a_1 = poseidonHash([a0, external_nullifier]);
|
||||
|
||||
y = a_0 + x * a_1;
|
||||
|
||||
internal_nullifier = poseidonHash([a_1]);
|
||||
|
||||
internal_nullifier = poseidonHash([a_1])
|
||||
```
|
||||
|
||||
It relies on the properties of the [Shamir's Secret sharing scheme](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing).
|
||||
|
||||
#### Sending the output message
|
||||
##### Sending the output message
|
||||
|
||||
The user's output message (`output_message`),
|
||||
containing the signal should contain the following fields at minimum:
|
||||
containing the signal SHOULD contain the following fields at minimum:
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
{
|
||||
signal: signal, # non-hashed signal
|
||||
signal: signal, # non-hashed signal,
|
||||
proof: zk_proof,
|
||||
internal_nullifier: internal_nullifier,
|
||||
x: x, # signal_hash
|
||||
x: x, # signal_hash,
|
||||
y: y,
|
||||
rln_identifier: rln_identifier
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Additionally depending on the application,
|
||||
the following fields might be required:
|
||||
the following fields MAY be required:
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
{
|
||||
root: Merkle_proof.root,
|
||||
epoch: epoch
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Verification and slashing
|
||||
### Verification and slashing
|
||||
|
||||
The slashing implementation is dependent on the type of application.
|
||||
If the application is implemented in a centralised manner,
|
||||
@@ -209,19 +235,22 @@ the slashing will be implemented only on the server.
|
||||
Otherwise if the application is distributed,
|
||||
the slashing will be implemented on each user's client.
|
||||
|
||||
### Implementation notes
|
||||
#### Implementation notes
|
||||
|
||||
Each user of the protocol (server or otherwise) will need to store metadata for each message received by each user,
|
||||
Each user of the protocol
|
||||
(server or otherwise) MUST store metadata for each message received by each user,
|
||||
for the given `epoch`.
|
||||
The data can be deleted when the `epoch` passes.
|
||||
Storing metadata is required, so that if a user sends more than one unique signal per `epoch`,
|
||||
Storing metadata is REQUIRED, so that if a user sends more than one unique signal per `epoch`,
|
||||
they can be slashed and removed from the protocol.
|
||||
The metadata stored contains the `x`, `y` shares and the `internal_nullifier` for the user for each message.
|
||||
The metadata stored contains the `x`, `y` shares and
|
||||
the `internal_nullifier` for the user for each message.
|
||||
If enough such shares are present, the user's secret can be retreived.
|
||||
|
||||
One way of storing received metadata (`messaging_metadata`) is the following format:
|
||||
|
||||
```
|
||||
```js
|
||||
|
||||
{
|
||||
[external_nullifier]: {
|
||||
[internal_nullifier]: {
|
||||
@@ -230,14 +259,15 @@ One way of storing received metadata (`messaging_metadata`) is the following for
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Verification
|
||||
##### Verification
|
||||
|
||||
The output message verification consists of the following steps:
|
||||
- `external_nullifier` correctness
|
||||
- non-duplicate message check
|
||||
- `zk_proof` verification
|
||||
- `zk_proof` zero-knowledge proof verification
|
||||
- spam verification
|
||||
|
||||
**1. `external_nullifier` correctness**
|
||||
@@ -247,16 +277,18 @@ If the `external_nullifier` is correct the verification continues, otherwise, th
|
||||
|
||||
**2. non-duplicate message check**
|
||||
The received message is checked to ensure it is not duplicate.
|
||||
The duplicate message check is performed by verifying that the `x` and `y` fields do not exist in the `messaging_metadata` object.
|
||||
The duplicate message check is performed by verifying that the `x` and `y`
|
||||
fields do not exist in the `messaging_metadata` object.
|
||||
If the `x` and `y` fields exist in the `x_shares` and `y_shares` array for the `external_nullifier` and
|
||||
the `internal_nullifier` the message can be considered as a duplicate.
|
||||
Duplicate messages are discarded.
|
||||
|
||||
**3. `zk_proof` verification**
|
||||
|
||||
The `zk_proof` should be verified by providing the `zk_proof` field to the circuit verifier along with the `public_signal`:
|
||||
The `zk_proof` SHOULD be verified by providing the `zk_proof` field to the circuit verifier along with the `public_signal`:
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
[
|
||||
y,
|
||||
Merkle_proof.root,
|
||||
@@ -264,6 +296,7 @@ The `zk_proof` should be verified by providing the `zk_proof` field to the circu
|
||||
x, # signal_hash
|
||||
external_nullifier
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
If the proof verification is correct,
|
||||
@@ -271,28 +304,30 @@ the verification continues, otherwise the message is discarded.
|
||||
|
||||
**4. Double signaling verification**
|
||||
|
||||
After the proof is verified the `x`, and `y` fields are added to the `x_shares` and `y_shares` arrays of the `messaging_metadata` `external_nullifier` and `internal_nullifier` object.
|
||||
After the proof is verified the `x`, and `y` fields are added to the `x_shares` and `y_shares`
|
||||
arrays of the `messaging_metadata` `external_nullifier` and `internal_nullifier` object.
|
||||
If the length of the arrays is equal to the signaling threshold (`limit`), the user can be slashed.
|
||||
|
||||
#### Slashing
|
||||
##### Slashing
|
||||
|
||||
After the verification, the user can be slashed if two different shares are present to reconstruct their `identity_secret_hash` from `x_shares` and `y_shares` fields,
|
||||
for their `internal_nullifier`.
|
||||
After the verification,
|
||||
the user SHOULD be slashed if two different shares are present to reconstruct their `identity_secret_hash` from `x_shares` and `y_shares` fields, for their `internal_nullifier`.
|
||||
The secret can be retreived by the properties of the Shamir's secret sharing scheme.
|
||||
In particular the secret (`a_0`) can be retrieved by computing [Lagrange polynomials](https://en.wikipedia.org/wiki/Lagrange_polynomial).
|
||||
|
||||
After the secret is retreived,
|
||||
the user's `identity_commitment` can be generated from the secret and it can be used for removing the user from the membership Merkle tree (zeroing out the leaf that contains the user's `identity_commitment`).
|
||||
Additionally, depending on the application the `identity_secret_hash` can be used for taking the user's provided stake.
|
||||
the user's `identity_commitment` SHOULD be generated from the secret and
|
||||
it can be used for removing the user from the membership Merkle tree
|
||||
(zeroing out the leaf that contains the user's `identity_commitment`).
|
||||
Additionally, depending on the application the `identity_secret_hash` MAY be used for taking the user's provided stake.
|
||||
|
||||
## Technical overview
|
||||
### Technical overview
|
||||
|
||||
The main RLN construct is implemented using a [ZK-SNARK](https://z.cash/technology/zksnarks/) circuit.
|
||||
However, it is helpful to describe the other necessary outside components for interaction with the circuit,
|
||||
which together with the ZK-SNARK circuit enable the above mentioned features.
|
||||
|
||||
|
||||
### Terminology
|
||||
#### Terminology
|
||||
|
||||
| Term | Description |
|
||||
|---------------------------|-------------------------------------------------------------------------------------|
|
||||
@@ -301,7 +336,7 @@ which together with the ZK-SNARK circuit enable the above mentioned features.
|
||||
| **Identity secret** | An array of two unique random components (identity nullifier and identity trapdoor), which must be kept private by the user. Secret hash and identity commitment are derived from this array. |
|
||||
| **Identity nullifier** | Random 32 byte value used as component for identity secret generation. |
|
||||
| **Identity trapdoor** | Random 32 byte value used as component for identity secret generation. |
|
||||
| **Identity secret hash** | The hash of the identity secret, obtained using the Poseidon hash function. It is used for deriving the identity commitment of the user, and as a private input for zk proof generation. The secret hash should be kept private by the user. |
|
||||
| **Identity secret hash** | The hash of the identity secret, obtained using the Poseidon hash function. It is used for deriving the identity commitment of the user, and as a private input for zero-knowledge proof generation. The secret hash should be kept private by the user. |
|
||||
| **Identity commitment** | Hash obtained from the `Identity secret hash` by using the poseidon hash function. It is used by the users for registering in the protocol. |
|
||||
| **Signal** | The message generated by a user. It is an arbitrary bit string that may represent a chat message, a URL request, protobuf message, etc. |
|
||||
| **Signal hash** | Keccak256 hash of the signal modulo circuit's field characteristic, used as an input in the RLN circuit. |
|
||||
@@ -310,7 +345,7 @@ which together with the ZK-SNARK circuit enable the above mentioned features.
|
||||
| **Merkle proof** | Proof that a user is member of the RLN membership tree. |
|
||||
|
||||
|
||||
### RLN ZK-Circuit specific terms
|
||||
#### RLN Zero-Knowledge Circuit specific terms
|
||||
|
||||
| Term | Description |
|
||||
|---------------------------|-------------------------------------------------------------------------------------|
|
||||
@@ -321,30 +356,27 @@ which together with the ZK-SNARK circuit enable the above mentioned features.
|
||||
| **External nullifier** | Poseidon hash of [Epoch, RLN Identifier]. An identifier that groups signals and can be thought of as a voting booth. |
|
||||
| **Internal nullifier** | Poseidon hash of [A1]. This field ensures that a user can send only one valid signal per external nullifier without risking being slashed. Public input of the circuit. |
|
||||
|
||||
|
||||
|
||||
### ZK Circuits specification
|
||||
#### Zero-Knowledge Circuits specification
|
||||
|
||||
Anonymous signaling with a controlled rate limit is enabled by proving that the user is part of a group which has high barriers to entry (form of stake) and
|
||||
enabling secret reveal if more than 1 unique signal is produced per external nullifier.
|
||||
The membership part is implemented using membership [Merkle trees](https://en.wikipedia.org/wiki/Merkle_tree) and Merkle proofs,
|
||||
The membership part is implemented using membership [Merkle trees](https://en.wikipedia.org/wiki/Merkle_tree) and Merkle proofs,
|
||||
while the secret reveal part is enabled by using the Shamir's Secret Sharing scheme.
|
||||
Essentially the protocol requires the users to generate zero-knowledge proof to be able to send signals and participate in the application.
|
||||
Essentially the protocol requires the users to generate zero-knowledge proof to be able to send signals and
|
||||
participate in the application.
|
||||
The zero knowledge proof proves that the user is member of a group,
|
||||
but also enforces the user to share part of their secret for each signal in an external nullifier.
|
||||
The external nullifier is usually represented by timestamp or a time interval.
|
||||
It can also be thought of as a voting booth in voting applications.
|
||||
|
||||
The ZK Circuit is implemented using a [Groth-16 ZK-SNARK](https://eprint.iacr.org/2016/260.pdf),
|
||||
The zero-knowledge Circuit is implemented using a [Groth-16 ZK-SNARK](https://eprint.iacr.org/2016/260.pdf),
|
||||
using the [circomlib](https://docs.circom.io/) library.
|
||||
|
||||
|
||||
#### System parameters
|
||||
##### System parameters
|
||||
|
||||
- `DEPTH` - Merkle tree depth
|
||||
|
||||
|
||||
#### Circuit parameters
|
||||
##### Circuit parameters
|
||||
|
||||
**Public Inputs**
|
||||
- `x`
|
||||
@@ -360,7 +392,7 @@ using the [circomlib](https://docs.circom.io/) library.
|
||||
- `root` - the rln membership tree root
|
||||
- `internal_nullifier`
|
||||
|
||||
#### Hash function
|
||||
##### Hash function
|
||||
|
||||
Canonical [Poseidon hash implementation](https://eprint.iacr.org/2019/458.pdf) is used,
|
||||
as implemented in the [circomlib library](https://github.com/iden3/circomlib/blob/master/circuits/poseidon.circom), according to the Poseidon paper.
|
||||
@@ -377,54 +409,59 @@ This Poseidon hash version (canonical implementation) uses the following paramet
|
||||
|7 | 8 | 8 | 64|
|
||||
|8 | 9 | 8 | 63|
|
||||
|
||||
##### Membership implementation
|
||||
|
||||
#### Membership implementation
|
||||
|
||||
For a valid signal, a user's `identity_commitment` (more on identity commitments below) must exist in identity membership tree.
|
||||
For a valid signal, a user's `identity_commitment`
|
||||
(more on identity commitments below) must exist in identity membership tree.
|
||||
Membership is proven by providing a membership proof (witness).
|
||||
The fields from the membership proof required for the verification are:
|
||||
The fields from the membership proof REQUIRED for the verification are:
|
||||
`path_elements` and `identity_path_index`.
|
||||
|
||||
[IncrementalQuinTree](https://github.com/appliedzkp/incrementalquintree) algorithm is used for constructing the Membership Merkle tree.
|
||||
The circuits are reused from this repository.
|
||||
You can find out more details about the IncrementalQuinTree algorithm [here](https://ethresear.ch/t/gas-and-circuit-constraint-benchmarks-of-binary-and-quinary-incremental-Merkle-trees-using-the-poseidon-hash-function/7446).
|
||||
|
||||
### Slashing and Shamir's Secret Sharing
|
||||
#### Slashing and Shamir's Secret Sharing
|
||||
|
||||
Slashing is enabled by using polynomials and [Shamir's Secret sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing).
|
||||
In order to produce a valid proof, `identity_secret_hash` as a private input to the circuit.
|
||||
Then a secret equation is created in the form of:
|
||||
|
||||
```
|
||||
y = a_0 + x * a_1,
|
||||
```js
|
||||
|
||||
y = a_0 + x * a_1;
|
||||
|
||||
```
|
||||
|
||||
where `a_0` is the `identity_secret_hash` and `a_1 = hash(a_0, external nullifier)`.
|
||||
Along with the generated proof,
|
||||
the users need to provide a `(x, y)` share which satisfies the line equation,
|
||||
the users MUST provide a `(x, y)` share which satisfies the line equation,
|
||||
in order for their proof to be verified.
|
||||
`x` is the hashed signal, while the `y` is the circuit output.
|
||||
With more than one pair of unique shares, anyone can derive `a_0`, i.e. the `identity_secret_hash` .
|
||||
With more than one pair of unique shares, anyone can derive `a_0`, i.e. the `identity_secret_hash`.
|
||||
The hash of a signal will be the evaluation point `x`.
|
||||
In this way, a member who sends more than one unique signal per `external_nullifier` risks their identity secret being revealed.
|
||||
In this way,
|
||||
a member who sends more than one unique signal per `external_nullifier` risks their identity secret being revealed.
|
||||
|
||||
Note that shares used in different epochs and different RLN apps cannot be used to derive the identity secret hash.
|
||||
Note that shares used in different epochs and
|
||||
different RLN apps cannot be used to derive the `identity_secret_hash`.
|
||||
|
||||
Thanks to the `external_nullifier` definition, also shares computed from same secret within same epoch but in different RLN apps cannot be used to derive the identity secret hash.
|
||||
|
||||
The `rln_identifier` is a random value from a finite field,
|
||||
unique per RLN app,
|
||||
and is used for additional cross-application security - to protect the user secrets being compromised if they use the same credentials accross different RLN apps.
|
||||
The `rln_identifier` is a random value from a finite field, unique per RLN app,
|
||||
and is used for additional cross-application security -
|
||||
to protect the user secrets being compromised if they use the same credentials accross different RLN apps.
|
||||
If `rln_identifier` is not present,
|
||||
the user uses the same credentials and sends a different message for two different RLN apps using the same `external_nullifier`,
|
||||
the user uses the same credentials and
|
||||
sends a different message for two different RLN apps using the same `external_nullifier`,
|
||||
then their user signals can be grouped by the `internal_nullifier` which could lead the user's secret revealed.
|
||||
This is because two separate signals under the same `internal_nullifier` can be treated as rate limiting violation.
|
||||
With adding the `rln_identifier` field we obscure the `internal_nullifier`,
|
||||
so this kind of attack can be hardened because we don't have the same `internal_nullifier` anymore.
|
||||
|
||||
### Identity credentials generation
|
||||
#### Identity credentials generation
|
||||
|
||||
In order to be able to generate valid proofs, the users need to be part of the identity membership Merkle tree.
|
||||
In order to be able to generate valid proofs, the users MUST be part of the identity membership Merkle tree.
|
||||
They are part of the identity membership Merkle tree if their `identity_commitment` is placed in a leaf in the tree.
|
||||
|
||||
The identity credentials of a user are composed of:
|
||||
@@ -433,132 +470,146 @@ The identity credentials of a user are composed of:
|
||||
- `identity_secret_hash`
|
||||
- `identity_commitment`
|
||||
|
||||
#### `identity_secret`
|
||||
##### `identity_secret`
|
||||
|
||||
The `identity_secret` is generated in the following way:
|
||||
|
||||
```
|
||||
identity_nullifier = random_32_byte_buffer
|
||||
identity_trapdoor = random_32_byte_buffer
|
||||
identity_secret = [identity_nullifier, identity_trapdoor]
|
||||
```js
|
||||
|
||||
identity_nullifier = random_32_byte_buffer;
|
||||
identity_trapdoor = random_32_byte_buffer;
|
||||
identity_secret = [identity_nullifier, identity_trapdoor];
|
||||
|
||||
```
|
||||
|
||||
The same secret should not be used accross different protocols,
|
||||
The same secret SHOULD NOT be used accross different protocols,
|
||||
because revealing the secret at one protocol could break privacy for the user in the other protocols.
|
||||
|
||||
#### `identity_secret_hash`
|
||||
##### `identity_secret_hash`
|
||||
|
||||
The `identity_secret_hash` is generated by obtaining a Poseidon hash of the `identity_secret` array:
|
||||
|
||||
```
|
||||
identity_secret_hash = poseidonHash(identity_secret)
|
||||
```js
|
||||
|
||||
identity_secret_hash = poseidonHash(identity_secret);
|
||||
|
||||
```
|
||||
|
||||
#### `identity_commitment`
|
||||
##### `identity_commitment`
|
||||
|
||||
The `identity_commitment` is generated by obtaining a Poseidon hash of the `identity_secret_hash`:
|
||||
|
||||
```
|
||||
identity_commitment = poseidonHash([identity_secret_hash])
|
||||
```js
|
||||
|
||||
identity_commitment = poseidonHash([identity_secret_hash]);
|
||||
|
||||
```
|
||||
|
||||
### Appendix A: Security Considerations
|
||||
|
||||
## Appendix A: Security considerations
|
||||
|
||||
RLN is an experimental and still un-audited technology. This means that the circuits have not been yet audited.
|
||||
RLN is an experimental and still un-audited technology.
|
||||
This means that the circuits have not been yet audited.
|
||||
Another consideration is the security of the underlying primitives.
|
||||
zk-SNARKS require a trusted setup for generating a prover and verifier keys.
|
||||
The standard for this is to use trusted [Multi-Party Computation (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation) ceremony,
|
||||
which requires two phases.
|
||||
Trusted MPC ceremony has not yet been performed for the RLN circuits.
|
||||
|
||||
### SSS security assumptions
|
||||
#### SSS Security Assumptions
|
||||
|
||||
Shamir-Secret Sharing requires polynomial coefficients to be independent of each other.
|
||||
However, `a_1` depends on `a_0` through the Poseidon hash algorithm.
|
||||
Due to the design of Poseidon, it is possible to [attack](https://github.com/Rate-Limiting-Nullifier/rln-circuits/pull/7#issuecomment-1416085627) the protocol.
|
||||
It was decided *not* to change the circuits design, since at the moment the attack is infeasible. Therefore, implementers must be aware that the current version provides approximately 160-bit security and not 254.
|
||||
Due to the design of Poseidon,
|
||||
it is possible to [attack](https://github.com/Rate-Limiting-Nullifier/rln-circuits/pull/7#issuecomment-1416085627) the protocol.
|
||||
It was decided *not* to change the circuits design, since at the moment the attack is infeasible.
|
||||
Therefore, implementers must be aware that the current version provides approximately 160-bit security and not 254.
|
||||
Possible improvements:
|
||||
* [change the circuit](https://github.com/Rate-Limiting-Nullifier/rln-circuits/pull/7#issuecomment-1416085627) to make coefficients independent;
|
||||
* switch to other hash function (Keccak, SHA);
|
||||
|
||||
## Appendix B: Identity scheme choice
|
||||
### Appendix B: Identity Scheme Choice
|
||||
|
||||
The hashing scheme used is based on the design decisions which also include the Semaphore circuits.
|
||||
Our goal was to ensure compatibility of the secrets for apps that use Semaphore and
|
||||
RLN circuits while also not compromising on security because of using the same secrets.
|
||||
|
||||
For example let's say there is a voting app that uses Semaphore,
|
||||
For example, let's say there is a voting app that uses Semaphore,
|
||||
and also a chat app that uses RLN.
|
||||
The UX would be better if the users would not need to care about complicated identity management (secrets and commitments) t
|
||||
hey use for each app, and it would be much better if they could use a single id commitment for this.
|
||||
The UX would be better if the users would not need to care about complicated identity management
|
||||
(secrets and commitments) they use for each app,
|
||||
and it would be much better if they could use a single id commitment for this.
|
||||
Also in some cases these kind of dependency is required -
|
||||
RLN chat app using Interep as a registry (instead of using financial stake).
|
||||
One potential concern about this interoperability is a slashed user on the RLN app side
|
||||
having their security compromised on the semaphore side apps as well.
|
||||
I.e obtaining the user's secret, anyone would be able to generate valid semaphore proofs as the slashed user.
|
||||
We don't want that, and we should keep user's app specific security threats in the domain of that app alone.
|
||||
I.e obtaining the user's secret,
|
||||
anyone would be able to generate valid semaphore proofs as the slashed user.
|
||||
We don't want that,
|
||||
and we should keep user's app specific security threats in the domain of that app alone.
|
||||
|
||||
To achieve the above interoperability UX while preventing the shared app security model
|
||||
(i.e slashing user on an RLN app having impact on Semaphore apps),
|
||||
we had to do the follow in regard the identity secret and identity commitment:
|
||||
|
||||
```
|
||||
identity_secret = [identity_nullifier, identity_trapdoor]
|
||||
identity_secret_hash = poseidonHash(identity_secret)
|
||||
identity_commitment = poseidonHash([identity_secret_hash])
|
||||
```js
|
||||
|
||||
identity_secret = [identity_nullifier, identity_trapdoor];
|
||||
identity_secret_hash = poseidonHash(identity_secret);
|
||||
identity_commitment = poseidonHash([identity_secret_hash]);
|
||||
|
||||
```
|
||||
|
||||
Secret components for generating Semaphore proof:
|
||||
|
||||
```
|
||||
identity_nullifier
|
||||
identity_trapdoor
|
||||
```
|
||||
- `identity_nullifier`
|
||||
- `identity_trapdoor`
|
||||
|
||||
Secret components for generting RLN proof:
|
||||
|
||||
```
|
||||
identity_secret_hash
|
||||
```
|
||||
- `identity_secret_hash`
|
||||
|
||||
When a user is slashed on the RLN app side, their identity secret hash is revealed.
|
||||
However a semaphore proof can't be generated because we do not know the user's nullifier and trapdoor.
|
||||
When a user is slashed on the RLN app side, their `identity_secret_hash` is revealed.
|
||||
However, a semaphore proof can't be generated because
|
||||
we do not know the user's `identity_nullifier` and `identity_trapdoor`.
|
||||
|
||||
With this design we achieve:
|
||||
|
||||
identity commitment (Semaphore) == identity commitment (RLN)
|
||||
|
||||
`identity_commitment` (Semaphore) == `identity_commitment` (RLN)
|
||||
secret (semaphore) != secret (RLN).
|
||||
|
||||
This is the only option we had for the scheme in order to satisfy the properties described above.
|
||||
|
||||
Also for RLN we do a single secret component input for the circuit.
|
||||
Also, for RLN we do a single secret component input for the circuit.
|
||||
Thus we need to hash the secret array (two components) to a secret hash,
|
||||
and we use that as a secret component input.
|
||||
|
||||
## Appendix C: Auxiliary tooling
|
||||
### Appendix C: Auxiliary Tooling
|
||||
|
||||
There are few additional tools implemented for easier integrations and usage of the RLN protocol.
|
||||
|
||||
[`zerokit`](https://github.com/vacp2p/zerokit) is a set of Zero Knowledge modules, written in Rust and designed to be used in many different environments.
|
||||
[`zerokit`](https://github.com/vacp2p/zerokit) is a set of Zero Knowledge modules,
|
||||
written in Rust and designed to be used in many different environments.
|
||||
Among different modules, it supports `Semaphore` and `RLN`.
|
||||
|
||||
[`zk-kit`](https://github.com/appliedzkp/zk-kit) is a typescript library which exposes APIs for identity credentials generation,
|
||||
as well as proof generation.
|
||||
It supports various protocols (`Semaphore`, `RLN`).
|
||||
|
||||
[`zk-keeper`](https://github.com/akinovak/zk-keeper) is a browser plugin which allows for safe credential storing and proof generation.
|
||||
You can think of MetaMask for ZK-Proofs.
|
||||
[`zk-keeper`](https://github.com/akinovak/zk-keeper) is a browser plugin which allows for safe credential storing and
|
||||
proof generation.
|
||||
You can think of MetaMask for zero-knowledge proofs.
|
||||
It uses `zk-kit` under the hood.
|
||||
|
||||
## Appendix D: Example usage
|
||||
### Appendix D: Example Usage
|
||||
|
||||
The following examples are code snippets using the `zerokit` RLN module.
|
||||
The examples are written in [rust](https://www.rust-lang.org/).
|
||||
|
||||
### Creating a RLN object
|
||||
#### Creating a RLN Object
|
||||
|
||||
```rust
|
||||
|
||||
use rln::protocol::*;
|
||||
use rln::public::*;
|
||||
use std::io::Cursor;
|
||||
@@ -569,42 +620,50 @@ let tree_height = 20;
|
||||
let resources = Cursor::new("../zerokit/rln/resources/tree_height_20/");
|
||||
// We create a new RLN instance
|
||||
let mut rln = RLN::new(tree_height, resources);
|
||||
|
||||
```
|
||||
|
||||
### Generating identity credentials
|
||||
#### Generating Identity Credentials
|
||||
|
||||
```rust
|
||||
|
||||
// We generate an identity tuple
|
||||
let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.extended_key_gen(&mut buffer).unwrap();
|
||||
// We deserialize the keygen output to obtain
|
||||
// the identiy_secret and id_commitment
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) = deserialize_identity_tuple(buffer.into_inner());
|
||||
|
||||
```
|
||||
|
||||
### Adding ID commitment to the RLN Merkle tree
|
||||
#### Adding ID Commitment to the RLN Merkle Tree
|
||||
|
||||
```rust
|
||||
|
||||
// We define the tree index where id_commitment will be added
|
||||
let id_index = 10;
|
||||
// We serialize id_commitment and pass it to set_leaf
|
||||
let mut buffer = Cursor::new(serialize_field_element(id_commitment));
|
||||
rln.set_leaf(id_index, &mut buffer).unwrap();
|
||||
|
||||
```
|
||||
|
||||
### Setting epoch and signal
|
||||
#### Setting Epoch and Signal
|
||||
|
||||
```rust
|
||||
|
||||
// We generate epoch from a date seed and we ensure is
|
||||
// mapped to a field element by hashing-to-field its content
|
||||
let epoch = hash_to_field(b"Today at noon, this year");
|
||||
// We set our signal
|
||||
let signal = b"RLN is awesome";
|
||||
|
||||
```
|
||||
|
||||
### Generating proof
|
||||
#### Generating Proof
|
||||
|
||||
```rust
|
||||
|
||||
// We prepare input to the proof generation routine
|
||||
let proof_input = prepare_prove_input(identity_secret, id_index, epoch, signal);
|
||||
// We generate a RLN proof for proof_input
|
||||
@@ -614,18 +673,21 @@ rln.generate_rln_proof(&mut in_buffer, &mut out_buffer)
|
||||
.unwrap();
|
||||
// We get the public outputs returned by the circuit evaluation
|
||||
let proof_data = out_buffer.into_inner();
|
||||
|
||||
```
|
||||
|
||||
### Verifiying proof
|
||||
#### Verifiying Proof
|
||||
|
||||
```rust
|
||||
|
||||
// We prepare input to the proof verification routine
|
||||
let verify_data = prepare_verify_input(proof_data, signal);
|
||||
// We verify the zk-proof against the provided proof values
|
||||
// We verify the zero-knowledge proof against the provided proof values
|
||||
let mut in_buffer = Cursor::new(verify_data);
|
||||
let verified = rln.verify(&mut in_buffer).unwrap();
|
||||
// We ensure the proof is valid
|
||||
assert!(verified);
|
||||
|
||||
```
|
||||
|
||||
For more details please visit the [`zerokit`](https://github.com/vacp2p/zerokit) library.
|
||||
@@ -636,17 +698,28 @@ Copyright and related rights waived via [CC0](https://creativecommons.org/public
|
||||
|
||||
## References
|
||||
|
||||
- [17/WAKU2-RLN-RELAY RFC](../../waku/standards/core/17/rln-relay.md)
|
||||
- [Interep](https://interep.link/)
|
||||
- [incremental Merkle tree algorithm](https://github.com/appliedzkp/incrementalquintree/blob/master/ts/IncrementalQuinTree.ts)
|
||||
- [Shamir's Secret sharing scheme](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing)
|
||||
- [Lagrange polynomials](https://en.wikipedia.org/wiki/Lagrange_polynomial)
|
||||
- [ZK-SNARK](https://z.cash/technology/zksnarks/)
|
||||
- [Merkle trees](https://en.wikipedia.org/wiki/Merkle_tree)
|
||||
- [Groth-16 ZK-SNARK](https://eprint.iacr.org/2016/260.pdf)
|
||||
- [circomlib](https://docs.circom.io/)
|
||||
- [Poseidon hash implementation](https://eprint.iacr.org/2019/458.pdf)
|
||||
- [circomlib library](https://github.com/iden3/circomlib/blob/master/circuits/poseidon.circom)
|
||||
- [IncrementalQuinTree](https://github.com/appliedzkp/incrementalquintree)
|
||||
- [IncrementalQuinTree algorithm](https://ethresear.ch/t/gas-and-circuit-constraint-benchmarks-of-binary-and-quinary-incremental-Merkle-trees-using-the-poseidon-hash-function/7446)
|
||||
- [Multi-Party Computation (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation)
|
||||
- [Poseidon hash attack](https://github.com/Rate-Limiting-Nullifier/rln-circuits/pull/7#issuecomment-1416085627)
|
||||
- [zerokit](https://github.com/vacp2p/zerokit)
|
||||
- [zk-kit](https://github.com/appliedzkp/zk-kit)
|
||||
- [zk-keeper](https://github.com/akinovak/zk-keeper)
|
||||
- [rust](https://www.rust-lang.org/)
|
||||
|
||||
### Informative
|
||||
- [1] https://medium.com/privacy-scaling-explorations/rate-limiting-nullifier-a-spam-protection-mechanism-for-anonymous-environments-bbe4006a57d
|
||||
- [2] https://github.com/appliedzkp/zk-kit
|
||||
- [3] https://github.com/akinovak/zk-keeper
|
||||
- [4] https://z.cash/technology/zksnarks/
|
||||
- [5] https://en.wikipedia.org/wiki/Merkle_tree
|
||||
- [6] https://eprint.iacr.org/2016/260.pdf
|
||||
- [7] https://docs.circom.io/
|
||||
- [8] https://eprint.iacr.org/2019/458.pdf
|
||||
- [9] https://github.com/appliedzkp/incrementalquintree
|
||||
- [10] https://ethresear.ch/t/gas-and-circuit-constraint-benchmarks-of-binary-and-quinary-incremental-merkle-trees-using-the-poseidon-hash-function/7446
|
||||
- [11] https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing
|
||||
- [12] https://research.nccgroup.com/2020/06/24/security-considerations-of-zk-snark-parameter-multi-party-computation/
|
||||
- [13] https://github.com/Rate-Limiting-Nullifier/rln-circuits/
|
||||
- [14] https://rate-limiting-nullifier.github.io/rln-docs/
|
||||
- [2] https://research.nccgroup.com/2020/06/24/security-considerations-of-zk-snark-parameter-multi-party-computation/
|
||||
- [3] https://github.com/Rate-Limiting-Nullifier/rln-circuits/
|
||||
- [4] https://rate-limiting-nullifier.github.io/rln-docs/
|
||||
|
||||
@@ -1,810 +0,0 @@
|
||||
---
|
||||
slug: 70
|
||||
title: 70/ETH-SECPM
|
||||
name: Secure channel setup using Ethereum accounts
|
||||
status: raw
|
||||
category: Standards Track
|
||||
tags:
|
||||
editor: Ramses Fernandez <ramses@status.im>
|
||||
contributors:
|
||||
---
|
||||
|
||||
## Motivation
|
||||
The need for secure communications has become paramount.
|
||||
Traditional centralized messaging protocols are susceptible to various security threats,
|
||||
including unauthorized access, data breaches, and single points of failure.
|
||||
Therefore a decentralized approach to secure communication becomes increasingly relevant,
|
||||
offering a robust solution to address these challenges.
|
||||
|
||||
This specification outlines a private messaging service using the Ethereum blockchain as authentication service.
|
||||
Rooted in the existing [model](../../waku/standards/application/20/toy-eth-pm.md),
|
||||
this proposal addresses the deficiencies related to forward privacy and authentication inherent in the current framework.
|
||||
The specification is divided into 3 sections:
|
||||
|
||||
- Private 1-to-1 communications protocol, based on [Signal's double ratchet](https://signal.org/docs/specifications/doubleratchet/).
|
||||
- Private group messaging protocol, based on the [MLS protocol](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
- Description of an Ethereum-based authentication protocol, based on [SIWE](https://eips.ethereum.org/EIPS/eip-4361).
|
||||
|
||||
|
||||
## Private 1-to-1 communications protocol
|
||||
|
||||
### Theory
|
||||
The specification is based on the noise protocol framework.
|
||||
It corresponds to the double ratchet scheme combined with the X3DH algorithm, which will be used to initialize the former.
|
||||
We chose to express the protocol in noise to be be able to use the noise streamlined implementation and proving features.
|
||||
The X3DH algorithm provides both authentication and forward secrecy, as stated in the [X3DH specification](https://signal.org/docs/specifications/x3dh/).
|
||||
|
||||
This protocol will consist of several stages:
|
||||
|
||||
1. Key setting for X3DH: this step will produce prekey bundles for Bob which will be fed into X3DH. It will also allow Alice to generate the keys required to run the X3DH algorithm correctly.
|
||||
2. Execution of X3DH: This step will output a common secret key `SK` together with an additional data vector `AD`. Both will be used in the double ratchet algorithm initialization.
|
||||
3. Execution of the double ratchet algorithm for forward secure, authenticated communications, using the common secret key `SK`, obtained from X3DH, as a root key.
|
||||
|
||||
The protocol assumes the following requirements:
|
||||
- Alice knows Bob’s Ethereum address.
|
||||
- Bob is willing to participate in the protocol, and publishes his public key.
|
||||
- Bob’s ownership of his public key is verifiable,
|
||||
- Alice wants to send message M to Bob.
|
||||
- An eavesdropper cannot read M’s content even if she is storing it or relaying it.
|
||||
|
||||
### Syntax
|
||||
#### Cryptographic suite
|
||||
The following cryptographic functions MUST be used:
|
||||
- `X488` as Diffie-Hellman function `DH`.
|
||||
- `SHA256` as KDF.
|
||||
- `AES256-GCM` as AEAD algorithm.
|
||||
- `SHA512` as hash function.
|
||||
- `XEd448` for digital signatures.
|
||||
|
||||
#### X3DH initialization
|
||||
This scheme MUST work on the curve curve448.
|
||||
The X3DH algorithm corresponds to the IX pattern in Noise.
|
||||
|
||||
Bob and Alice MUST define personal key pairs `(ik_B, IK_B)` and `(ik_A, IK_A)` respectively where:
|
||||
- The key `ik` must be kept secret,
|
||||
- and the key `IK` is public.
|
||||
|
||||
Bob MUST generate new keys using `(ik_B, IK_B) = GENERATE_KEYPAIR(curve = curve448)`.
|
||||
|
||||
Bob MUST also generate a public key pair `(spk_B, SPK_B) = GENERATE_KEYPAIR(curve = curve448)`.
|
||||
|
||||
`SPK` is a public key generated and stored at medium-term.
|
||||
Both signed prekey and the certificate MUST undergo periodic replacement.
|
||||
After replacing the key,
|
||||
Bob keeps the old private key of `SPK` for some interval, dependant on the implementation.
|
||||
This allows Bob to decrypt delayed messages.
|
||||
|
||||
Bob MUST sign `SPK` for authentication: `SigSPK = XEd448(ik, Encode(SPK))`
|
||||
|
||||
A final step requires the definition of `prekey_bundle = (IK, SPK, SigSPK, OPK_i)`
|
||||
|
||||
One-time keys `OPK` MUST be generated as `(opk_B, OPK_B) = GENERATE_KEYPAIR(curve = curve448)`.
|
||||
|
||||
Before sending an initial message to Bob, Alice MUST generate an AD: `AD = Encode(IK_A) || Encode(IK_B)`.
|
||||
|
||||
Alice MUST generate ephemeral key pairs `(ek, EK) = GENERATE_KEYPAIR(curve = curve448)`.
|
||||
|
||||
The function `Encode()` transforms an curve448 public key into a byte sequence.
|
||||
This is specified in the [RFC 7748](http://www.ietf.org/rfc/rfc7748.txt) on elliptic curves for security.
|
||||
|
||||
One MUST consider `q = 2^446 - 13818066809895115352007386748515426880336692474882178609894547503885` for digital signatures with `(XEd448_sign, XEd448_verify)`:
|
||||
```
|
||||
XEd448_sign((ik, IK), message):
|
||||
Z = randbytes(64)
|
||||
r = SHA512(2^456 - 2 || ik || message || Z )
|
||||
R = (r * convert_mont(5)) % q
|
||||
h = SHA512(R || IK || M)
|
||||
s = (r + h * ik) % q
|
||||
return (R || s)
|
||||
```
|
||||
```
|
||||
XEd448_verify(u, message, (R || s)):
|
||||
if (R.y >= 2^448) or (s >= 2^446): return FALSE
|
||||
h = (SHA512(R || 156326 || message)) % q
|
||||
R_check = s * convert_mont(5) - h * 156326
|
||||
if R == R_check: return TRUE
|
||||
return FALSE
|
||||
```
|
||||
```
|
||||
convert_mont(u):
|
||||
u_masked = u % mod 2^448
|
||||
inv = ((1 - u_masked)^(2^448 - 2^224 - 3)) % (2^448 - 2^224 - 1)
|
||||
P.y = ((1 + u_masked) * inv)) % (2^448 - 2^224 - 1)
|
||||
P.s = 0
|
||||
return P
|
||||
```
|
||||
|
||||
#### Use of X3DH
|
||||
This specification combines the double ratchet with X3DH using the following data as initialization for the former:
|
||||
|
||||
- The `SK` output from X3DH becomes the `SK` input of the double ratchet. See section 3.3 of [Signal Specification](https://signal.org/docs/specifications/doubleratchet/) for a detailed description.
|
||||
- The `AD` output from X3DH becomes the `AD` input of the double ratchet. See sections 3.4 and 3.5 of [Signal Specification](https://signal.org/docs/specifications/doubleratchet/) for a detailed description.
|
||||
- Bob’s signed prekey `SigSPKB` from X3DH is used as Bob’s initial ratchet public key of the double ratchet.
|
||||
|
||||
X3DH has three phases:
|
||||
|
||||
1. Bob publishes his identity key and prekeys to a server, a network, or dedicated smart contract.
|
||||
2. Alice fetches a prekey bundle from the server, and uses it to send an initial message to Bob.
|
||||
3. Bob receives and processes Alice's initial message.
|
||||
|
||||
Alice MUST perform the following computations:
|
||||
```
|
||||
dh1 = DH(IK_A, SPK_B, curve = curve448)
|
||||
dh2 = DH(EK_A, IK_B, curve = curve448)
|
||||
dh3 = DH(EK_A, SPK_B)
|
||||
SK = KDF(dh1 || dh2 || dh3)
|
||||
```
|
||||
Alice MUST send to Bob a message containing:
|
||||
|
||||
- `IK_A, EK_A`.
|
||||
- An identifier to Bob's prekeys used.
|
||||
- A message encrypted with AES256-GCM using `AD` and `SK`.
|
||||
|
||||
Upon reception of the initial message, Bob MUST:
|
||||
1. Perform the same computations above with the `DH()` function.
|
||||
2. Derive `SK` and construct `AD`.
|
||||
3. Decrypt the initial message encrypted with `AES256-GCM`.
|
||||
4. If decryption fails, abort the protocol.
|
||||
|
||||
#### Initialization of the double datchet
|
||||
In this stage Bob and Alice have generated key pairs and agreed a shared secret `SK` using X3DH.
|
||||
|
||||
Alice calls `RatchetInitAlice()` defined below:
|
||||
```
|
||||
RatchetInitAlice(SK, IK_B):
|
||||
state.DHs = GENERATE_KEYPAIR(curve = curve448)
|
||||
state.DHr = IK_B
|
||||
state.RK, state.CKs = HKDF(SK, DH(state.DHs, state.DHr))
|
||||
state.CKr = None
|
||||
state.Ns, state.Nr, state.PN = 0
|
||||
state.MKSKIPPED = {}
|
||||
```
|
||||
The HKDF function MUST be the proposal by [Krawczyk and Eronen](http://www.ietf.org/rfc/rfc5869.txt).
|
||||
In this proposal `chaining_key` and `input_key_material` MUST be replaced with `SK` and the output of `DH` respectively.
|
||||
|
||||
Similarly, Bob calls the function `RatchetInitBob()` defined below:
|
||||
```
|
||||
RatchetInitBob(SK, (ik_B,IK_B)):
|
||||
state.DHs = (ik_B, IK_B)
|
||||
state.Dhr = None
|
||||
state.RK = SK
|
||||
state.CKs, state.CKr = None
|
||||
state.Ns, state.Nr, state.PN = 0
|
||||
state.MKSKIPPED = {}
|
||||
```
|
||||
#### Encryption
|
||||
This function performs the symmetric key ratchet.
|
||||
|
||||
```
|
||||
RatchetEncrypt(state, plaintext, AD):
|
||||
state.CKs, mk = HMAC-SHA256(state.CKs)
|
||||
header = HEADER(state.DHs, state.PN, state.Ns)
|
||||
state.Ns = state.Ns + 1
|
||||
return header, AES256-GCM_Enc(mk, plaintext, AD || header)
|
||||
```
|
||||
The `HEADER` function creates a new message header containing the public key from the key pair output of the `DH`function.
|
||||
It outputs the previous chain length `pn`, and the message number `n`.
|
||||
The returned header object contains ratchet public key `dh` and integers `pn` and `n`.
|
||||
|
||||
#### Decryption
|
||||
The function `RatchetDecrypt()` decrypts incoming messages:
|
||||
```
|
||||
RatchetDecrypt(state, header, ciphertext, AD):
|
||||
plaintext = TrySkippedMessageKeys(state, header, ciphertext, AD)
|
||||
if plaintext != None:
|
||||
return plaintext
|
||||
if header.dh != state.DHr:
|
||||
SkipMessageKeys(state, header.pn)
|
||||
DHRatchet(state, header)
|
||||
SkipMessageKeys(state, header.n)
|
||||
state.CKr, mk = HMAC-SHA256(state.CKr)
|
||||
state.Nr = state.Nr + 1
|
||||
return AES256-GCM_Dec(mk, ciphertext, AD || header)
|
||||
```
|
||||
Auxiliary functions follow:
|
||||
|
||||
```
|
||||
DHRatchet(state, header):
|
||||
state.PN = state.Ns
|
||||
state.Ns = state.Nr = 0
|
||||
state.DHr = header.dh
|
||||
state.RK, state.CKr = HKDF(state.RK, DH(state.DHs, state.DHr))
|
||||
state.DHs = GENERATE_KEYPAIR(curve = curve448)
|
||||
state.RK, state.CKs = HKDF(state.RK, DH(state.DHs, state.DHr))
|
||||
```
|
||||
```
|
||||
SkipMessageKeys(state, until):
|
||||
if state.NR + MAX_SKIP < until:
|
||||
raise Error
|
||||
if state.CKr != none:
|
||||
while state.Nr < until:
|
||||
state.CKr, mk = HMAC-SHA256(state.CKr)
|
||||
state.MKSKIPPED[state.DHr, state.Nr] = mk
|
||||
state.Nr = state.Nr + 1
|
||||
```
|
||||
```
|
||||
TrySkippedMessageKey(state, header, ciphertext, AD):
|
||||
if (header.dh, header.n) in state.MKSKIPPED:
|
||||
mk = state.MKSKIPPED[header.dh, header.n]
|
||||
delete state.MKSKIPPED[header.dh, header.n]
|
||||
return AES256-GCM_Dec(mk, ciphertext, AD || header)
|
||||
else: return None
|
||||
```
|
||||
|
||||
## Information retrieval
|
||||
|
||||
### Static data
|
||||
Some data, such as the key pairs `(ik, IK)` for Alice and Bob, MAY NOT be regenerated after a period of time.
|
||||
Therefore the prekey bundle MAY be stored in long-term storage solutions, such as a dedicated smart contract which outputs such a key pair when receiving an Ethereum wallet address.
|
||||
|
||||
Storing static data is done using a dedicated smart contract `PublicKeyStorage` which associates the Ethereum wallet address of a user with his public key.
|
||||
This mapping is done by `PublicKeyStorage` using a `publicKeys` function, or a `setPublicKey` function.
|
||||
This mapping is done if the user passed an authorization process.
|
||||
A user who wants to retrieve a public key associated with a specific wallet address calls a function `getPublicKey`.
|
||||
The user provides the wallet address as the only input parameter for `getPublicKey`.
|
||||
The function outputs the associated public key from the smart contract.
|
||||
|
||||
### Ephemeral data
|
||||
Storing ephemeral data on Ethereum MAY be done using a combination of on-chain and off-chain solutions.
|
||||
This approach provides an efficient solution to the problem of storing updatable data in Ethereum.
|
||||
1. Ethereum stores a reference or a hash that points to the off-chain data.
|
||||
2. Off-chain solutions can include systems like IPFS, traditional cloud storage solutions, or decentralized storage networks such as a [Swarm](https://www.ethswarm.org).
|
||||
In any case, the user stores the associated IPFS hash, URL or reference in Ethereum.
|
||||
|
||||
The fact of a user not updating the ephemeral information can be understood as Bob not willing to participate in any communication.
|
||||
|
||||
This applies to `KeyPackage`, which in the MLS specification are meant to be stored in a directory provided by the delivery service.
|
||||
If such an element does not exist, `KeyPackage` MUST be stored according to one of the two options outlined above.
|
||||
|
||||
## Private group messaging protocol
|
||||
### Theory
|
||||
The [Messaging Layer Security](https://datatracker.ietf.org/doc/rfc9420/)(MLS) protocol aims at providing a group of users with end-to-end encryption in an authenticated and asynchronous way.
|
||||
The main security characteristics of the protocol are: Message confidentiality and authentication, sender authentication,
|
||||
membership agreement, post-remove and post-update security, and forward secrecy and post-compromise security.
|
||||
The MLS protocol achieves: low-complexity, group integrity, synchronization and extensibility.
|
||||
|
||||
The extension to group chat described in forthcoming sections is built upon the [MLS](https://datatracker.ietf.org/doc/rfc9420/) protocol.
|
||||
|
||||
### Syntax
|
||||
Each MLS session uses a single cipher suite that specifies the primitives to be used in group key computations. The cipher suite MUST use:
|
||||
- `X488` as Diffie-Hellman function.
|
||||
- `SHA256` as KDF.
|
||||
- `AES256-GCM` as AEAD algorithm.
|
||||
- `SHA512` as hash function.
|
||||
- `XEd448` for digital signatures.
|
||||
|
||||
Formats for public keys, signatures and public-key encryption MUST follow Section 5.1 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Hash-based identifiers
|
||||
Some MLS messages refer to other MLS objects by hash.
|
||||
These identifiers MUST be computed according to Section 5.2 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Credentials
|
||||
Each member of a group presents a credential that provides one or more identities for the member and associates them with the member's signing key.
|
||||
The identities and signing key are verified by the Authentication Service in use for a group.
|
||||
|
||||
Credentials MUST follow the specifications of section 5.3 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Message framing
|
||||
Handshake and application messages use a common framing structure providing encryption to ensure confidentiality within the group, and signing to authenticate the sender.
|
||||
|
||||
The structure is:
|
||||
- `PublicMessage`: represents a message that is only signed, and not encrypted.
|
||||
The definition and the encoding/decoding of a `PublicMessage` MUST follow the specification in section 6.2 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
- `PrivateMessage`: represents a signed and encrypted message, with protections for both the content of the message and related metadata.
|
||||
The definition, and the encoding/decoding of a `PrivateMessage` MUST follow the specification in section 6.3 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
Applications MUST use `PrivateMessage` to encrypt application messages.
|
||||
|
||||
Applications SHOULD use `PrivateMessage` to encode handshake messages.
|
||||
|
||||
Each encrypted MLS message carries a "generation" number which is a per-sender incrementing counter.
|
||||
If a group member observes a gap in the generation sequence for a sender,
|
||||
then they know that they have missed a message from that sender.
|
||||
|
||||
### Nodes contents
|
||||
The nodes of a ratchet tree contain several types of data:
|
||||
|
||||
- Leaf nodes describe individual members.
|
||||
- Parent nodes describe subgroups.
|
||||
|
||||
Contents of each kind of node, and its structure MUST follow the indications described in sections 7.1 and 7.2 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Leaf node validation
|
||||
`KeyPackage` objects describe the client's capabilities and provides keys that can be used to add the client to a group.
|
||||
|
||||
The validity of a leaf node needs to be verified at the following stages:
|
||||
- When a leaf node is downloaded in a `KeyPackage`, before it is used to add the client to the group.
|
||||
- When a leaf node is received by a group member in an Add, Update, or Commit message.
|
||||
- When a client validates a ratchet tree.
|
||||
|
||||
A client MUST verify the validity of a leaf node following the instructions of section 7.3 in [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Ratchet tree evolution
|
||||
Whenever a member initiates an epoch change, they MAY need to refresh the key pairs of their leaf and of the nodes on their direct path. This is done to keep forward secrecy and post-compromise security.
|
||||
The member initiating the epoch change MUST follow this procedure procedure.
|
||||
A member updates the nodes along its direct path as follows:
|
||||
- Blank all the nodes on the direct path from the leaf to the root.
|
||||
- Generate a fresh HPKE key pair for the leaf.
|
||||
- Generate a sequence of path secrets, one for each node on the leaf's filtered direct path.
|
||||
It MUST follow the procedure described in section 7.4 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
- Compute the sequence of HPKE key pairs `(node_priv,node_pub)`, one for each node on the leaf's direct path.
|
||||
It MUST follow the procedure described in section 7.4 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Views of the tree synchronization
|
||||
After generating fresh key material and applying it to update their local tree state, the generator broadcasts this update to other members of the group.
|
||||
This operation MUST be done according to section 7.5 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Leaf synchronization
|
||||
Changes to group memberships MUST be represented by adding and removing leaves of the tree.
|
||||
This corresponds to increasing or decreasing the depth of the tree, resulting in the number of leaves being doubled or halved.
|
||||
These operations MUST be done as described in section 7.7 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Tree and parent hashing
|
||||
Group members can agree on the cryptographic state of the group by generating a hash value that represents the contents of the group ratchet tree and the member’s credentials.
|
||||
The hash of the tree is the hash of its root node, defined recursively from the leaves.
|
||||
Tree hashes summarize the state of a tree at point in time.
|
||||
The hash of a leaf is the hash of the `LeafNodeHashInput` object.
|
||||
At the same time, the hash of a parent node including the root, is the hash of a `ParentNodeHashInput` object.
|
||||
Parent hashes capture information about how keys in the tree were populated.
|
||||
|
||||
Tree and parent hashing MUST follow the directions in Sections 7.8 and 7.9 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Key schedule
|
||||
Group keys are derived using the `Extract` and `Expand` functions from the KDF for the group's cipher suite, as well as the functions defined below:
|
||||
|
||||
```
|
||||
ExpandWithLabel(Secret, Label, Context, Length) = KDF.Expand(Secret, KDFLabel, Length)
|
||||
DeriveSecret(Secret, Label) = ExpandWithLabel(Secret, Label, "", KDF.Nh)
|
||||
```
|
||||
`KDFLabel` MUST be specified as:
|
||||
```
|
||||
struct {
|
||||
uint16 length;
|
||||
opaque label<V>;
|
||||
opaque context<V>;
|
||||
} KDFLabel;
|
||||
```
|
||||
The fields of `KDFLabel` MUST be:
|
||||
```
|
||||
length = Length;
|
||||
label = "MLS 1.0 " + Label;
|
||||
context = Context;
|
||||
```
|
||||
|
||||
Each member of the group MUST maintaint a `GroupContext` object summarizing the state of the group.
|
||||
The sturcture of such object MUST be:
|
||||
|
||||
```
|
||||
struct {
|
||||
ProtocolVersion version = mls10;
|
||||
CipherSuite cipher_suite;
|
||||
opaque group_id<V>;
|
||||
uint64 epoch;
|
||||
opaque tree_hash<V>;
|
||||
opaque confirmed_trasncript_hash<V>;
|
||||
Extension extension<V>;
|
||||
} GroupContext;
|
||||
```
|
||||
|
||||
The use of key scheduling MUST follow the indications in sections 8.1 - 8.7 in [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Secret trees
|
||||
For the generation of encryption keys and nonces, the key schedule begins with the `encryption_secret` at the root and derives a tree of secrets with the same structure as the group's ratchet tree.
|
||||
Each leaf in the secret tree is associated with the same group member as the corresponding leaf in the ratchet tree.
|
||||
|
||||
If `N` is a parent node in the secret tree, the secrets of the children of `N` MUST be defined following section 9 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
#### Encryption keys
|
||||
|
||||
MLS encrypts three different types of information:
|
||||
- Metadata (sender information).
|
||||
- Handshake messages (Proposal and Commit).
|
||||
- Application messages.
|
||||
|
||||
For handshake and application messages, a sequence of keys is derived via a sender ratchet.
|
||||
Each sender has their own sender ratchet, and each step along the ratchet is called a generation. These procedures MUST follow section 9.1 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
#### Deletion schedule
|
||||
|
||||
All security-sensitive values MUST be deleted as soon as they are consumed.
|
||||
A sensitive value S is consumed if:
|
||||
|
||||
- S was used to encrypt or (successfully) decrypt a message.
|
||||
- A key, nonce, or secret derived from S has been consumed.
|
||||
|
||||
The deletion procedure MUST follow the instruction described in section 9.2 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Key packages
|
||||
KeyPackage objects are used to ease the addition of clients to a group asynchronously.
|
||||
A KeyPackage object specifies:
|
||||
|
||||
- Protocol version and cipher suite supported by the client.
|
||||
- Public keys that can be used to encrypt Welcome messages. Welcome messages provide new members with the information to initialize their state for the epoch in which they were added or in which they want to add themselves to the group
|
||||
- The content of the leaf node that should be added to the tree to represent this client.
|
||||
|
||||
KeyPackages are intended to be used only once and SHOULD NOT be reused.
|
||||
|
||||
Clients MAY generate and publish multiple KeyPackages to support multiple cipher suites.
|
||||
|
||||
The structure of the object MUST be:
|
||||
```
|
||||
struct {
|
||||
ProtocolVersion version;
|
||||
CipherSuite cipher_suite;
|
||||
HPKEPublicKey init_key;
|
||||
LeafNode leaf_node;
|
||||
Extension extensions<V>;
|
||||
/* SignWithLabel(., "KeyPackageTBS", KeyPackageTBS) */
|
||||
opaque signature<V>;
|
||||
}
|
||||
```
|
||||
```
|
||||
struct {
|
||||
ProtocolVersion version;
|
||||
CipheSuite cipher_suite;
|
||||
HPKEPublicKey init_key;
|
||||
LeafNode leaf_node;
|
||||
Extension extensions<V>;
|
||||
}
|
||||
```
|
||||
`KeyPackage` object MUST be verified when:
|
||||
- A `KeyPackage` is downloaded by a group member, before it is used to add the client to the group.
|
||||
- When a `KeyPackage` is received by a group member in an `Add` message.
|
||||
|
||||
Verification MUST be done as follows:
|
||||
- Verify that the cipher suite and protocol version of the `KeyPackage` match those in the `GroupContext`.
|
||||
- Verify that the `leaf_node` of the `KeyPackage` is valid for a `KeyPackage`.
|
||||
- Verify that the signature on the `KeyPackage` is valid.
|
||||
- Verify that the value of `leaf_node.encryption_key` is different from the value of the `init_key field`.
|
||||
|
||||
HPKE public keys are opaque values in a format defined by Section 4 of [RFC9180](https://datatracker.ietf.org/doc/rfc9180/).
|
||||
|
||||
Signature public keys are represented as opaque values in a format defined by the cipher suite's signature scheme.
|
||||
|
||||
### Group creation
|
||||
A group is always created with a single member.
|
||||
Other members are then added to the group using the usual Add/Commit mechanism.
|
||||
The creator of a group MUST set:
|
||||
- the group ID.
|
||||
- cipher suite.
|
||||
- initial extensions for the group.
|
||||
|
||||
If the creator intends to add other members at the time of creation, then it SHOULD fetch `KeyPackages` for those members, and select a cipher suite and extensions according to their capabilities.
|
||||
|
||||
The creator MUST use the capabilities information in these `KeyPackages` to verify that the chosen version and cipher suite is the best option supported by all members.
|
||||
|
||||
Group IDs SHOULD be constructed so they are unique with high probability.
|
||||
|
||||
To initialize a group, the creator of the group MUST initialize a one-member group with the following initial values:
|
||||
- Ratchet tree: A tree with a single node, a leaf node containing an HPKE public key and credential for the creator.
|
||||
- Group ID: A value set by the creator.
|
||||
- Epoch: `0`.
|
||||
- Tree hash: The root hash of the above ratchet tree.
|
||||
- Confirmed transcript hash: The zero-length octet string.
|
||||
- Epoch secret: A fresh random value of size `KDF.Nh`.
|
||||
- Extensions: Any values of the creator's choosing.
|
||||
|
||||
The creator MUST also calculate the interim transcript hash:
|
||||
- Derive the `confirmation_key` for the epoch according to Section 8 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
- Compute a `confirmation_tag` over the empty `confirmed_transcript_hash` using the `confirmation_key` as described in Section 8.1 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
- Compute the updated `interim_transcript_hash` from the `confirmed_transcript_hash` and the `confirmation_tag` as described in Section 8.2 [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
All members of a group MUST support the cipher suite and protocol version in use. Additional requirements MAY be imposed by including a `required_capabilities` extension in the `GroupContext`.
|
||||
|
||||
```
|
||||
struct {
|
||||
ExtensionType extension_types<V>;
|
||||
ProposalType proposal_types<V>;
|
||||
CredentialType credential_types<V>;
|
||||
}
|
||||
```
|
||||
|
||||
### Group evolution
|
||||
Group membership can change, and existing members can change their keys in order to achieve post-compromise security.
|
||||
In MLS, each such change is accomplished by a two-step process:
|
||||
- A proposal to make the change is broadcast to the group in a Proposal message.
|
||||
- A member of the group or a new member broadcasts a Commit message that causes one or more proposed changes to enter into effect.
|
||||
|
||||
The group evolves from one cryptographic state to another each time a Commit message is sent and processed.
|
||||
These states are called epochs and are uniquely identified among states of the group by eight-octet epoch values.
|
||||
|
||||
Proposals are included in a `FramedContent` by way of a `Proposal` structure that indicates their type:
|
||||
|
||||
```
|
||||
struct {
|
||||
ProposalType proposal_type;
|
||||
select (Proposal.proposal_type) {
|
||||
case add: Add:
|
||||
case update: Update;
|
||||
case remove: Remove;
|
||||
case psk: PreSharedKey;
|
||||
case reinit: ReInit;
|
||||
case external_init: ExternalInit;
|
||||
case group_context_extensions: GroupContextExtensions;
|
||||
}
|
||||
```
|
||||
On receiving a `FramedContent` containing a `Proposal`, a client MUST verify the signature inside `FramedContentAuthData` and that the epoch field of the enclosing FramedContent is equal to the epoch field of the current GroupContext object.
|
||||
If the verification is successful, then the Proposal SHOULD be cached in such a way that it can be retrieved by hash in a later Commit message.
|
||||
|
||||
Proposals are organized as follows:
|
||||
- `Add`: requests that a client with a specified KeyPackage be added to the group.
|
||||
- `Update`: similar to Add, it replaces the sender's LeafNode in the tree instead of adding a new leaf to the tree.
|
||||
- `Remove`: requests that the member with the leaf index removed be removed from the group.
|
||||
- `ReInit`: requests to reinitialize the group with different parameters.
|
||||
- `ExternalInit`: used by new members that want to join a group by using an external commit.
|
||||
- `GroupContentExtensions`: it is used to update the list of extensions in the GroupContext for the group.
|
||||
|
||||
Proposals structure and semantics MUST follow sections 12.1.1 - 12.1.7 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
Any list of commited proposals MUST be validated either by a the group member who created the commit, or any group member processing such commit.
|
||||
The validation MUST be done according to one of the procedures described in Section 12.2 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
When creating or processing a Commit, a client applies a list of proposals to the ratchet tree and `GroupContext`.
|
||||
The client MUST apply the proposals in the list in the order described in Section 12.3 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Commit messages
|
||||
Commit messages initiate new group epochs.
|
||||
It informs group members to update their representation of the state of the group by applying the proposals and advancing the key schedule.
|
||||
|
||||
Each proposal covered by the Commit is included by a `ProposalOrRef` value.
|
||||
`ProposalOrRef` identify the proposal to be applied by value or by reference.
|
||||
Commits that refer to new Proposals from the committer can be included by value.
|
||||
Commits for previously sent proposals from anyone can be sent by reference.
|
||||
Proposals sent by reference are specified by including the hash of the `AuthenticatedContent`.
|
||||
|
||||
Group members that have observed one or more valid proposals within an epoch MUST send a Commit message before sending application data.
|
||||
A sender and a receiver of a Commit MUST verify that the committed list of proposals is valid.
|
||||
The sender of a Commit SHOULD include all valid proposals received during the current epoch.
|
||||
|
||||
Functioning of commits MUST follow the instructions of Section 12.4 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Application messages
|
||||
Handshake messages provide an authenticated group key exchange to clients.
|
||||
To protect application messages sent among the members of a group, the `encryption_secret` provided by the key schedule is used to derive a sequence of nonces and keys for message encryption.
|
||||
|
||||
Each client MUST maintain their local copy of the key schedule for each epoch during which they are a group member.
|
||||
They derive new keys, nonces, and secrets as needed. This data MUST be deleted as soon as they have been used.
|
||||
|
||||
Group members MUST use the AEAD algorithm associated with the negotiated MLS ciphersuite to encrypt and decrypt Application messages according to the Message Framing section.
|
||||
The group identifier and epoch allow a device to know which group secrets should be used and from which Epoch secret to start computing other secrets and keys.
|
||||
Application messages SHOULD be padded to provide resistance against traffic analysis techniques.
|
||||
This avoids additional information to be provided to an attacker in order to guess the length of the encrypted message.
|
||||
Padding SHOULD be used on messages with zero-valued bytes before AEAD encryption.
|
||||
|
||||
Functioning of application messages MUST follow the instructions of Section 15 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/).
|
||||
|
||||
### Considerations with respect to decentralization
|
||||
The MLS protocol assumes the existence on a (central, untrusted) *delivery service*, whose responsabilites include:
|
||||
|
||||
- Acting as a directory service providing the initial keying material for clients to use.
|
||||
- Routing MLS messages among clients.
|
||||
|
||||
The central delivery service can be avoided in protocols using the publish/gossip approach, such as [gossipsub](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub).
|
||||
|
||||
Concerning keys, each node can generate and disseminate their encryption key among the other nodes, so they can create a local version of the tree that allows for the generation of the group key.
|
||||
|
||||
Another important component is the *authentication service*, which is replaced with SIWE in this specification.
|
||||
|
||||
## Ethereum-based authentication protocol
|
||||
### Theory
|
||||
Sign-in with Ethereum describes how Ethereum accounts authenticate with off-chain services by signing a standard message format
|
||||
parameterized by scope, session details, and security mechanisms.
|
||||
Sign-in with Ethereum (SIWE), which is described in the [EIP 4361](https://eips.ethereum.org/EIPS/eip-4361), MUST be the authentication method required.
|
||||
|
||||
### Syntax
|
||||
#### Message format (ABNF)
|
||||
A SIWE Message MUST conform with the following Augmented Backus–Naur Form ([RFC 5234](https://datatracker.ietf.org/doc/html/rfc5234)) expression.
|
||||
|
||||
```
|
||||
sign-in-with-ethereum =
|
||||
[ scheme "://" ] domain %s" wants you to sign in with your Ethereum account:" LF
|
||||
address LF
|
||||
LF
|
||||
[ statement LF ]
|
||||
LF
|
||||
%s"URI: " uri LF
|
||||
%s"Version: " version LF
|
||||
%s"Chain ID: " chain-id LF
|
||||
%s"Nonce: " nonce LF
|
||||
%s"Issued At: " issued-at
|
||||
[ LF %s"Expiration Time: " expiration-time ]
|
||||
[ LF %s"Not Before: " not-before ]
|
||||
[ LF %s"Request ID: " request-id ]
|
||||
[ LF %s"Resources:"
|
||||
resources ]
|
||||
|
||||
scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
; See RFC 3986 for the fully contextualized
|
||||
; definition of "scheme".
|
||||
|
||||
domain = authority
|
||||
; From RFC 3986:
|
||||
; authority = [ userinfo "@" ] host [ ":" port ]
|
||||
; See RFC 3986 for the fully contextualized
|
||||
; definition of "authority".
|
||||
|
||||
address = "0x" 40*40HEXDIG
|
||||
; Must also conform to captilization
|
||||
; checksum encoding specified in EIP-55
|
||||
; where applicable (EOAs).
|
||||
|
||||
statement = *( reserved / unreserved / " " )
|
||||
; See RFC 3986 for the definition
|
||||
; of "reserved" and "unreserved".
|
||||
; The purpose is to exclude LF (line break).
|
||||
|
||||
uri = URI
|
||||
; See RFC 3986 for the definition of "URI".
|
||||
|
||||
version = "1"
|
||||
|
||||
chain-id = 1*DIGIT
|
||||
; See EIP-155 for valid CHAIN_IDs.
|
||||
|
||||
nonce = 8*( ALPHA / DIGIT )
|
||||
; See RFC 5234 for the definition
|
||||
; of "ALPHA" and "DIGIT".
|
||||
|
||||
issued-at = date-time
|
||||
expiration-time = date-time
|
||||
not-before = date-time
|
||||
; See RFC 3339 (ISO 8601) for the
|
||||
; definition of "date-time".
|
||||
|
||||
request-id = *pchar
|
||||
; See RFC 3986 for the definition of "pchar".
|
||||
|
||||
resources = *( LF resource )
|
||||
|
||||
resource = "- " URI
|
||||
```
|
||||
|
||||
This specification defines the following SIWE Message fields that can be parsed from a SIWE Message by following the rules in ABNF Message Format:
|
||||
|
||||
- `scheme` OPTIONAL. The URI scheme of the origin of the request.
|
||||
Its value MUST be a [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) URI scheme.
|
||||
|
||||
- `domain` REQUIRED. The domain that is requesting the signing.
|
||||
Its value MUST be a [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) authority. The authority includes an OPTIONAL port.
|
||||
If the port is not specified, the default port for the provided scheme is assumed.
|
||||
|
||||
If scheme is not specified, HTTPS is assumed by default.
|
||||
- `address` REQUIRED. The Ethereum address performing the signing.
|
||||
Its value SHOULD be conformant to mixed-case checksum address encoding specified in ERC-55 where applicable.
|
||||
|
||||
- `statement` OPTIONAL. A human-readable ASCII assertion that the user will sign which MUST NOT include '\n' (the byte 0x0a).
|
||||
- `uri` REQUIRED. An [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) URI referring to the resource that is the subject of the signing.
|
||||
|
||||
- `version` REQUIRED. The current version of the SIWE Message, which MUST be 1 for this specification.
|
||||
|
||||
- `chain-id` REQUIRED. The EIP-155 Chain ID to which the session is bound, and the network where Contract Accounts MUST be resolved.
|
||||
|
||||
- `nonce` REQUIRED. A random string (minimum 8 alphanumeric characters) chosen by the relying party and used to prevent replay attacks.
|
||||
|
||||
- `issued-at` REQUIRED. The time when the message was generated, typically the current time.
|
||||
Its value MUST be an ISO 8601 datetime string.
|
||||
|
||||
- `expiration-time` OPTIONAL. The time when the signed authentication message is no longer valid.
|
||||
Its value MUST be an ISO 8601 datetime string.
|
||||
|
||||
- `not-before` OPTIONAL. The time when the signed authentication message will become valid.
|
||||
Its value MUST be an ISO 8601 datetime string.
|
||||
|
||||
- `request-id` OPTIONAL. An system-specific identifier that MAY be used to uniquely refer to the sign-in request.
|
||||
|
||||
- `resources` OPTIONAL. A list of information or references to information the user wishes to have resolved as part of authentication by the relying party.
|
||||
Every resource MUST be a RFC 3986 URI separated by "\n- " where \n is the byte 0x0a.
|
||||
|
||||
#### Signing and Verifying Messages with Ethereum Accounts
|
||||
- For Externally Owned Accounts, the verification method specified in [ERC-191](https://eips.ethereum.org/EIPS/eip-191) MUST be used.
|
||||
|
||||
- For Contract Accounts,
|
||||
|
||||
- The verification method specified in [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) SHOULD be used.
|
||||
Otherwise, the implementer MUST clearly define the verification method to attain security and interoperability for both wallets and relying parties.
|
||||
|
||||
- When performing [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) signature verification, the contract performing the verification MUST be resolved from the specified `chain-id`.
|
||||
|
||||
- Implementers SHOULD take into consideration that [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) implementations are not required to be pure functions.
|
||||
They can return different results for the same inputs depending on blockchain state.
|
||||
This can affect the security model and session validation rules.
|
||||
|
||||
#### Resolving Ethereum Name Service (ENS) Data
|
||||
- The relying party or wallet MAY additionally perform resolution of ENS data, as this can improve the user experience by displaying human-friendly information that is related to the `address`.
|
||||
Resolvable ENS data include:
|
||||
- The primary ENS name.
|
||||
- The ENS avatar.
|
||||
- Any other resolvable resources specified in the ENS documentation.
|
||||
|
||||
- If resolution of ENS data is performed, implementers SHOULD take precautions to preserve user privacy and consent.
|
||||
Their `address` could be forwarded to third party services as part of the resolution process.
|
||||
|
||||
#### Implementer steps: specifying the request origin
|
||||
The `domain` and, if present, the `scheme`, in the SIWE Message MUST correspond to the origin from where the signing request was made.
|
||||
|
||||
#### Implementer steps: verifying a signed message
|
||||
The SIWE Message MUST be checked for conformance to the ABNF Message Format and its signature MUST be checked as defined in Signing and Verifying Messages with Ethereum Accounts.
|
||||
|
||||
#### Implementer steps: creating sessions
|
||||
Sessions MUST be bound to the address and not to further resolved resources that can change.
|
||||
|
||||
#### Implementer steps: interpreting and resolving resources
|
||||
Implementers SHOULD ensure that that URIs in the listed resources are human-friendly when expressed in plaintext form.
|
||||
|
||||
#### Wallet implementer steps: verifying the message format
|
||||
The full SIWE message MUST be checked for conformance to the ABNF defined in ABNF Message Format.
|
||||
|
||||
Wallet implementers SHOULD warn users if the substring `"wants you to sign in with your Ethereum account"` appears anywhere in an [ERC-191](https://eips.ethereum.org/EIPS/eip-191) message signing request unless the message fully conforms to the format defined ABNF Message Format.
|
||||
|
||||
#### Wallet implementer steps: verifying the request origin
|
||||
Wallet implementers MUST prevent phishing attacks by verifying the origin of the request against the `scheme` and `domain` fields in the SIWE Message.
|
||||
|
||||
The origin SHOULD be read from a trusted data source such as the browser window or over WalletConnect [ERC-1328](https://eips.ethereum.org/EIPS/eip-1328) sessions for comparison against the signing message contents.
|
||||
|
||||
Wallet implementers MAY warn instead of rejecting the verification if the origin is pointing to localhost.
|
||||
|
||||
The following is a RECOMMENDED algorithm for Wallets to conform with the requirements on request origin verification defined by this specification.
|
||||
|
||||
The algorithm takes the following input variables:
|
||||
|
||||
- fields from the SIWE message.
|
||||
- `origin` of the signing request: the origin of the page which requested the signin via the provider.
|
||||
- `allowedSchemes`: a list of schemes allowed by the Wallet.
|
||||
- `defaultScheme`: a scheme to assume when none was provided. Wallet implementers in the browser SHOULD use https.
|
||||
- developer mode indication: a setting deciding if certain risks should be a warning instead of rejection. Can be manually configured or derived from `origin` being localhost.
|
||||
|
||||
The algorithm is described as follows:
|
||||
|
||||
- If `scheme` was not provided, then assign `defaultScheme` as scheme.
|
||||
- If `scheme` is not contained in `allowedSchemes`, then the `scheme` is not expected and the Wallet MUST reject the request.
|
||||
Wallet implementers in the browser SHOULD limit the list of allowedSchemes to just 'https' unless a developer mode is activated.
|
||||
- If `scheme` does not match the scheme of origin, the Wallet SHOULD reject the request.
|
||||
Wallet implementers MAY show a warning instead of rejecting the request if a developer mode is activated.
|
||||
In that case the Wallet continues processing the request.
|
||||
- If the `host` part of the `domain` and `origin` do not match, the Wallet MUST reject the request unless the Wallet is in developer mode.
|
||||
In developer mode the Wallet MAY show a warning instead and continues procesing the request.
|
||||
- If `domain` and `origin` have mismatching subdomains, the Wallet SHOULD reject the request unless the Wallet is in developer mode.
|
||||
In developer mode the Wallet MAY show a warning instead and continues procesing the request.
|
||||
- Let `port` be the port component of `domain`, and if no port is contained in domain, assign port the default port specified for the scheme.
|
||||
- If `port` is not empty, then the Wallet SHOULD show a warning if the `port` does not match the port of `origin`.
|
||||
- If `port` is empty, then the Wallet MAY show a warning if `origin` contains a specific port.
|
||||
- Return request origin verification completed.
|
||||
|
||||
#### Wallet implementer steps: creating SIWE interfaces
|
||||
Wallet implementers MUST display to the user the following fields from the SIWE Message request by default and prior to signing, if they are present: `scheme`, `domain`, `address`, `statement`, and `resources`.
|
||||
Other present fields MUST also be made available to the user prior to signing either by default or through an extended interface.
|
||||
|
||||
Wallet implementers displaying a plaintext SIWE Message to the user SHOULD require the user to scroll to the bottom of the text area prior to signing.
|
||||
|
||||
Wallet implementers MAY construct a custom SIWE user interface by parsing the ABNF terms into data elements for use in the interface.
|
||||
The display rules above still apply to custom interfaces.
|
||||
|
||||
#### Wallet implementer steps: supporting internationalization (i18n)
|
||||
After successfully parsing the message into ABNF terms, translation MAY happen at the UX level per human language.
|
||||
|
||||
## Privacy and Security Considerations
|
||||
- The double ratchet "recommends" using AES in CBC mode. Since encryption must be with an AEAD encryption scheme, we will use AES in GCM mode instead (supported by Noise).
|
||||
- For the information retrieval, the algorithm MUST include a access control mechanisms to restrict who can call the set and get functions.
|
||||
- One SHOULD include event logs to track changes in public keys.
|
||||
- The curve vurve448 MUST be chosen due to its higher security level: 224-bit security instead of the 128-bit security provided by X25519.
|
||||
- It is important that Bob MUST NOT reuse `SPK`.
|
||||
|
||||
## Copyright
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
# References
|
||||
- [Augmented BNF for Syntax Specifications](https://datatracker.ietf.org/doc/html/rfc5234)
|
||||
- [Gossipsub](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub)
|
||||
- [HMAC-based Extract-and-Expand Key Derivation Function](https://www.ietf.org/rfc/rfc5869.txt)
|
||||
- [Hybrid Public Key Encryption](https://datatracker.ietf.org/doc/rfc9180/)
|
||||
- [Security Analysis and Improvements for the IETF MLS Standard for Group Messaging](https://eprint.iacr.org/2019/1189.pdf)
|
||||
- [Signed Data Standard](https://eips.ethereum.org/EIPS/eip-191)
|
||||
- [Sign-In with Ethereum](https://eips.ethereum.org/EIPS/eip-4361)
|
||||
- [Standard Signature Validation Method for Contracts](https://eips.ethereum.org/EIPS/eip-1271)
|
||||
- [The Double Ratchet Algorithm](https://signal.org/docs/specifications/doubleratchet/)
|
||||
- [The Messaging Layer Security Protocol](https://datatracker.ietf.org/doc/rfc9420/)
|
||||
- [The X3DH Key Agreement Protocol](https://signal.org/docs/specifications/x3dh/)
|
||||
- [Toy Ethereum Private Messaging Protocol](https://rfc.vac.dev/spec/20/)
|
||||
- [Uniform Resource Identifier](https://datatracker.ietf.org/doc/html/rfc3986)
|
||||
- [WalletConnect URI Format](https://eips.ethereum.org/EIPS/eip-1328)
|
||||
732
vac/raw/decentralized-messaging-ethereum.md
Normal file
@@ -0,0 +1,732 @@
|
||||
---
|
||||
title: VAC-DECENTRALIZED-MESSAGING-ETHEREUM
|
||||
name: Decentralized Key and Session Setup for Secure Messaging over Ethereum
|
||||
status: raw
|
||||
category: informational
|
||||
editor: Ramses Fernandez-Valencia <ramses@status.im>
|
||||
contributors:
|
||||
---
|
||||
|
||||
## Abstract
|
||||
This document introduces a decentralized group messaging protocol using Ethereum adresses as identifiers.
|
||||
It is based in the proposal [DCGKA](https://eprint.iacr.org/2020/1281) by Weidner et al.
|
||||
It includes also approximations to overcome limitations related to using PKI and the multi-device setting.
|
||||
|
||||
## Motivation
|
||||
|
||||
The need for secure communications has become paramount.
|
||||
Traditional centralized messaging protocols are susceptible to various security threats,
|
||||
including unauthorized access, data breaches, and single points of failure.
|
||||
Therefore a decentralized approach to secure communication becomes increasingly relevant,
|
||||
offering a robust solution to address these challenges.
|
||||
|
||||
Secure messaging protocols used should have the following key features:
|
||||
1. **Asynchronous Messaging:** Users can send messages even if the recipients are not online at the moment.
|
||||
2. **Resilience to Compromise:** If a user's security is compromised,
|
||||
the protocol ensures that previous messages remain secure through forward secrecy (FS).
|
||||
This means that messages sent before the compromise cannot be decrypted by adversaries.
|
||||
Additionally, the protocol maintains post-compromise security (PCS) by regularly updating keys,
|
||||
making it difficult for adversaries to decrypt future communication.
|
||||
3. **Dynamic Group Management:** Users can easily add or remove group members at any time,
|
||||
reflecting the flexible nature of communication within the app.
|
||||
|
||||
In this field, there exists a *trilemma*, similar to what one observes in blockchain,
|
||||
involving three key aspects:
|
||||
1. security,
|
||||
2. scalability, and
|
||||
3. decentralization.
|
||||
|
||||
For instance, protocols like the [MLS](https://messaginglayersecurity.rocks) perform well in terms of scalability and security.
|
||||
However, they falls short in decentralization.
|
||||
|
||||
Newer studies such as [CoCoa](https://eprint.iacr.org/2022/251) improve features related to security and scalability,
|
||||
but they still rely on servers, which may not be fully trusted though they are necessary.
|
||||
|
||||
On the other hand,
|
||||
older studies like [Causal TreeKEM](https://mattweidner.com/assets/pdf/acs-dissertation.pdf) exhibit decent scalability (logarithmic)
|
||||
but lack forward secrecy and have weak post-compromise security (PCS).
|
||||
|
||||
The creators of [DCGKA](https://eprint.iacr.org/2020/1281) introduce a decentralized,
|
||||
asynchronous secure group messaging protocol that supports dynamic groups.
|
||||
This protocol operates effectively on various underlying networks without strict requirements on message ordering or latency.
|
||||
It can be implemented in peer-to-peer or anonymity networks,
|
||||
accommodating network partitions, high latency links, and disconnected operation seamlessly.
|
||||
Notably, the protocol doesn't rely on servers or
|
||||
a consensus protocol for its functionality.
|
||||
|
||||
This proposal provides end-to-end encryption with forward secrecy and post-compromise security,
|
||||
even when multiple users concurrently modify the group state.
|
||||
|
||||
## Theory
|
||||
### Protocol overview
|
||||
|
||||
This protocol makes use of ratchets to provide FS by encrypting each message with a different key.
|
||||
|
||||
In the figure one can see the ratchet for encrypting a sequence of messages.
|
||||
The sender requires an initial update secret `I_1`, which is introduced in a PRG.
|
||||
The PRG will produce two outputs, namely a symmetric key for AEAD encryption, and
|
||||
a seed for the next ratchet state.
|
||||
The associated data needed in the AEAD encryption includes the message index `i`.
|
||||
The ciphertext `c_i` associated to message `m_i` is then broadcasted to all group members.
|
||||
The next step requires deleting `I_1`, `k_i` and any old ratchet state.
|
||||
|
||||
After a period of time the sender may replace the ratchet state with new update secrets `I_2`, `I_3`, and so on.
|
||||
|
||||
To start a post-compromise security update,
|
||||
a user creates a new random value known as a seed secret and
|
||||
shares it with every other group member through a secure two-party channel.
|
||||
Upon receiving the seed secret,
|
||||
each group member uses it to calculate an update secret for both the sender's ratchet and their own.
|
||||
Additionally, the recipient sends an unencrypted acknowledgment to the group confirming the update.
|
||||
Every member who receives the acknowledgment updates not only the ratchet for the original sender but
|
||||
also the ratchet for the sender of the acknowledgment.
|
||||
Consequently, after sharing the seed secret through `n - 1` two-party messages and
|
||||
confirming it with `n - 1` broadcast acknowledgments,
|
||||
every group member has derived an update secret and updated their ratchet accordingly.
|
||||
|
||||
When removing a group member,
|
||||
the user who initiates the removal conducts a post-compromise security update
|
||||
by sending the update secret to all group members except the one being removed.
|
||||
To add a new group member,
|
||||
each existing group member shares the necessary state with the new user,
|
||||
enabling them to derive their future update secrets.
|
||||
|
||||
Since group members may receive messages in various orders,
|
||||
it's important to ensure that each sender's ratchet is updated consistently
|
||||
with the same sequence of update secrets at each group member.
|
||||
|
||||
The network protocol used in this scheme ensures that messages from the same sender are processed in the order they were sent.
|
||||
|
||||
### Components of the protocol
|
||||
|
||||
This protocol relies in 3 components:
|
||||
authenticated causal broadcast (ACB),
|
||||
decentralized group membership (DGM) and
|
||||
2-party secure messaging (2SM).
|
||||
|
||||
#### Authenticated causal broadcast
|
||||
A causal order is a partial order relation `<` on messages.
|
||||
Two messages `m_1` and `m_2` are causally ordered, or
|
||||
`m_1` causally precedes `m_2`
|
||||
(denoted by `m_1 < m_2`), if one of the following contiditions hold:
|
||||
|
||||
1. `m_1` and `m_2` were sent by the same group member, and `m_1` was sent before `m_2`.
|
||||
2. `m_2` was sent by a group member U, and `m_1` was received and
|
||||
processed by `U` before sending `m_2`.
|
||||
3. There exists `m_3` such that `m_1 < m_3` and `m_3 < m_2`.
|
||||
|
||||
Causal broadcast requires that before processing `m`,
|
||||
a group member must process all preceding messages `{m' | m' < m}`.
|
||||
|
||||
The causal broadcast module used in this protocol authenticates the sender of each message,
|
||||
as well as its causal ordering metadata, using a digital signature under the sender’s identity key.
|
||||
This prevents a passive adversary from impersonating users or affecting causally ordered delivery.
|
||||
|
||||
#### Decentralized group membership
|
||||
This protocol assumes the existence of a decentralized group membership function (denoted as DGM)
|
||||
that takes a set of membership change messages and their causal order relantionships,
|
||||
and returns the current set of group members’ IDs.
|
||||
It needs to be deterministic and depend only on causal order, and not exact order.
|
||||
|
||||
#### 2-party secure messaging (2SM)
|
||||
This protocol makes use of bidirectional 2-party secure messaging schemes,
|
||||
which consist of 3 algorithms: `2SM-Init`, `2SM-Send` and `2SM-Receive`.
|
||||
|
||||
##### 2SM-Init
|
||||
This function takes two IDs as inputs:
|
||||
`ID1` representing the local user and `ID2` representing the other party.
|
||||
It returns an initial protocol state `sigma`.
|
||||
The 2SM protocol relies on a Public Key Infrastructure (PKI) or
|
||||
a key server to map these IDs to their corresponding public keys.
|
||||
In practice, the PKI should incorporate ephemeral prekeys.
|
||||
This allows users to send messages to a new group member,
|
||||
even if that member is currently offline.
|
||||
|
||||
##### 2SM-Send
|
||||
This function takes a state `sigma` and a plaintext `m` as inputs, and
|
||||
returns a new state `sigma’` and a ciphertext `c`.
|
||||
|
||||
##### 2SM-Receive
|
||||
This function takes a state `sigma` and a ciphertext `c`, and
|
||||
returns a new state `sigma’` and a plaintext `m`.
|
||||
|
||||
#### 2SM Syntax
|
||||
|
||||
The variable `sigma` denotes the state consisting in the variables below:
|
||||
```
|
||||
sigma.mySks[0] = sk
|
||||
sigma.nextIndex = 1
|
||||
sigma.receivedSk = empty_string
|
||||
sigma.otherPk = pk`<br>
|
||||
sigma.otherPksender = “other”
|
||||
sigma.otherPkIndex = 0
|
||||
```
|
||||
|
||||
#### 2SM-Init
|
||||
On input a key pair `(sk, pk)`, this functions otuputs a state `sigma`.
|
||||
|
||||
#### 2SM-Send
|
||||
This function encrypts the message `m` using `sigma.otherPk`,
|
||||
which represents the other party’s current public key.
|
||||
This key is determined based on the last public key generated for the other party or
|
||||
the last public key received from the other party,
|
||||
whichever is more recent.
|
||||
`sigma.otherPkSender` is set to `me` in the former case and `other` in the latter case.
|
||||
|
||||
Metadata including `otherPkSender` and
|
||||
`otherPkIndex` are included in the message to indicate which of the recipient’s public keys is being utilized.
|
||||
|
||||
Additionally, this function generates a new key pair for the local user,
|
||||
storing the secret key in `sigma.mySks` and sending the public key.
|
||||
Similarly, it generates a new key pair for the other party,
|
||||
sending the secret key (encrypted) and storing the public key in `sigma.otherPk`.
|
||||
|
||||
```
|
||||
sigma.mySks[sigma.nextIndex], myNewPk) = PKE-Gen()
|
||||
(otherNewSk, otherNewPk) = PKE-Gen()
|
||||
plaintext = (m, otherNewSk, sigma`.nextIndex, myNewPk)
|
||||
msg = (PKE-Enc(sigma.otherPk, plaintext), sigma.otherPkSender, sigma.otherPkIndex)
|
||||
sigma.nextIndex++
|
||||
(sigma.otherPk, sigma.otherPkSender, sigma.otherPkIndex) = (otherNewPk, "me", empty_string)
|
||||
return (sigma`, msg)
|
||||
```
|
||||
|
||||
#### 2SM-Receive
|
||||
|
||||
This function utilizes the metadata of the message `c` to determine which secret key to utilize for decryption,
|
||||
assigning it to `sk`.
|
||||
If the secret key corresponds to one generated by ourselves,
|
||||
that secret key along with all keys with lower index are deleted.
|
||||
This deletion is indicated by `sigma.mySks[≤ keyIndex] = empty_string`.
|
||||
Subsequently, the new public and secret keys contained in the message are stored.
|
||||
|
||||
```
|
||||
(ciphertext, keySender, keyIndex) = c
|
||||
if keySender = "other" then
|
||||
sk = sigma.mySks[keyIndex]
|
||||
sigma.mySks[≤ keyIndex] = empty_string
|
||||
else sk = sigma.receivedSk
|
||||
(m, sigma.receivedSk, sigma.otherPkIndex, sigma.otherPk) = PKE-Dec(sk, ciphertext)
|
||||
sigma.otherPkSender = "other"
|
||||
return (sigma, m)
|
||||
```
|
||||
### PKE Syntax
|
||||
|
||||
The required PKE that MUST be used is ElGamal with a 2048-bit modulus `p`.
|
||||
|
||||
#### Parameters
|
||||
|
||||
The following parameters must be used:
|
||||
|
||||
```
|
||||
p = 308920927247127345254346920820166145569
|
||||
g = 2
|
||||
```
|
||||
|
||||
#### PKE-KGen
|
||||
|
||||
Each user `u` MUST do the following:
|
||||
|
||||
```
|
||||
PKE-KGen():
|
||||
a = randint(2, p-2)
|
||||
pk = (p, g, g^a)
|
||||
sk = a
|
||||
return (pk, sk)
|
||||
```
|
||||
|
||||
#### PKE-Enc
|
||||
|
||||
A user `v` encrypting a message `m` for `u` MUST follow these steps:
|
||||
|
||||
```
|
||||
PKE-Enc(pk):
|
||||
k = randint(2, p-2)
|
||||
eta = g^k % p
|
||||
delta = m * (g^a)^k % p
|
||||
return ((eta, delta))
|
||||
```
|
||||
|
||||
#### PKE-Dec
|
||||
|
||||
The user `u` recovers a message `m` from a ciphertext `c` by performing the following operations:
|
||||
|
||||
```
|
||||
PKE-Dec(sk):
|
||||
mu = eta^(p-1-sk) % p
|
||||
return ((mu * delta) % p)
|
||||
```
|
||||
|
||||
### DCGKA Syntax
|
||||
#### Auxiliary functions
|
||||
|
||||
There exist 6 functions that are auxiliary for the rest of components of the protocol, namely:
|
||||
|
||||
#### init
|
||||
|
||||
This function takes an `ID` as input and returns its associated initial state, denoted by `gamma`:
|
||||
|
||||
```
|
||||
gamma.myId = ID
|
||||
gamma.mySeq = 0
|
||||
gamma.history = empty
|
||||
gamma.nextSeed = empty_string
|
||||
gamma.2sm[·] = empty_string
|
||||
gamma.memberSecret[·, ·, ·] = empty_string
|
||||
gamma.ratchet[·] = empty_string
|
||||
return (gamma)
|
||||
```
|
||||
|
||||
#### encrypt-to
|
||||
|
||||
Upon reception of the recipient’s `ID` and a plaintext,
|
||||
it encrypts a direct message for another group member.
|
||||
Should it be the first message for a particular `ID`,
|
||||
then the `2SM` protocol state is initialized and stored in `gamma.2sm[recipient.ID]`.
|
||||
One then uses `2SM_Send` to encrypt the message and store the updated protocol in `gamma`.
|
||||
|
||||
```
|
||||
if gamma.2sm[recipient_ID] = empty_string then
|
||||
gamma.2sm[recipient_ID] = 2SM_Init(gamma.myID, recipient_ID)
|
||||
(gamma.2sm[recipient_ID], ciphertext) = 2SM_Send(gamma.2sm[recipient_ID], plaintext)
|
||||
return (gamma, ciphertext)
|
||||
```
|
||||
|
||||
#### decrypt-from
|
||||
|
||||
After receiving the sender’s `ID` and a ciphertext,
|
||||
it behaves as the reverse function of `encrypt-to` and has a similar initialization:
|
||||
|
||||
```
|
||||
if gamma.2sm[sender_ID] = empty_string then
|
||||
gamma.2sm[sender_ID] = 2SM_Init(gamma.myID, sender_ID)
|
||||
(gamma.2sm[sender_ID], plaintext) = 2SM_Receive(gamma.2sm[sender_ID], ciphertext)
|
||||
return (gamma, plaintext)
|
||||
```
|
||||
|
||||
#### update-ratchet
|
||||
|
||||
This function generates the next update secret `I_update` for the group member `ID`.
|
||||
The ratchet state is stored in `gamma.ratchet[ID]`.
|
||||
It is required to use a HMAC-based key derivation function HKDF to combine the ratchet state with an input,
|
||||
returning an update secret and a new ratchet state.
|
||||
|
||||
```
|
||||
(updateSecret, gamma.ratchet[ID]) = HKDF(gamma.ratchet[ID], input)
|
||||
return (gamma, updateSecret)
|
||||
```
|
||||
|
||||
#### member-view
|
||||
|
||||
This function calculates the set of group members based on the most recent control message sent by the specified user `ID`.
|
||||
It filters the group membership operations to include only those observed by the specified `ID`, and
|
||||
then invokes the DGM function to generate the group membership.
|
||||
|
||||
```
|
||||
ops = {m in gamma.history st. m was sent or acknowledged by ID}
|
||||
return DGM(ops)
|
||||
```
|
||||
|
||||
#### generate-seed
|
||||
|
||||
This functions generates a random bit string and
|
||||
sends it encrypted to each member of the group using the `2SM` mechanism.
|
||||
It returns the updated protocol state and
|
||||
the set of direct messages (denoted as `dmsgs`) to send.
|
||||
|
||||
```
|
||||
gamma.nextSeed = random.randbytes()
|
||||
dmsgs = empty
|
||||
for each ID in recipients:
|
||||
(gamma, msg) = encrypt-to(gamma, ID, gamma.nextSeed)
|
||||
dmsgs = dmsgs + (ID, msg)
|
||||
return (gamma, dmsgs)
|
||||
```
|
||||
|
||||
### Creation of a group
|
||||
|
||||
A group is generated in a 3 steps procedure:
|
||||
|
||||
1. A user calls the `create` function and broadcasts a control message of type *create*.
|
||||
2. Each receiver of the message processes the message and broadcasts an *ack* control message.
|
||||
3. Each member processes the *ack* message received.
|
||||
|
||||
#### create
|
||||
This function generates a *create* control message and
|
||||
calls `generate-seed` to define the set of direct messages that need to be sent.
|
||||
Then it calls `process-create` to process the control message for this user.
|
||||
The function `process-create` returns a tuple including an updated state gamma and
|
||||
an update secret `I`.
|
||||
|
||||
```
|
||||
control = (“create”, gamma.mySeq, IDs)
|
||||
(gamma, dmsgs) = generate-seed(gamma, IDs)
|
||||
(gamma, _, _, I, _) = process-create(gamma, gamma.myId, gamma.mySeq, IDs, empty_string)
|
||||
return (gamma, control, dmsgs, I)
|
||||
```
|
||||
|
||||
#### process-seed
|
||||
This function initially employs `member-view` to identify the users who were part of the group when the control message was dispatched.
|
||||
Then, it attempts to acquire the seed secret through the following steps:
|
||||
|
||||
1. If the control message was dispatched by the local user,
|
||||
it uses the most recent invocation of `generate-seed` stored the seed secret in `gamma.nextSeed`.
|
||||
2. If the `control` message was dispatched by another user, and
|
||||
the local user is among its recipients,
|
||||
the function utilizes `decrypt-from` to decrypt the direct message that includes the seed secret.
|
||||
3. Otherwise, it returns an `ack` message without deriving an update secret.
|
||||
|
||||
Afterwards, `process-seed` generates separate member secrets for each group member from the seed secret by combining the seed secret and
|
||||
each user ID using HKDF.
|
||||
The secret for the sender of the message is stored in `senderSecret`,
|
||||
while those for the other group members are stored in `gamma.memberSecret`.
|
||||
The sender's member secret is immediately utilized to update their KDF ratchet and
|
||||
compute their update secret `I_sender` using `update-ratchet`.
|
||||
If the local user is the sender of the control message,
|
||||
the process is completed, and the update secret is returned.
|
||||
However, if the seed secret is received from another user,
|
||||
an `ack` control message is constructed for broadcast,
|
||||
including the sender ID and sequence number of the message being acknowledged.
|
||||
|
||||
The final step computes an update secret `I_me` for the local user invoking the `process-ack` function.
|
||||
|
||||
```
|
||||
recipients = member-view(gamma, sender) - {sender}
|
||||
if sender = gamma.myId then seed = gamma.nextSeed; gamma.nextSeed = empty_string
|
||||
else if gamma.myId in recipients then (gamma, seed) = decrypt-from(gamma, sender, dmsg)
|
||||
else
|
||||
return (gamma, (ack, ++gamma.mySeq, (sender, seq)), empty_string , empty_string , empty_string)
|
||||
|
||||
for ID in recipients do gamma.memberSecret[sender, seq, ID] = HKDF(seed, ID)
|
||||
|
||||
senderSecret = HKDF(seed, sender)
|
||||
(gamma, I_sender) = update-ratchet(gamma, sender, senderSecret)
|
||||
if sender = gamma.myId then return (gamma, empty_string , empty_string , I_sender, empty_string)
|
||||
control = (ack, ++gamma.mySeq, (sender, seq))
|
||||
members = member-view(gamma, gamma.myId)
|
||||
forward = empty
|
||||
for ID in {members - (recipients + {sender})}
|
||||
s = gamma.memberSecret[sender, seq, gamma.myId]
|
||||
(gamma, msg) = encrypt-to(gamma, ID, s)
|
||||
forward = forward + {(ID, msg)}
|
||||
|
||||
(gamma, _, _, I_me, _) = process-ack(gamma, gamma.myId, gamma.mySeq, (sender, seq), empty_string)
|
||||
return (gamma, control, forward, I_sender, I_me)
|
||||
```
|
||||
|
||||
#### process-create
|
||||
This function is called by the sender and each of the receivers of the `create` control message.
|
||||
First, it records the information from the create message in the `gamma.history+ {op}`,
|
||||
which is used to track group membership changes. Then, it proceeds to call `process-seed`.
|
||||
|
||||
```
|
||||
op = (”create”, sender, seq, IDs)
|
||||
gamma.history = gamma.history + {op}
|
||||
return (process-seed(gamma, sender, seq, dmsg))
|
||||
```
|
||||
|
||||
#### process-ack
|
||||
This function is called by those group members once they receive an ack message.
|
||||
In `process-ack`, `ackID` and `ackSeq` are the sender and
|
||||
sequence number of the acknowledged message.
|
||||
Firstly, if the acknowledged message is a group membership operation,
|
||||
it records the acknowledgement in `gamma.history`.
|
||||
|
||||
Following this, the function retrieves the relevant member secret from `gamma.memberSecret`,
|
||||
which was previously obtained from the seed secret contained in the acknowledged message.
|
||||
|
||||
Finally, it updates the ratchet for the sender of the `ack` and
|
||||
returns the resulting update secret.
|
||||
|
||||
```
|
||||
if (ackID, ackSeq) was a create / add / remove then
|
||||
op = ("ack", sender, seq, ackID, ackSeq)
|
||||
gamma.history = gamma.history + {op}`
|
||||
s = gamma.memberSecret[ackID, ackSeq, sender]
|
||||
gamma.memberSecret[ackID, ackSeq, sender] = empty_string
|
||||
if (s = empty_string) & (dmsg = empty_string) then return (gamma, empty_string, empty_string, empty_string, empty_string)
|
||||
if (s = empty_string) then (gamma, s) = decrypt-from(gamma, sender, dmsg)
|
||||
(gamma, I) = update-ratchet(gamma, sender, s)
|
||||
return (gamma, empty_string, empty_string, I, empty_string)
|
||||
```
|
||||
|
||||
The HKDF function MUST follow RFC 5869 using the hash function SHA256.
|
||||
|
||||
|
||||
### Post-compromise security updates and group member removal
|
||||
|
||||
The functions `update` and `remove` share similarities with `create`:
|
||||
they both call the function `generate-seed` to encrypt a new seed secret for each group member.
|
||||
The distinction lies in the determination of the group members using `member-view`.
|
||||
In the case of `remove`, the user being removed is excluded from the recipients of the seed secret.
|
||||
Additionally, the control message they construct is designated with type `update` or `remove` respectively.
|
||||
|
||||
Likewise, `process-update` and `process-remove` are akin to `process-create`.
|
||||
The function `process-update` skips the update of `gamma.history`,
|
||||
whereas `process-remove` includes a removal operation in the history.
|
||||
|
||||
#### update
|
||||
```
|
||||
control = ("update", ++gamma.mySeq, empty_string)
|
||||
recipients = member-view(gamma, gamma.myId) - {gamma.myId}
|
||||
(gamma, dmsgs) = generate-seed(gamma, recipients)
|
||||
(gamma, _, _, I , _) = process-update(gamma, gamma.myId, gamma.mySeq, empty_string, empty_string)
|
||||
return (gamma, control, dmsgs, I)
|
||||
```
|
||||
|
||||
#### remove
|
||||
```
|
||||
control = ("remove", ++gamma.mySeq, empty)
|
||||
recipients = member-view(gamma, gamma.myId) - {ID, gamma.myId}
|
||||
(gamma, dmsgs) = generate-seed(gamma, recipients)
|
||||
(gamma, _, _, I , _) = process-update(gamma, gamma.myId, gamma.mySeq, ID, empty_string)
|
||||
return (gamma, control, dmsgs, I)
|
||||
```
|
||||
|
||||
#### process-update
|
||||
`return process-seed(gamma, sender, seq, dmsg)`
|
||||
|
||||
#### process-remove
|
||||
```
|
||||
op = ("remove", sender, seq, removed)
|
||||
gamma.history = gamma.history + {op}
|
||||
return process-seed(gamma, sender, seq, dmsg)
|
||||
```
|
||||
|
||||
### Group member addition
|
||||
|
||||
#### add
|
||||
When adding a new group member,
|
||||
an existing member initiates the process by invoking the `add` function and
|
||||
providing the ID of the user to be added.
|
||||
This function prepares a control message marked as `add` for broadcast to the group.
|
||||
Simultaneously, it creates a welcome message intended for the new member as a direct message.
|
||||
This `welcome` message includes the current state of the sender's KDF ratchet,
|
||||
encrypted using `2SM`, along with the history of group membership operations conducted so far.
|
||||
|
||||
```
|
||||
control = ("add", ++gamma.mySeq, ID)
|
||||
(gamma, c) = encrypt-to(gamma, ID, gamma.ratchet[gamma.myId])
|
||||
op = ("add", gamma.myId, gamma.mySeq, ID)
|
||||
welcome = (gamma.history + {op}, c)
|
||||
(gamma, _, _, I, _) = process-add(gamma, gamma.myId, gamma.mySeq, ID, empty_string)
|
||||
return (gamma, control, (ID, welcome), I)
|
||||
```
|
||||
|
||||
#### process-add
|
||||
This function is invoked by both the sender and
|
||||
each recipient of an `add` message, which includes the new group member.
|
||||
If the local user is the newly added member,
|
||||
the function proceeds to call `process-welcome` and then exits.
|
||||
Otherwise, it extends `gamma.history` with the `add` operation.
|
||||
|
||||
Line 5 determines whether the local user was already a group member at the time the `add` message was sent;
|
||||
this condition is typically true but may be false if multiple users were added concurrently.
|
||||
|
||||
On lines 6 to 8, the ratchet for the sender of the *add* message is updated twice.
|
||||
In both calls to `update-ratchet`,
|
||||
a constant string is used as the ratchet input instead of a random seed secret.
|
||||
|
||||
The value returned by the first ratchet update is stored in `gamma.memberSecret` as the added user’s initial member secret.
|
||||
The result of the second ratchet update becomes `I_sender`,
|
||||
the update secret for the sender of the `add` message.
|
||||
On line 10, if the local user is the sender, the update secret is returned.
|
||||
|
||||
If the local user is not the sender, an acknowledgment for the `add` message is required.
|
||||
Therefore, on line 11, a control message of type `add-ack` is constructed for broadcast.
|
||||
Subsequently, in line 12 the current ratchet state is encrypted using `2SM` to generate a direct message intended for the added user,
|
||||
allowing them to decrypt subsequent messages sent by the sender.
|
||||
Finally, in lines 13 to 15, `process-add-ack` is called to calculate the local user’s update secret (`I_me`),
|
||||
which is then returned along with `I_sender`.
|
||||
|
||||
```
|
||||
if added = gamma.myId then return process-welcome(gamma, sender, seq, dmsg)
|
||||
op = ("add", sender, seq, added)
|
||||
gamma.history = gamma.history + {op}
|
||||
if gamma.myId in member-view(gamma, sender) then
|
||||
(gamma, s) = update-ratchet(gamma, sender, "welcome")
|
||||
gamma.memberSecret[sender, seq, added] = s
|
||||
(gamma, I_sender) = update-ratchet(gamma, sender, "add")
|
||||
else I_sender = empty_string
|
||||
if sender = gamma.myId then return (gamma, empty_string, empty_string, I_sender, empty_string)
|
||||
control = ("add-ack", ++gamma.mySeq, (sender, seq))
|
||||
(gamma, c) = encrypt-to(gamma, added, ratchet[gamma.myId])
|
||||
(gamma, _, _, I_me, _) = process-add-ack(gamma, gamma.myId, gamma.mySeq, (sender, seq), empty_string)
|
||||
return (gamma, control, {(added, c)}, I_sender, I_me)
|
||||
```
|
||||
|
||||
#### process-add-ack
|
||||
This function is invoked by both the sender and each recipient of an `add-ack` message,
|
||||
including the new group member.
|
||||
Upon lines 1–2, the acknowledgment is added to `gamma.history`,
|
||||
mirroring the process in `process-ack`.
|
||||
If the current user is the new group member,
|
||||
the `add-ack` message includes the direct message constructed in `process-add`;
|
||||
this direct message contains the encrypted ratchet state of the sender of the `add-ack`,
|
||||
then it is decrypted on lines 3–5.
|
||||
|
||||
Upon line 6, a check is performed to check if the local user was already a group member at the time the `add-ack` was sent.
|
||||
If affirmative, a new update secret `I` for the sender of the `add-ack` is computed on line 7 by invoking `update-ratchet` with the constant string `add`.
|
||||
|
||||
In the scenario involving the new member,
|
||||
the ratchet state was recently initialized on line 5.
|
||||
This ratchet update facilitates all group members, including the new addition,
|
||||
to derive each member’s update by obtaining any update secret from before their inclusion.
|
||||
|
||||
```
|
||||
op = ("ack", sender, seq, ackID, ackSeq)
|
||||
gamma$.history = gamma.history + {op}
|
||||
if dmsg != empty_string then
|
||||
(gamma, s) = decrypt-from(gamma, sender, dmsg)
|
||||
gamma.ratchet[sender] = s
|
||||
if gamma.myId in member-view(gamma, sender) then
|
||||
(gamma, I) = update-ratchet(gamma, sender, "add")
|
||||
return (gamma, empty_string, empty_string, I, empty_string)
|
||||
else return (gamma, empty_string, empty_string, empty_string, empty_string)
|
||||
```
|
||||
|
||||
#### process-welcome
|
||||
This function serves as the second step called by a newly added group member.
|
||||
In this context, `adderHistory` represents the adding user’s copy of `gamma.history` sent in their welcome message,
|
||||
which is utilized to initialize the added user’s history.
|
||||
Here, `c` denotes the ciphertext of the adding user’s ratchet state,
|
||||
which is decrypted on line 2 using `decrypt-from`.
|
||||
|
||||
Once `gamma.ratchet[sender]` is initialized,
|
||||
`update-ratchet` is invoked twice on lines 3 to 5 with the constant strings `welcome` and `add` respectively.
|
||||
These operations mirror the ratchet operations performed by every other group member in `process-add`.
|
||||
The outcome of the first `update-ratchet` call becomes the first member secret for the added user,
|
||||
while the second call returns `I_sender`, the update secret for the sender of the add operation.
|
||||
|
||||
Subsequently, the new group member constructs an *ack* control message to broadcast on line 6 and
|
||||
calls `process-ack` to compute their initial update secret I_me.
|
||||
The function `process-ack` reads from `gamma.memberSecret` and
|
||||
passes it to `update-ratchet`.
|
||||
The previous ratchet state for the new member is the empty string `empty`, as established by `init`,
|
||||
thereby initializing the new member’s ratchet.
|
||||
Upon receiving the new member’s `ack`,
|
||||
every other group member initializes their copy of the new member’s ratchet in a similar manner.
|
||||
|
||||
By the conclusion of `process-welcome`,
|
||||
the new group member has acquired update secrets for themselves and the user who added them.
|
||||
The ratchets for other group members are initialized by `process-add-ack`.
|
||||
|
||||
```
|
||||
gamma.history = adderHistory
|
||||
(gamma, gamma.ratchet[sender]) = decrypt-from(gamma, sender, c)
|
||||
(gamma, s) = update-ratchet(gamma, sender, "welcome")
|
||||
gamma.memberSecret[sender, seq, gamma.myId] = s
|
||||
(gamma, I_sender) = update-ratchet(gamma, sender, "add")
|
||||
control = ("ack", ++gamma.mySeq, (sender, seq))
|
||||
(gamma, _, _, I_me, _) = process-ack(gamma, gamma.myId, gamma.mySeq, (sender, seq), empty_string)
|
||||
return (gamma, control, empty_string , I_sender, I_me)
|
||||
```
|
||||
|
||||
## Privacy Considerations
|
||||
|
||||
### Dependency on PKI
|
||||
The [DCGKA](https://eprint.iacr.org/2020/1281) proposal presents some limitations highlighted by the authors.
|
||||
Among these limitations one finds the requirement of a PKI (or a key server) mapping IDs to public keys.
|
||||
|
||||
One method to overcome this limitation is adapting the protocol SIWE (Sign in with Ethereum) so
|
||||
a user `u_1` who wants to start a communication with a user `u_2` can interact with latter’s wallet to request a public key using an Ethereum address as `ID`.
|
||||
|
||||
#### SIWE
|
||||
The [SIWE](https://docs.login.xyz/general-information/siwe-overview) (Sign In With Ethereum) proposal was a suggested standard for leveraging Ethereum to authenticate and authorize users on web3 applications.
|
||||
Its goal is to establish a standardized method for users to sign in to web3 applications using their Ethereum address and private key,
|
||||
mirroring the process by which users currently sign in to web2 applications using their email and password.
|
||||
Below follows the required steps:
|
||||
|
||||
1. A server generates a unique Nonce for each user intending to sign in.
|
||||
2. A user initiates a request to connect to a website using their wallet.
|
||||
3. The user is presented with a distinctive message that includes the Nonce and details about the website.
|
||||
4. The user authenticates their identity by signing in with their wallet.
|
||||
5. Upon successful authentication, the user's identity is confirmed or approved.
|
||||
6. The website grants access to data specific to the authenticated user.
|
||||
|
||||
#### Our approach
|
||||
The idea in the [DCGKA](https://eprint.iacr.org/2020/1281) setting closely resembles the procedure outlined in SIWE. Here:
|
||||
|
||||
1. The server corresponds to user D1,
|
||||
who initiates a request (instead of generating a nonce) to obtain the public key of user D2.
|
||||
2. Upon receiving the request, the wallet of D2 send the request to the user,
|
||||
3. User D2 receives the request from the wallet, and decides whether accepts or rejects.
|
||||
4. The wallet and responds with a message containing the requested public key in case of acceptance by D2.
|
||||
|
||||
This message may be signed, allowing D1 to verify that the owner of the received public key is indeed D2.
|
||||
|
||||
### Multi-device setting
|
||||
One may see the set of devices as a group and create a group key for internal communications.
|
||||
One may use treeKEM for instance,
|
||||
since it provides interesting properties like forward secrecy and post-compromise security.
|
||||
All devices share the same `ID`,
|
||||
which is held by one of them, and from other user’s point of view, they would look as a single user.
|
||||
|
||||
Using servers, like in the paper [Multi-Device for Signal](https://eprint.iacr.org/2019/1363), should be avoided;
|
||||
but this would imply using a particular device as receiver and broadcaster within the group.
|
||||
There is an obvious drawback which is having a single device working as a “server”.
|
||||
Should this device be attacked or without connection, there should be a mechanism for its revocation and replacement.
|
||||
|
||||
Another approach for communications between devices could be using the keypair of each device.
|
||||
This could open the door to use UPKE, since keypairs should be regenerated frequently.
|
||||
|
||||
Each time a device sends a message, either an internal message or an external message,
|
||||
it needs to replicate and broadcast it to all devices in the group.
|
||||
|
||||
The mechanism for the substitution of misbehaving leader devices follows:
|
||||
|
||||
1. Each device within a group knows the details of other leader devices.
|
||||
This information may come from metadata in received messages, and is replicated by the leader device.
|
||||
2. To replace a leader, the user should select any other device within its group and
|
||||
use it to send a signed message to all other users.
|
||||
3. To get the ability to sign messages,
|
||||
this new leader should request the keypair associated to the ID to the wallet.
|
||||
4. Once the leader has been changed,
|
||||
it revocates access from DCGKA to the former leader using the DCGKA protocol.
|
||||
5. The new leader starts a key update in DCGKA.
|
||||
|
||||
Not all devices in a group should be able to send messages to other users.
|
||||
Only the leader device should be in charge of sending and receiving messages.
|
||||
To prevent other devices from sending messages outside their group, a requirement should be signing each message.
|
||||
The keys associated to the `ID` should only be in control of the leader device.
|
||||
|
||||
The leader device is in charge of setting the keys involved in the DCGKA.
|
||||
This information must be replicated within the group to make sure it is updated.
|
||||
|
||||
To detect missing messages or potential misbehavior, messages must include a counter.
|
||||
|
||||
### Using UPKE
|
||||
|
||||
Managing the group of devices of a user can be done either using a group key protocol such as treeKEM or
|
||||
using the keypair of each device.
|
||||
Setting a common key for a group of devices under the control of the same actor might be excessive,
|
||||
furthermore it may imply some of the problems one can find in the usual setting of a group of different users;
|
||||
for example: one of the devices may not participate in the required updating processes, representing a threat for the group.
|
||||
|
||||
The other approach to managing the group of devices is using each device’s keypair,
|
||||
but it would require each device updating these materia frequently, something that may not happens.
|
||||
|
||||
[UPKE](https://eprint.iacr.org/2022/068) is a form of asymetric cryptography
|
||||
where any user can update any other user’s key pair by running an update algorithm with (high-entropy) private coins.
|
||||
Any sender can initiate a *key update* by sending a special update ciphertext.
|
||||
This ciphertext updates the receiver’s public key and also, once processed by the receiver, will update their secret key.
|
||||
|
||||
To the best of my knowledge,
|
||||
there exists several efficient constructions both [UPKE from ElGamal](https://eprint.iacr.org/2019/1189) (based in the DH assumption) and
|
||||
[UPKE from Lattices]((https://eprint.iacr.org/2023/1400)) (based in lattices).
|
||||
None of them have been implemented in a secure messaging protocol, and this opens the door to some novel research.
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
## References
|
||||
- [DCGKA](https://eprint.iacr.org/2020/1281)
|
||||
- [MLS](https://messaginglayersecurity.rocks)
|
||||
- [CoCoa](https://eprint.iacr.org/2022/251)
|
||||
- [Causal TreeKEM](https://mattweidner.com/assets/pdf/acs-dissertation.pdf)
|
||||
- [SIWE](https://docs.login.xyz/general-information/siwe-overview)
|
||||
- [Multi-device for Signal](https://eprint.iacr.org/2019/1363)
|
||||
- [UPKE](https://eprint.iacr.org/2022/068)
|
||||
- [UPKE from ElGamal](https://eprint.iacr.org/2019/1189)
|
||||
- [UPKE from Lattices](https://eprint.iacr.org/2023/1400)
|
||||
1364
vac/raw/eth-secpm.md
Normal file
@@ -1,6 +1,5 @@
|
||||
---
|
||||
slug: 46
|
||||
title: 46/GOSSIPSUB-TOR-PUSH
|
||||
title: GOSSIPSUB-TOR-PUSH
|
||||
name: Gossipsub Tor Push
|
||||
status: raw
|
||||
category: Standards Track
|
||||
BIN
vac/raw/images/eth-secpm_add.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
vac/raw/images/eth-secpm_creation.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
vac/raw/images/eth-secpm_credential.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
vac/raw/images/eth-secpm_fetching.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
vac/raw/images/eth-secpm_onchain-register-1.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
vac/raw/images/eth-secpm_onchain-register-2.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
vac/raw/images/eth-secpm_onchain-update.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
vac/raw/images/eth-secpm_remove.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
vac/raw/images/eth-secpm_update.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
1
vac/raw/images/test.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
744
vac/raw/mix.md
Normal file
@@ -0,0 +1,744 @@
|
||||
---
|
||||
title: LIBP2P-MIX
|
||||
name: Libp2p Mix Protocol
|
||||
status: raw
|
||||
category: Standards Track
|
||||
tags:
|
||||
editor: Akshaya Mani <akshaya@status.im>
|
||||
contributors:
|
||||
---
|
||||
|
||||
## Abstract
|
||||
|
||||
This document specifies the Mix protocol, a custom protocol within the
|
||||
[libp2p](https://libp2p.io) framework designed to enable anonymous communication
|
||||
in peer-to-peer networks. The Mix protocol allows libp2p nodes to send messages
|
||||
without revealing the sender's identity to intermediary nodes or the recipient.
|
||||
It achieves this by using the [Sphinx packet format](https://www.researchgate.net/publication/220713667_Sphinx_A_Compact_and_Provably_Secure_Mix_Format),
|
||||
which encrypts and routes messages through a series of nodes (mix nodes)
|
||||
before reaching the recipient.
|
||||
|
||||
Key features of the protocol include:
|
||||
|
||||
i. Path selection for choosing a random route through the network via multiple
|
||||
mix nodes.\
|
||||
ii. Sphinx packet construction and processing, providing cryptographic
|
||||
guarantees of anonymity and security.\
|
||||
iii. Pluggable spam protection mechanism to prevent abuse of the mix network.\
|
||||
iv. Delayed message forwarding to thwart timing analysis attacks.
|
||||
|
||||
**Protocol identifier:** `"/mix/1.0.0"`
|
||||
|
||||
Note: The Mix Protocol is designed to work alongside existing libp2p protocols,
|
||||
allowing for seamless integration with current libp2p applications while
|
||||
providing enhanced privacy features. For example, it can encapsulate messages
|
||||
from protocols like [GossipSub](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.2.md)
|
||||
to ensure sender anonymity.
|
||||
|
||||
## Background
|
||||
|
||||
libp2p protocols do not inherently protect sender identities.
|
||||
|
||||
The Mix protocol enhances anonymity in libp2p by implementing a mix network,
|
||||
where messages are anonymized through multiple relay nodes before reaching the
|
||||
intended recipient. The Sphinx packet format is a well-researched component which
|
||||
this specification leverages to offer strong anonymity properties by concealing
|
||||
sender and recipient information at each relay.
|
||||
|
||||
Using this approach, even the nodes relaying messages cannot determine the
|
||||
sender or final recipient, within a robust adversarial model. This decentralized
|
||||
solution distributes trust among participants, eliminating single points of
|
||||
failure and enhancing overall network resilience. Additionally, pluggable
|
||||
spam protection mechanism and delayed forwarding address common attacks on
|
||||
anonymity networks, such as spam and timing analysis.
|
||||
|
||||
The Mix protocol is designed with flexibility in mind, allowing for
|
||||
pluggable components such as spam protection, peer discovery, and
|
||||
incentivization mechanisms. This design choice enables the protocol to evolve
|
||||
and adapt to different network requirements and constraints. This also leaves
|
||||
room for future enhancements such as cover traffic generation.
|
||||
|
||||
By incorporating these features, the Mix protocol aims to provide a robust
|
||||
anonymity layer within the libp2p ecosystem, enabling developers to easily
|
||||
incorporate privacy features into their applications.
|
||||
|
||||
## Specification
|
||||
|
||||
### 1. Protocol Identifier
|
||||
|
||||
The Mix protocol is identified by the string `"/mix/1.0.0"`.
|
||||
|
||||
### 2. Custom Mix Protocol
|
||||
|
||||
The Mix protocol is designed as a standalone protocol,
|
||||
identified by the protocol identifier `"/mix/1.0.0"`.
|
||||
This approach allows the Mix protocol to operate independently,
|
||||
decoupled from specific applications,
|
||||
providing greater flexibility and reusability across various libp2p protocols.
|
||||
By doing so, the Mix protocol can evolve independently,
|
||||
focusing on its core functionality without being tied to the development
|
||||
and maintenance cycles of other protocols.
|
||||
|
||||
#### 2.1 Mix Nodes Roles
|
||||
|
||||
All nodes participating in the Mix protocol are considered as mix nodes. They
|
||||
have the capability to create/process and forward Sphinx packets. Mix nodes can
|
||||
play different roles depending on their position in a particular message path:
|
||||
|
||||
- **Sender Node**
|
||||
- A mix node that initiates the anonymous message publishing process.
|
||||
- Responsible for:
|
||||
- Path selection.
|
||||
- Sphinx packet creation.
|
||||
- Initiating the message routing through the mix network.
|
||||
- Must run both the Mix protocol instance and the instance of the libp2p
|
||||
protocol for the message being published (_e.g.,_ GossipSub, Ping, etc.).
|
||||
- **Intermediary Mix Node**
|
||||
- A mix node that is neither the sender nor the exit node in a message path.
|
||||
- Responsible for:
|
||||
- Receiving Sphinx packets.
|
||||
- Processing (decrypting and re-encrypting) Sphinx packets.
|
||||
- Forwarding processed packets to the next node in the path.
|
||||
- Only needs to run the Mix protocol instance.
|
||||
- **Exit Node**
|
||||
- The final mix node in a message path.
|
||||
- Responsible for:
|
||||
- Receiving and processing the final Sphinx packet.
|
||||
- Extracting the original message.
|
||||
- Disseminating the decrypted message using the appropriate libp2p protocol.
|
||||
- Must run both the Mix protocol instance and the instance of the libp2p
|
||||
protocol for the message being published.
|
||||
|
||||
#### 2.2 Roles Flexibility
|
||||
|
||||
A single mix node can play different roles in different paths:
|
||||
|
||||
- It can be a sender node for messages it initiates.
|
||||
- It can be an intermediary node for messages it is forwarding.
|
||||
- It can be an exit node for messages it is disseminating.
|
||||
|
||||
#### 2.3 Incentives
|
||||
|
||||
To publish an anonymous libp2p message (_e.g.,_ GossipSub, Ping, etc.), nodes
|
||||
MUST run a mix node instance. This requirement serves as an incentive for nodes
|
||||
to participate in the mix network, as it allows them to benefit from the
|
||||
anonymity features while also contributing to the network's overall anonymity
|
||||
and robustness.
|
||||
|
||||
#### 2.4 Node Discovery
|
||||
|
||||
All mix nodes participate in the discovery process and maintain a list of
|
||||
discovered nodes.
|
||||
|
||||
- **Bootstrap Nodes**
|
||||
|
||||
i. The network has a set of well-known bootstrap nodes that new mix nodes
|
||||
can connect to when joining the network.\
|
||||
ii. The bootstrap nodes help new mix nodes discover other active mix nodes in
|
||||
the network.
|
||||
|
||||
- **Discovery**
|
||||
|
||||
i. All mix nodes publish their Ethereum Node Records (ENRs) containing:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "v4",
|
||||
"multiaddr": "/ip4/192.0.2.1/udp/9000/quic",
|
||||
"ed25519": "0x5a6fcd3e9d6a5e4d5f71e7e5b4cfa9b7b73d9f5f7e9a8b9c5d7f9e8d5a6f7c9e",
|
||||
"mix": "/mix/1.0.0",
|
||||
"supported_protocols": ["ping", "gossipsub"]
|
||||
}
|
||||
```
|
||||
|
||||
**Field Explanations**
|
||||
|
||||
- `id`: Indicates the ENR format version (_e.g.,_ `"v4"`).
|
||||
- `multiaddr`: The node's multiaddress, including the transport protocol
|
||||
(_e.g.,_ QUIC) and IP address/port.
|
||||
- `ed25519`: The node's Ed25519 public key, used for Sphinx encryption.
|
||||
- `mix`: Indicates the supported Mix protocol version (_e.g.,_ `"/mix/1.0.0"`).
|
||||
- `supported_protocols`: A list of other libp2p protocols supported by the
|
||||
node (_e.g.,_ Ping, GossipSub, etc.).
|
||||
- Additional fields may be included based on the node's requirements.
|
||||
|
||||
ii. The mix nodes use a peer discovery protocol like [WAKU](https://waku.org)/[Discv5](https://github.com/ethereum/devp2p/blob/master/discv5/discv5.md):
|
||||
|
||||
- Connect to a set of bootstrap nodes when joining the network.
|
||||
- Regularly update their list of known peers.
|
||||
- Obtain a random sample of nodes that is representative of the network.
|
||||
|
||||
- **Path Selection (Message Senders Only)**
|
||||
|
||||
To send an anonymous message, a mix node performs the following actions:
|
||||
|
||||
i. Choose a random exit node that supports the required libp2p protocol for
|
||||
the message.\
|
||||
ii. Select remaining L-1 unique mix nodes randomly without replacement from
|
||||
the list of discovered nodes.
|
||||
|
||||
- **Forwarding To Next Hop (Intermediary Nodes Only)**
|
||||
|
||||
When a mix node receives an incoming Sphinx packet, it performs the following
|
||||
actions:
|
||||
|
||||
i. Decrypts the packet to obtain the next hop multiaddress\
|
||||
ii. Checks if the next hop is in the list of discovered nodes.\
|
||||
iii. If not, performs discovery for that specific node.\
|
||||
iv. Forwards the Sphinx packet to the next hop.
|
||||
|
||||
#### 2.5 Protocol Registration
|
||||
|
||||
The protocol is registered with the libp2p host using the `"/mix/1.0.0"`
|
||||
identifier. This identifier is used to establish connections and negotiate the
|
||||
protocol between libp2p peers.
|
||||
|
||||
#### 2.6 Transport Layer
|
||||
|
||||
The Mix protocol uses secure transport protocols to ensure confidentiality and
|
||||
integrity of communications. The recommended transport protocols are
|
||||
[QUIC](https://datatracker.ietf.org/doc/rfc9000/) or TLS (preferably QUIC
|
||||
due to its performance benefits and built-in features
|
||||
such as low latency and efficient multiplexing).
|
||||
|
||||
#### 2.7 Connection Establishment
|
||||
|
||||
- The sender initiates a secure connection (TLS or QUIC) to the first mix node
|
||||
using the libp2p transport.
|
||||
- The sender uses the `"/mix/1.0.0"` protocol identifier to convey that the
|
||||
connection is for the Mix protocol.
|
||||
- Once the connection is established, the sender can forward Sphinx packets
|
||||
using the Mix protocol.
|
||||
- Subsequent mix nodes in the path follow the same process when forwarding
|
||||
messages to other mix nodes.
|
||||
|
||||
### 3. Cryptographic Primitives and Security Parameter
|
||||
|
||||
- **Security Parameter:** $\kappa = 128$ bits provides a balance between
|
||||
security and efficiency.
|
||||
- **Cryptographic Primitives**
|
||||
- **Group G**: Curve25519 elliptic curve offers 128-bit security with small
|
||||
(32-byte) group elements, efficient for both encryption and key exchange.
|
||||
- **Hash function H**: SHA-256.
|
||||
- **KDF:** SHA-256 (truncated to 128 bits).
|
||||
- **AES-CTR:** AES-128 in counter mode.
|
||||
- **Inputs:** Key `k` (16 bytes), Initialization Vector `iv` (16 bytes),
|
||||
Plaintext `p`
|
||||
- **Initialization Vector (IV)**: 16 bytes, chosen randomly for each
|
||||
encryption.
|
||||
- **Plaintext**: Data to be encrypted (_e.g.,_ routing information, message
|
||||
payload).
|
||||
- **Output**: Ciphertext `c` (same size as plaintext `p`).
|
||||
- **Operation**: AES-CTR mode uses key and the counter (`iv`) to produce a
|
||||
keystream, which is XORed with the plaintext to produce the ciphertext.
|
||||
- **HMAC-SHA-256:** 256-bit MAC (truncated to 128 bits).
|
||||
- **Inputs:** Key `k` (16 bytes), Message `m`
|
||||
- **Message**: Data to be authenticated (_e.g.,_ $β$ component).
|
||||
- **Output**: MAC `mac` (truncated to 128 bits).
|
||||
- **Operation**: HMAC-SHA-256 uses the key and the message to produce a
|
||||
hash-based message authentication code.
|
||||
|
||||
### 4. Sphinx Packet Format
|
||||
|
||||
#### 4.1 Packet Components and Sizes
|
||||
|
||||
1. **Alpha ($α$)**: 32 bytes
|
||||
|
||||
- Represents a Curve25519 group element (x-coordinate in GF(2^255 - 19)).
|
||||
- Used by mix nodes to extract shared session key using their private key.
|
||||
|
||||
2. **Beta ($β$)**: $((t+1)r + 1)\kappa$ bytes typically, where $r$ is the maximum
|
||||
path length.
|
||||
|
||||
- Contains the encrypted routing information.
|
||||
- We recommend a reasonable maximum path length of $r=5$, considering
|
||||
latency/anonymity trade-offs.
|
||||
- This gives a reasonable size of $336$ bytes, when $t = 3$ (refer
|
||||
Section 5.2.10 for the choice of $t$).
|
||||
- We extend $β$ to accommodate next hop address and delay below.
|
||||
|
||||
3. **Gamma ($γ$)**: $\kappa$ bytes (16 bytes)
|
||||
|
||||
- Output of HMAC-SHA-256, truncated to 128 bits.
|
||||
- Ensures the integrity of the header information.
|
||||
|
||||
4. **Delta ($δ$)**: The encrypted payload, which can be of variable size.
|
||||
- According to the [MixMatch](https://petsymposium.org/popets/2024/popets-2024-0050.pdf)
|
||||
paper, the Nym network uses Sphinx packets of a fixed
|
||||
size (2413 bytes).
|
||||
- Considering this, the maximum $δ$ size can be chosen as 2413 bytes minus
|
||||
the header length (which will be derived below).
|
||||
|
||||
#### 4.2 Address Format and Delay Specification
|
||||
|
||||
In the original
|
||||
[Sphinx](https://cypherpunks.ca/~iang/pubs/Sphinx_Oakland09.pdf) paper, the
|
||||
authors use node IDs of size $\kappa$ ($16$ bytes) to represent the next hop
|
||||
addresses. To accommodate larger addresses, we'll use a combined size of
|
||||
$t\kappa$ bytes for the address and delay, where $t$ is small (_e.g.,_ $t = 2$
|
||||
or $3$).
|
||||
|
||||
- **Delay**: 2 bytes
|
||||
Allows delays up to 65,535 milliseconds ≈ 65 seconds.
|
||||
- **Address**: $t\kappa-2$ bytes
|
||||
This flexible format can accommodate various address types, including:
|
||||
- libp2p multiaddress (variable length, typically 32-64 bytes).
|
||||
- Custom format with:
|
||||
- IP address (IPv4 or IPv6, 4 or 16 bytes)
|
||||
- TCP/UDP port number (2 bytes)
|
||||
- QUIC/TLS protocol identifier flag (1 byte)
|
||||
- Peer ID (32 bytes for Ed25519 or Secp256k1).
|
||||
|
||||
The entire Sphinx packet header ($α$, $β$, and $γ$) can fit within a fixed size
|
||||
of $32 + (r(t+1)+1)\kappa + 16 = 384$ bytes, leaving ample room for a large $δ$ of
|
||||
up to $2413 - 384 = 2029$ bytes.
|
||||
|
||||
#### 4.3 Message Format
|
||||
|
||||
The Mix protocol uses the Sphinx packet format to encapsulate messages and
|
||||
routing information.
|
||||
|
||||
```proto
|
||||
message SphinxPacket {
|
||||
bytes alpha = 1; // 32 bytes
|
||||
bytes beta = 2; // 304 - 384 bytes
|
||||
bytes gamma = 3; // 16 bytes
|
||||
bytes delta = 4; // variable size, max 2029 bytes
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Handler Function
|
||||
|
||||
The [handler function](https://docs.libp2p.io/concepts/fundamentals/protocols/#handler-functions)
|
||||
is responsible for processing connections and messages for
|
||||
the Mix protocol. It operates according to the mix node roles (_i.e.,_ sender,
|
||||
intermediary mix node, or exit node) defined in
|
||||
[Section 2.1](#21-mix-nodes-roles). This function is crucial for implementing
|
||||
the core functionality of the mixnet protocol within the libp2p framework.
|
||||
|
||||
When a node receives a new stream for the `"/mix/1.0.0"` protocol, the handler
|
||||
function is invoked. It performs different operations based on the node's role
|
||||
in the current message path:
|
||||
|
||||
- **Role Determination**
|
||||
|
||||
The handler first determines the node's role for the incoming message. This
|
||||
is typically done by examining the packet structure and the node's position
|
||||
in the network.
|
||||
|
||||
- **Packet Processing**
|
||||
|
||||
Depending on the role, the handler processes the Sphinx packet differently:
|
||||
|
||||
- For senders, it creates and sends new Sphinx packets.
|
||||
- For intermediary nodes, it processes and forwards existing packets.
|
||||
- For exit nodes, it decrypts the final layer and disseminates the original message.
|
||||
|
||||
- **Error Handling**
|
||||
|
||||
It manages any errors that occur during packet processing, such as invalid
|
||||
MACs or decryption failures.
|
||||
|
||||
- **Logging and Metrics**
|
||||
|
||||
The handler is also be responsible for logging important events and
|
||||
collecting metrics for network analysis and debugging.
|
||||
|
||||
The specific implementation of the handler function for each role (_i.e.,_
|
||||
sender, intermediary, and exit node) is detailed in the following subsections.
|
||||
|
||||
#### 5.1 Sender
|
||||
|
||||
1. **Convert the libp2p Message to Bytes**
|
||||
|
||||
Serialize the libp2p message to bytes and store the result in
|
||||
`libp2p_message`. This can be done using Protocol Buffers or another
|
||||
serialization method.
|
||||
|
||||
2. **Apply Spam Protection**
|
||||
|
||||
Apply the chosen spam protection mechanism to the `libp2p_message`.
|
||||
This could be Proof of Work (PoW), Verifiable Delay Function (VDF),
|
||||
Rate Limiting Nullifier (RLN), or other suitable approaches.
|
||||
|
||||
Refer to [Appendix A](#appendix-a-example-spam-protection-using-proof-of-work)
|
||||
for details on the current implementation using PoW.
|
||||
|
||||
3. **Prepare the Message**
|
||||
|
||||
Prepare the `message` by combining the `libp2p_message` with any necessary data
|
||||
from the spam protection mechanism. The exact format of `message` will depend
|
||||
on the chosen spam protection method.
|
||||
|
||||
Note: The spam protection mechanism is designed as a pluggable interface,
|
||||
allowing for different methods to be implemented based on network requirements.
|
||||
This flexibility extends to other components such as peer discovery and incentivization,
|
||||
which are not specified in detail to allow for future optimizations and adaptations.
|
||||
|
||||
4. **Perform Path Selection** (refer [Section 2.4](#24-node-discovery))
|
||||
|
||||
- Let the Ed25519 public keys of the mix nodes in the path be
|
||||
$y_0,\ y_1,\ \ldots,\ y_{L-1}$.
|
||||
|
||||
5. **Wrap Final Message in Sphinx Packet**
|
||||
Perform the following steps to wrap `message` in a Sphinx packet:
|
||||
|
||||
a. **Compute** **Alphas ($α_i$**, **$i=0$** to **$L-1$)**
|
||||
|
||||
- Select a random exponent $x$ from $\mathbb{Z}_q^*$.
|
||||
- Compute initial alpha $α_0$, shared secret $s_0$, and blinding factor $b_0$:
|
||||
- $α_0 = g^x$ using Curve25519 scalar multiplication.
|
||||
- $s_0 = y_0^x$, where $y_0$ is the public key of the first hop.
|
||||
- $b_0 = H(α_0\ |\ s_0)$, where $H$ is the SHA-256 hash function (refer
|
||||
_[Section 3](#3-cryptographic-primitives-and-security-parameter)_ for details).
|
||||
- For each node $i$ (from $1$ to $L-1$):
|
||||
- $α_i = α_{i-1}^{b_{i-1}}$ using Curve25519 scalar multiplication.
|
||||
- $s_i = y_{i}^{x\prod_{\text{j=0}}^{\text{i-1}} b_{j}}$, where $y_{i}$ is
|
||||
the public key of the i-th hop.
|
||||
- $b_i = H(α_i\ |\ s_i)$, where $H$ is the SHA-256 hash function.
|
||||
|
||||
Note that $\alpha_i$ and $s_i$ are group elements, each 32 bytes long.
|
||||
|
||||
b. **Compute** **Filler Strings ($\phi_i$**, **$i=0$** to **$L-1$)**
|
||||
|
||||
- Initialize $\phi_0$ as an empty string.
|
||||
- For each $i$ (from $1$ to $L-1$):
|
||||
|
||||
- Derive the AES key and IV:
|
||||
|
||||
$`\text{φ\_aes\_key}_{i-1} = KDF(\text{"aes\_key"}\ |\ s_{i-1})`$
|
||||
|
||||
$`\text{φ\_iv}_{i-1} = H(\text{"iv"}\ |\ s_{i-1})`$ (truncated to 128 bits)
|
||||
|
||||
- Compute the filler string $\phi_i$ using $\text{AES-CTR}^\prime_i$,
|
||||
which is AES-CTR encryption with the keystream starting from
|
||||
index $((t+1)(r-i)+t+2)\kappa$ :
|
||||
|
||||
$`\phi_i = \text{AES-CTR}^\prime_i(\text{φ\_aes\_key}_{i-1},\ \text{φ\_iv}_{i-1},
|
||||
\ \phi_{i-1}\ |\ 0_{(t+1)\kappa})`$,
|
||||
where $0_{(t+1)\kappa}$ is the string of $0$ bits of length $(t+1)\kappa$.
|
||||
|
||||
Note that the length of $\phi_i$ is $(t+1)i\kappa$.
|
||||
|
||||
c. **Compute** **Betas and Gammas ($\beta_i$**, $\gamma_i$, **$i=0$** to **$L-1$)**
|
||||
|
||||
For each $i$ (from $L-1$ to $0$):
|
||||
|
||||
- Derive the AES key, MAC key, and IV:
|
||||
|
||||
$`\text{β\_aes\_key}_{i} = KDF(\text{"aes\_key"}\ |\ s_{i})`$
|
||||
|
||||
$`\text{mac\_key}_{i} = KDF(\text{"mac\_key"}\ |\ s_{i})`$
|
||||
|
||||
$`\text{β\_iv}_{i} = H(\text{"iv"}\ |\ s_{i})`$ (truncated to 128 bits)
|
||||
|
||||
- Generate random $`\text{delay\_i}`$, a 16-bit unsigned integer (0-65535 milliseconds).
|
||||
|
||||
Note that top-level applications can use other probability distributions,
|
||||
such as an exponential distribution, where shorter delays are more likely
|
||||
than longer delays. This can mimic real-world traffic patterns and provide
|
||||
robust anonymity against traffic analysis. The trade-off lies in balancing
|
||||
the need for flexible delay handling with the risk of exposing
|
||||
application-specific traffic patterns.
|
||||
|
||||
- If $i = L-1$ (_i.e.,_ exit node):
|
||||
|
||||
$`\beta_i = \text{AES-CTR}(\text{β\_aes\_key}_{i},\ \text{β\_iv}_{i},\ 0_{((t+1)
|
||||
(r-L)+t+2)\kappa})\ |\ \phi_{L-1}`$
|
||||
|
||||
- Otherwise (_i.e.,_ intermediary node):
|
||||
|
||||
$`\beta_i = \text{AES-CTR}(\text{β\_aes\_key}_{i},\ \text{β\_iv}_{i},\ \text
|
||||
{addr}_{i+1} \ |\ \text{delay}_{i+1}\ | \ \gamma_{i+1}\ |\ {\beta_{i+1}}_
|
||||
{[0\ldots(r(t+1)-t)\kappa−1]})`$
|
||||
|
||||
Note that the length of $\beta_i$ is $(r(t+1)+1)\kappa$, $0 \leq i \leq L-1$,
|
||||
where $t$ is the combined length of next hop address and delay.
|
||||
|
||||
- $`\gamma_i = \text{HMAC-SHA-256}(\text{mac\_key}_i,\ β_i)`$\
|
||||
Note that the length of $\gamma_i$ is $\kappa$.
|
||||
|
||||
d. **Compute** **Deltas (**$\delta_i$, **$i=0$** to **$L-1$)**
|
||||
|
||||
For each $i$ (from $L-1$ to $0$):
|
||||
|
||||
- Derive the AES key and IV:
|
||||
|
||||
$`\text{δ\_aes\_key}_{i} = KDF(\text{"δ\_aes\_key"}\ |\ s_{i})`$
|
||||
|
||||
$`\text{δ\_iv}_{i} = H(\text{"δ\_iv"}\ |\ s_{i})`$ (truncated to 128 bits)
|
||||
|
||||
- If $i = L-1$ (_i.e.,_ exit node):
|
||||
|
||||
$`\delta_i = \text{AES-CTR}(\text{δ\_aes\_key}_{i},\ \text{δ\_iv}_{i},
|
||||
\ 0_{\kappa}\ |\ m)`$, where $m$ is the `message`.
|
||||
|
||||
- Otherwise (_i.e.,_ intermediary node):
|
||||
|
||||
$`\delta_i = \text{AES-CTR}(\text{δ\_aes\_key}_{i},\ \text{δ\_iv}_{i},\ \delta_{i+1})`$
|
||||
|
||||
Note that the length of $\delta$ is $|m| + \kappa$.
|
||||
|
||||
Given that the derived size of $\delta$ is $2029$ bytes, this allows
|
||||
`message` to be of length $2029-16 = 2013$ bytes. This means smaller
|
||||
messages may need to be padded up to $2013$ bytes (e.g., using PKCS#7
|
||||
padding).
|
||||
|
||||
e. **Construct Final Sphinx Packet**
|
||||
|
||||
- Initialize header
|
||||
|
||||
```pseudocode
|
||||
alpha = alpha_0 // 32 bytes
|
||||
beta = beta_0 // $(r(t+1)+1)\kappa$ bytes
|
||||
gamma = gamma_0 // 16 bytes
|
||||
```
|
||||
|
||||
As discussed earlier, for a maximum path length of $r = 5$, and combined
|
||||
length of address and delay $t = 3\kappa = 48$ bytes, the header size is
|
||||
just $384$ bytes.
|
||||
- Initialize payload
|
||||
|
||||
`delta = delta_0 // variable size, max 2029 bytes`
|
||||
|
||||
For a fixed Sphinx packet size of $2413$ bytes and given the header length
|
||||
of $384$ bytes, `delta` size is $2029$ bytes.
|
||||
|
||||
6. **Serialize the Sphinx Packet** using Protocol Buffers.
|
||||
|
||||
7. **Send the Serialized Packet** to the first mix node using the
|
||||
`"/mix/1.0.0"` protocol.
|
||||
|
||||
#### 5.2 Intermediary Mix Node
|
||||
|
||||
Let $`x_i \in \mathbb{Z}_q^*`$ be the intermediary node’s private key
|
||||
corresponding to the public key $y_i \in G^*$. It performs the following steps
|
||||
to relay a message:
|
||||
|
||||
1. **Receive and Deserialize** the Sphinx packet using Protocol Buffers.
|
||||
2. **Compute Shared Secret $s = \alpha^{x_{i}}$**.
|
||||
|
||||
3. **Check If Previously Seen**
|
||||
|
||||
a. Compute tag $H(s)$, where $H$ is the SHA-256 hash function.
|
||||
|
||||
b. If tag is in the previously seen list of tags, discard the message.
|
||||
|
||||
c. This list can be reset whenever the node rotates its private key
|
||||
|
||||
4. **Compute MAC**
|
||||
|
||||
a. Derive MAC key
|
||||
|
||||
$`\text{mac\_key} = KDF(\text{"mac\_key"}\ |\ s)`$
|
||||
|
||||
b. Check if $`\gamma = \text{HMAC-SHA-256}(\text{mac\_key},\ β)`$ . If not,
|
||||
discard the message.
|
||||
|
||||
c. Otherwise, store tag $H(s)$ in the list of seen message tags.
|
||||
|
||||
5. **Decrypt One Layer**
|
||||
|
||||
a. Derive the AES key, MAC key, and IV:
|
||||
|
||||
$`\text{β\_aes\_key} = KDF(\text{"aes\_key"}\ |\ s)`$
|
||||
|
||||
$`\text{β\_iv} = H(\text{"iv"}\ |\ s)`$ (truncated to 128 bits)
|
||||
|
||||
b. Compute
|
||||
$`B = \text{AES-CTR}(\text{β\_aes\_key},\ \text{β\_iv},\ \beta\ |\ 0_{(t+1)k})`$.
|
||||
|
||||
c. Uniquely parse prefix of $B$
|
||||
|
||||
If $B$ has a prefix of **$0_{((t+1)(r-L)+t+2)\kappa}$,** the current node is the
|
||||
exit node (refer exit node operations below).
|
||||
|
||||
Otherwise, it is an intermediary node and it performs the followings steps
|
||||
to relay the message.
|
||||
|
||||
d. **Extract Routing Information**
|
||||
|
||||
$`\text{next\_hop} = B_{[0\ldots(t\kappa-17)]}`$ (first $t\kappa-2$ bytes).
|
||||
|
||||
e. **Extract Delay**
|
||||
|
||||
$`\text{delay} = B_{[(t\kappa-16)\ldots(t\kappa-1)]}`$ (following $2$ bytes).
|
||||
|
||||
f. **Extract Gamma**
|
||||
|
||||
$`{\gamma}' = B_{[t\kappa\ldots(t\kappa+\kappa-1)]}`$ (following $\kappa$ bytes).
|
||||
|
||||
g. **Extract Beta**
|
||||
|
||||
$`\beta' = B_{[(t\kappa+\kappa)\ldots(r(t+1)+t+2)\kappa-1]}`$ (following
|
||||
$((t+1)r + 1)\kappa$ bytes).
|
||||
|
||||
h. **Compute Alpha**
|
||||
|
||||
- Compute blinding factor $b = H(α\ |\ s)$, where $H$ is the SHA-256 hash
|
||||
function.
|
||||
- Compute $α^′ = α^b$.
|
||||
|
||||
i. **Compute Delta**
|
||||
|
||||
- Derive the AES key and IV:
|
||||
$`\text{δ\_aes\_key} = KDF(\text{"δ\_aes\_key"}\ |\ s)`$
|
||||
$`\text{δ\_iv} = H(\text{"δ\_iv"}\ |\ s)$` (truncated to 128 bits)
|
||||
- Compute $`\delta' = \text{AES-CTR}(\text{δ\_aes\_key},\ \text{δ\_iv},\ \delta)`$
|
||||
|
||||
6. **Construct Final Sphinx Packet**
|
||||
|
||||
a. Initialize header
|
||||
|
||||
```pseudocode
|
||||
alpha = alpha' // 32 bytes
|
||||
beta = beta' // $((t+1)r + 1)\kappa$ bytes
|
||||
gamma = gamma' // 16 bytes
|
||||
```
|
||||
|
||||
b. Initialize payload
|
||||
|
||||
`delta = delta' // variable size, max 2029 bytes`
|
||||
|
||||
7. **Serialize the Sphinx Packet** using Protocol Buffers.
|
||||
8. **Introduce A Delay** of $`\text{delay}`$ milliseconds.
|
||||
9. **Send the Serialized Packet** to $`\text{next\_hop}`$ using the
|
||||
`"/mix/1.0.0"` protocol.
|
||||
|
||||
#### 5.3 Exit Node
|
||||
|
||||
1. **Perform _Steps i. to v. b._ Above**, similar to an intermediary node. If
|
||||
$B$ has a prefix of $0_{((t+1)(r-L)+t+2)\kappa}$ (in _step 5. c._ above), the
|
||||
current node is the exit node. It performs the following steps to
|
||||
disseminate the message via the respective libp2p protocol.
|
||||
|
||||
2. **Compute Delta**
|
||||
|
||||
- Derive the AES key and IV:
|
||||
|
||||
$`\text{δ\_aes\_key} = KDF(\text{"δ\_aes\_key"}\ |\ s)`$
|
||||
|
||||
$`\text{δ\_iv} = H(\text{"δ\_iv"}\ |\ s)`$ (truncated to 128 bits)
|
||||
|
||||
- Compute $`\delta' = \text{AES-CTR}(\text{δ\_aes\_key},\ \text{δ\_iv},\ \delta)`$.
|
||||
|
||||
3. **Extract Message**
|
||||
|
||||
$m = \delta'_{[\kappa\ldots]}$ (remove first $\kappa$ bytes).
|
||||
|
||||
4. **Remove Any Padding** from $m$ to obtain the `message` including any
|
||||
necessary spam protection data.
|
||||
|
||||
5. **Verify Spam Protection**
|
||||
|
||||
Verify the spam protection mechanism applied to the `message`.
|
||||
If the verification fails, discard the `message`.
|
||||
Refer to [Appendix A](#appendix-a-example-spam-protection-using-proof-of-work)
|
||||
for details on the current implementation using PoW.
|
||||
|
||||
6. **Deserialize the extracted message** using the respective libp2p protocol's
|
||||
definition.
|
||||
|
||||
7. **Disseminate the message** via the respective libp2p protocol (_e.g.,_
|
||||
GossipSub).
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
## References
|
||||
|
||||
### Normative
|
||||
|
||||
[Handler function](https://docs.libp2p.io/concepts/fundamentals/protocols/#handler-functions)
|
||||
[libp2p](https://libp2p.io)\
|
||||
[Sphinx](https://cypherpunks.ca/~iang/pubs/Sphinx_Oakland09.pdf)
|
||||
|
||||
### Informative
|
||||
|
||||
[PoW](https://bitcoin.org/bitcoin.pdf)\
|
||||
[Sphinx packet size](https://petsymposium.org/popets/2024/popets-2024-0050.pdf)
|
||||
|
||||
## Appendix A. Example Spam Protection using Proof of Work
|
||||
|
||||
The current implementation uses a Proof of Work mechanism for spam protection.
|
||||
This section details the specific steps for attaching and verifying the PoW.
|
||||
|
||||
### Structure
|
||||
|
||||
The sender appends the PoW to the serialized libp2p message, `libp2p_message`,
|
||||
in a structured format, making it easy to parse and verify by the exit node.
|
||||
The sender includes the PoW as follows:
|
||||
|
||||
`message = <libp2p_message_bytes | timestamp | nonce>`
|
||||
|
||||
where:
|
||||
|
||||
`<libp2p_message_bytes>`: Serialized libp2p message (variable length).
|
||||
|
||||
`<timestamp>`: The current Unix timestamp in seconds (4 bytes).
|
||||
|
||||
`<nonce>`: The nonce that satisfies the PoW difficulty criterion (4 bytes).
|
||||
|
||||
**Nonce Size:** The nonce size should be large enough to ensure a sufficiently large
|
||||
search space. It should be chosen so that the range of possible nonce values
|
||||
allows for the difficulty target to be met. However, larger nonce sizes can increase
|
||||
the time and computational effort required to find a valid nonce. We use
|
||||
a 4-byte nonce to ensure sufficiently large search space with reasonable
|
||||
computational effort.
|
||||
|
||||
**Difficulty Level:** The difficulty level is usually expressed as the number of
|
||||
leading zeros required in the hash output. It is chosen such that the
|
||||
computational effort required is significant but not prohibitive.
|
||||
We recommend a reasonable difficulty level that requires around
|
||||
16-18 leading zeros in the SHA-256 hash. This would roughly take
|
||||
0.65 to 2.62 seconds to compute in a low-grade CPU,
|
||||
capable of computing 100,000 hashes per second.
|
||||
|
||||
### Calculate Proof of Work (PoW)
|
||||
|
||||
The sender performs the following steps to compute the PoW challenge and response:
|
||||
|
||||
i. **Create Challenge**
|
||||
|
||||
Retrieves the current Unix timestamp, `timestamp`, in seconds (4 bytes).
|
||||
|
||||
ii. **Find A Valid Nonce**
|
||||
|
||||
- Initializes the `nonce` to a 4-byte value (initially zero).
|
||||
|
||||
- Increments the `nonce` until the SHA-256 hash of
|
||||
`<libp2p_message_bytes | timestamp | nonce>` has at least
|
||||
18 leading zeros.
|
||||
|
||||
- The final value of the `nonce` is considered valid.
|
||||
|
||||
### Attach the PoW to the libp2p Message
|
||||
|
||||
Append the 4-byte `timestamp` and the valid `nonce` to
|
||||
the `libp2p_message_bytes` to form the `message`.
|
||||
|
||||
`message = <libp2p_message_bytes | timestamp | nonce>`
|
||||
|
||||
### Verify PoW
|
||||
|
||||
i. **Extract Timestamp and Nonce**
|
||||
|
||||
Split `message` into 4-byte `nonce` (last 4 bytes), 4-byte `timestamp`
|
||||
(the 4 bytes before the nonce), and the serialized libp2p message
|
||||
to be published, `libp2p_message_bytes` (the remaining bytes).
|
||||
|
||||
ii. **Verify Timestamp**
|
||||
|
||||
- Check the `timestamp` is within the last 5 minutes.
|
||||
|
||||
- If the timestamp is outside the acceptable window, the exit node
|
||||
discards the message.
|
||||
|
||||
iii. **Verifiy Response**
|
||||
|
||||
- Compute the SHA-256 hash of the `message` and check if the hash
|
||||
meets the difficulty requirement, _i.e._, has at least 18 leading zeros.
|
||||
|
||||
- If the hash is not valid, the exit node discards the message. Otherwise,
|
||||
it follows the steps to publish the message.
|
||||
@@ -1,11 +1,10 @@
|
||||
---
|
||||
slug: 48
|
||||
title: 48/RLN-INTEREP-SPEC
|
||||
title: RLN-INTEREP-SPEC
|
||||
name: Interep as group management for RLN
|
||||
status: raw
|
||||
category:
|
||||
tags: rln
|
||||
editor: Aaryamann Challani <aaryamann@status.im>
|
||||
editor: Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
contributors:
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: RLN-STEALTH-COMMITMENTS
|
||||
name: RLN Stealth Commitment Usage
|
||||
category: Standards Track
|
||||
editor: Aaryamann Challani <aaryamann@status.im>
|
||||
editor: Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
contributors:
|
||||
- Jimmy Debe <jimmy@status.im>
|
||||
---
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
slug: 58
|
||||
title: 58/RLN-V2
|
||||
title: RLN-V2
|
||||
name: Rate Limit Nullifier V2
|
||||
status: raw
|
||||
editor: Rasul Ibragimov <curryrasul@gmail.com>
|
||||
69
vac/raw/url-scheme.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: STATUS-URL-SCHEME
|
||||
name: Status URL Scheme
|
||||
status: raw
|
||||
category: Standards Track
|
||||
tags:
|
||||
editor: Felicio Mununga <felicio@status.im>
|
||||
contributors:
|
||||
---
|
||||
|
||||
## Abstract
|
||||
|
||||
This document describes URL scheme for previewing and deep linking content as well as for triggering actions.
|
||||
|
||||
## Background / Rationale / Motivation
|
||||
|
||||
### Requirements
|
||||
|
||||
#### Related scope
|
||||
|
||||
##### Features
|
||||
|
||||
- Onboarding website
|
||||
- Link preview
|
||||
- Link sharing
|
||||
- Deep linking
|
||||
- Routing and navigation
|
||||
- Payment requests
|
||||
- Chat creation
|
||||
|
||||
## Wire Format Specification / Syntax
|
||||
|
||||
### Schemes
|
||||
|
||||
- Internal `status-app://`
|
||||
- External `https://` (i.e. univers/deep links)
|
||||
|
||||
### Paths
|
||||
|
||||
| Name | Url | Description |
|
||||
| ----- | ---- | ---- |
|
||||
| User profile | `/u/<encoded_data>#<user_chat_key>` | Preview/Open user profile |
|
||||
| | `/u#<user_chat_key>` | |
|
||||
| | `/u#<ens_name>` | |
|
||||
| Community | `/c/<encoded_data>#<community_chat_key>` | Preview/Open community |
|
||||
| | `/c#<community_chat_key>` | |
|
||||
| Community channel | `/cc/<encoded_data>#<community_chat_key >`| Preview/Open community channel |
|
||||
| | `/cc/<channel_uuid>#<community_chat_key>` | |
|
||||
|
||||
<!-- # Security/Privacy Considerations
|
||||
|
||||
A standard track RFC in `stable` status MUST feature this section.
|
||||
A standard track RFC in `raw` or `draft` status SHOULD feature this section.
|
||||
Informational RFCs (in any state) may feature this section.
|
||||
If there are none, this section MUST explicitly state that fact.
|
||||
This section MAY contain additional relevant information, e.g. an explanation as to why there are no security consideration for the respective document. -->
|
||||
|
||||
## Discussions
|
||||
|
||||
- See <https://github.com/status-im/specs/pull/159>
|
||||
- See <https://github.com/status-im/status-web/issues/327>
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
## References
|
||||
|
||||
- [STATUS-URL-DATA](./url-data.md)
|
||||
@@ -5,7 +5,7 @@ name: X3DH usage for Waku payload encryption
|
||||
status: draft
|
||||
category: Standards Track
|
||||
tags: waku-application
|
||||
editor: Aaryamann Challani <aaryamann@status.im>
|
||||
editor: Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
contributors:
|
||||
- Andrea Piana <andreap@status.im>
|
||||
- Pedro Pombeiro <pedro@status.im>
|
||||
|
||||
@@ -5,7 +5,7 @@ name: Session management for Waku X3DH
|
||||
status: draft
|
||||
category: Standards Track
|
||||
tags: waku-application
|
||||
editor: Aaryamann Challani <aaryamann@status.im>
|
||||
editor: Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
contributors:
|
||||
- Andrea Piana <andreap@status.im>
|
||||
- Pedro Pombeiro <pedro@status.im>
|
||||
|
||||
@@ -8,7 +8,7 @@ editor: Simon-Pierre Vivier <simvivier@status.im>
|
||||
contributors:
|
||||
- Dean Eigenmann <dean@status.im>
|
||||
- Oskar Thorén <oskarth@titanproxy.com>
|
||||
- Aaryamann Challani <aaryamann@status.im>
|
||||
- Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
- Sanaz Taheri <sanaz@status.im>
|
||||
- Hanno Cornelius <hanno@status.im>
|
||||
---
|
||||
|
||||
@@ -8,7 +8,7 @@ tags: waku/core-protocol
|
||||
editor: Hanno Cornelius <hanno@status.im>
|
||||
contributors:
|
||||
- Sanaz Taheri <sanaz@status.im>
|
||||
- Aaryamann Challani <aaryamann@status.im>
|
||||
- Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
- Lorenzo Delgado <lorenzo@status.im>
|
||||
- Abhimanyu Rawat <abhi@status.im>
|
||||
- Oskar Thorén <oskarth@titanproxy.com>
|
||||
|
||||
@@ -7,118 +7,153 @@ tags: waku-core
|
||||
editor: Alvaro Revuelta <alvaro@status.im>
|
||||
contributors:
|
||||
- Oskar Thorén <oskarth@titanproxy.com>
|
||||
- Aaryamann Challani <aaryamann@status.im>
|
||||
- Aaryamann Challani <p1ge0nh8er@proton.me>
|
||||
- Sanaz Taheri <sanaz@status.im>
|
||||
- Hanno Cornelius <hanno@status.im>
|
||||
---
|
||||
|
||||
The `17/WAKU2-RLN-RELAY` protocol is an extension of `11/WAKU2-RELAY` which additionally provides spam protection using [Rate Limiting Nullifiers (RLN)](../../../../vac/32/rln-v1.md).
|
||||
## Abstract
|
||||
This specification describes the `17/WAKU2-RLN-RELAY` protocol,
|
||||
which is an extension of [`11/WAKU2-RELAY`](../11/relay.md) to provide spam protection using [Rate Limiting Nullifiers (RLN)](../../../../vac/32/rln-v1.md).
|
||||
|
||||
The security objective is to contain spam activity in a GossipSub network by enforcing a global messaging rate to all the peers.
|
||||
Peers that violate the messaging rate are considered spammers and their message is considered spam.
|
||||
The security objective is to contain spam activity in the (64/WAKU-NETWORK)[] by enforcing a global messaging rate to all the peers.
|
||||
Peers that violate the messaging rate are considered spammers and
|
||||
their message is considered spam.
|
||||
Spammers are also financially punished and removed from the system.
|
||||
|
||||
|
||||
<!-- **Protocol identifier***: `/vac/waku/waku-rln-relay/2.0.0-alpha1` -->
|
||||
|
||||
## Motivation
|
||||
|
||||
In open and anonymous p2p messaging networks, one big problem is spam resistance.
|
||||
Existing solutions, such as Whisper’s proof of work are computationally expensive hence not suitable for resource-limited nodes.
|
||||
Other reputation-based approaches might not be desirable, due to issues around arbitrary exclusion and privacy.
|
||||
In open and anonymous p2p messaging networks,
|
||||
one big problem is spam resistance.
|
||||
Existing solutions, such as Whisper’s proof of work,
|
||||
are computationally expensive hence not suitable for resource-limited nodes.
|
||||
Other reputation-based approaches might not be desirable,
|
||||
due to issues around arbitrary exclusion and privacy.
|
||||
|
||||
We augment the [`11/WAKU2-RELAY`](../11/relay.md) protocol with a novel construct of [RLN](../../../../vac/32/rln-v1.md) to enable an efficient economic spam prevention mechanism that can be run in resource-constrained environments.
|
||||
|
||||
## Specification
|
||||
|
||||
## Flow
|
||||
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [2119](https://www.ietf.org/rfc/rfc2119.txt).
|
||||
|
||||
### Flow
|
||||
|
||||
The messaging rate is defined by the `period` which indicates how many messages can be sent in a given period.
|
||||
We define an `epoch` as $\lceil$ `unix_time` / `period` $\rceil$. For example, if `unix_time` is `1644810116` and we set `period` to `30`, then `epoch` is $\lceil$`(unix_time/period)`$\rceil$ `= 54827003`.
|
||||
Note that `epoch` refers to epoch in RLN and not Unix epoch. This means a message can only be sent every period, where period is up to the application.
|
||||
See see section [Recommended System Parameters](#recommended-system-parameters) for some recommended ways to set a sensible `period` value depending on the application.
|
||||
We define an `epoch` as $\lceil$ `unix_time` / `period` $\rceil$.
|
||||
For example, if `unix_time` is `1644810116` and we set `period` to `30`,
|
||||
then `epoch` is $\lceil$ `(unix_time/period)` $\rceil$ `= 54827003`.
|
||||
|
||||
> **NOTE:** The `epoch` refers to the epoch in RLN and not Unix epoch.
|
||||
This means a message can only be sent every period, where the `period` is up to the application.
|
||||
|
||||
See section [Recommended System Parameters](#recommended-system-parameters) for the RECOMMENDED method to set a sensible `period` value depending on the application.
|
||||
Peers subscribed to a spam-protected `pubsubTopic` are only allowed to send one message per `epoch`.
|
||||
The higher-level layers adopting `17/WAKU2-RLN-RELAY` MAY choose to enforce the messaging rate for `WakuMessages` with a specific `contentTopic` published on a `pubsubTopic`.
|
||||
The higher-level layers adopting `17/WAKU2-RLN-RELAY` MAY choose to enforce the messaging rate for `WakuMessages` with a specific `contentTopic` published on a `pubsubTopic`.
|
||||
|
||||
#### Setup and Registration
|
||||
|
||||
A `pubsubTopic` that is spam-protected requires subscribed peers to form a [RLN group](../../../../vac/32/rln-v1.md).
|
||||
- Peers MUST be registered to the RLN group to be able to publish messages.
|
||||
- Registration MAY be moderated through a smart contract deployed on the Ethereum blockchain.
|
||||
|
||||
### Setup and Registration
|
||||
Peers subscribed to a specific `pubsubTopic` form a [RLN group](../../../../vac/32/rln-v1.md).
|
||||
<!-- link to the RLN group definition in the RLN RFC -->
|
||||
Peers MUST be registered to the RLN group to be able to publish messages.
|
||||
Registration is moderated through a smart contract deployed on the Ethereum blockchain.
|
||||
Each peer has an [RLN key pair](../../../../vac/32/rln-v1.md) denoted by `sk` and `pk`.
|
||||
The secret key `sk` is secret data and MUST be persisted securely by the peer.
|
||||
The state of the membership contract contains the list of registered members' public identity keys i.e., `pk`s.
|
||||
For the registration, a peer creates a transaction that invokes the registration function of the contract via which registers its `pk` in the group.
|
||||
The transaction also transfers some amount of ether to the contract to be staked.
|
||||
- The secret key `sk` is secret data and MUST be persisted securely by the peer.
|
||||
- The state of the membership contract SHOULD contain a list of all registered members' public identity keys i.e.,
|
||||
`pk`s.
|
||||
|
||||
For registration, a peer MUST create a transaction to invoke the registration function on the contract,
|
||||
which registers its `pk` in the RLN group.
|
||||
- The transaction MUST transfer additional tokens to the contract to be staked.
|
||||
This amount is denoted by `staked_fund` and is a system parameter.
|
||||
The peer who has the secret key `sk` associated with a registered `pk` would be able to withdraw a portion `reward_portion` of the staked fund by providing valid proof. <!-- a secure way to prove the possession of a pk is yet under discussion, maybe via commit and reveal -->
|
||||
The peer who has the secret key `sk` associated with a registered `pk` would be able to withdraw a portion `reward_portion` of the staked fund by providing valid proof.
|
||||
|
||||
`reward_portion` is also a system parameter.
|
||||
|
||||
Note that `sk` is initially only known to its owning peer however, it may get exposed to other peers in case the owner attempts spamming the system i.e., sending more than one message per `epoch`.
|
||||
> **NOTE:** Initially `sk` is only known to its owning peer however,
|
||||
it may get exposed to other peers in case the owner attempts spamming the system i.e.,
|
||||
sending more than one message per `epoch`.
|
||||
|
||||
An overview of registration is illustrated in Figure 1.
|
||||
|
||||

|
||||
|
||||
#### Publishing
|
||||
|
||||
### Publishing
|
||||
|
||||
To publish at a given `epoch`, the publishing peer proceeds based on the regular [`11/WAKU2-RELAY`](../11/relay.md) protocol.
|
||||
However, to protect against spamming, each `WakuMessage` (which is wrapped inside the `data` field of a PubSub message) MUST carry a [`RateLimitProof`](##RateLimitProof) with the following fields.
|
||||
To publish at a given `epoch`,
|
||||
the publishing peer proceeds based on the regular [`11/WAKU2-RELAY`](../11/relay.md) protocol.
|
||||
However, to protect against spamming, each `WakuMessage`
|
||||
(which is wrapped inside the `data` field of a PubSub message)
|
||||
MUST carry a [`RateLimitProof`](##RateLimitProof) with the following fields.
|
||||
Section [Payload](#payloads) covers the details about the type and encoding of these fields.
|
||||
|
||||
The `merkle_root` contains the root of the Merkle tree.
|
||||
- The `merkle_root` contains the root of the Merkle tree.
|
||||
- The `epoch` represents the current epoch.
|
||||
- The `nullifier` is an internal nullifier acting as a fingerprint that allows specifying whether two messages are published by the same peer during the same `epoch`.
|
||||
- The `nullifier` is a deterministic value derived from `sk` and
|
||||
`epoch` therefore any two messages issued by the same peer
|
||||
(i.e., using the same `sk`) for the same `epoch` are guaranteed to have identical `nullifier`s.
|
||||
- The `share_x` and `share_y` can be seen as partial disclosure of peer's `sk` for the intended `epoch`.
|
||||
They are derived deterministically from peer's `sk` and
|
||||
current `epoch` using [Shamir secret sharing scheme](../../../../vac/32/rln-v1.md).
|
||||
|
||||
The `epoch` represents the current epoch.
|
||||
If a peer discloses more than one such pair (`share_x`, `share_y`) for the same `epoch`,
|
||||
it would allow full disclosure of its `sk` and
|
||||
hence get access to its staked fund in the membership contract.
|
||||
|
||||
The `nullifier` is an internal nullifier acting as a fingerprint that allows specifying whether two messages are published by the same peer during the same `epoch`.
|
||||
The `nullifier` is a deterministic value derived from `sk` and `epoch` therefore any two messages issued by the same peer (i.e., using the same `sk`) for the same `epoch` are guaranteed to have identical `nullifier`s.
|
||||
- The `proof` field is a zero-knowledge proof signifying that:
|
||||
|
||||
The `share_x` and `share_y` can be seen as partial disclosure of peer's `sk` for the intended `epoch`.
|
||||
They are derived deterministically from peer's `sk` and current `epoch` using [Shamir secret sharing scheme](../../../../vac/32/rln-v1.md).
|
||||
If a peer discloses more than one such pair (`share_x`, `share_y`) for the same `epoch`, it would allow full disclosure of its `sk` and hence get access to its staked fund in the membership contract.
|
||||
|
||||
|
||||
The `proof` field is a zero-knowledge proof signifying that:
|
||||
1. The message owner is the current member of the group i.e., her/his identity commitment key `pk` is part of the membership group Merkle tree with the root `merkle_root`.
|
||||
2. `share_x` and `share_y` are correctly computed.
|
||||
1. The message owner is the current member of the group i.e.,
|
||||
the peer's identity commitment key, `pk`,
|
||||
is part of the membership group Merkle tree with the root `merkle_root`.
|
||||
2. `share_x` and `share_y` are correctly computed.
|
||||
3. The `nullifier` is constructed correctly.
|
||||
For more details about the proof generation check [RLN](../../../../vac/32/rln-v1.md)
|
||||
The proof generation relies on the knowledge of two pieces of private information i.e., `sk` and `authPath`.
|
||||
The `authPath` is a subset of Merkle tree nodes by which a peer can prove the inclusion of its `pk` in the group. <!-- TODO refer to RLN RFC for authPath def -->
|
||||
The proof generation also requires a set of public inputs which are: the Merkle tree root `merkle_root`, the current `epoch`, and the message for which the proof is going to be generated.
|
||||
In `17/WAKU2-RLN-RELAY`, the message is the concatenation of `WakuMessage`'s `payload` filed and its `contentTopic` i.e., `payload||contentTopic`.
|
||||
The proof generation also requires a set of public inputs which are:
|
||||
the Merkle tree root `merkle_root`, the current `epoch`, and
|
||||
the message for which the proof is going to be generated.
|
||||
In `17/WAKU2-RLN-RELAY`, the message is the concatenation of `WakuMessage`'s `payload` filed and
|
||||
its `contentTopic` i.e., `payload||contentTopic`.
|
||||
|
||||
### Group Synchronization
|
||||
#### Group Synchronization
|
||||
|
||||
Proof generation relies on the knowledge of Merkle tree root `merkle_root` and `authPath` which both require access to the membership Merkle tree.
|
||||
Getting access to the Merkle tree can be done in various ways.
|
||||
One way is that all the peers construct the tree locally.
|
||||
This can be done by listening to the registration and deletion events emitted by the membership contract.
|
||||
Getting access to the Merkle tree can be done in various ways:
|
||||
|
||||
1. Peers construct the tree locally.
|
||||
This can be done by listening to the registration and
|
||||
deletion events emitted by the membership contract.
|
||||
Peers MUST update the local Merkle tree on a per-block basis.
|
||||
This is discussed further in the [Merkle Root Validation](#merkle-root-validation) section.
|
||||
|
||||
Another approach for synchronizing the state of slashed `pk`s is to disseminate such information through a p2p GossipSub network to which all peers are subscribed.
|
||||
This is in addition to sending the deletion transaction to the membership contract.
|
||||
2. For synchronizing the state of slashed `pk`s,
|
||||
disseminate such information through a `pubsubTopic` to which all peers are subscribed.
|
||||
A deletion transaction SHOULD occur on the membership contract.
|
||||
The benefit of an off-chain slashing is that it allows real-time removal of spammers as opposed to on-chain slashing in which peers get informed with a delay,
|
||||
where the delay is due to mining the slashing transaction.
|
||||
For the group synchronization, one important security consideration is that peers MUST make sure they always use the most recent Merkle tree root in their proof generation.
|
||||
|
||||
For the group synchronization,
|
||||
one important security consideration is that peers MUST make sure they always use the most recent Merkle tree root in their proof generation.
|
||||
The reason is that using an old root can allow inference about the index of the user's `pk` in the membership tree hence compromising user privacy and breaking message unlinkability.
|
||||
|
||||
### Routing
|
||||
#### Routing
|
||||
|
||||
Upon the receipt of a PubSub message via [`11/WAKU2-RELAY`](../11/relay.md) protocol, the routing peer parses the `data` field as a `WakuMessage` and gets access to the `RateLimitProof` field.
|
||||
Upon the receipt of a PubSub message via [`11/WAKU2-RELAY`](../11/relay.md) protocol,
|
||||
the routing peer parses the `data` field as a `WakuMessage` and gets access to the `RateLimitProof` field.
|
||||
The peer then validates the `RateLimitProof` as explained next.
|
||||
|
||||
#### Epoch Validation
|
||||
If the `epoch` attached to the message is more than `max_epoch_gap` apart from the routing peer's current `epoch` then the message is discarded and considered invalid.
|
||||
##### Epoch Validation
|
||||
If the `epoch` attached to the `WakuMessage` is more than `max_epoch_gap`,
|
||||
apart from the routing peer's current `epoch`,
|
||||
then the `WakuMessage` MUST be discarded and considered invalid.
|
||||
This is to prevent a newly registered peer from spamming the system by messaging for all the past epochs.
|
||||
`max_epoch_gap` is a system parameter for which we provide some recommendations in section [Recommended System Parameters](#recommended-system-parameters).
|
||||
|
||||
#### Merkle Root Validation
|
||||
##### Merkle Root Validation
|
||||
The routing peers MUST check whether the provided Merkle root in the `RateLimitProof` is valid.
|
||||
It can do so by maintaining a local set of valid Merkle roots, which consist of `acceptable_root_window_size` past roots.
|
||||
It can do so by maintaining a local set of valid Merkle roots,
|
||||
which consist of `acceptable_root_window_size` past roots.
|
||||
These roots refer to the final state of the Merkle tree after a whole block consisting of group changes is processed.
|
||||
The Merkle roots are updated on a per-block basis instead of a per-event basis.
|
||||
This is done because if Merkle roots are updated on a per-event basis, some peers could send messages with a root that refers to a Merkle tree state that might get invalidated while the message is still propagating in the network, due to many registrations happening during this time frame.
|
||||
@@ -130,33 +165,38 @@ This also allows peers which are not well connected to the network to be able to
|
||||
This network delay is related to the nature of asynchronous network conditions, which means that peers see membership changes asynchronously, and therefore may have differing local Merkle trees.
|
||||
See [Recommended System Parameters](#recommended-system-parameters) on choosing an appropriate `acceptable_root_window_size`.
|
||||
|
||||
#### Proof Verification
|
||||
##### Proof Verification
|
||||
The routing peers MUST check whether the zero-knowledge proof `proof` is valid.
|
||||
It does so by running the zk verification algorithm as explained in [RLN](../../../../vac/32/rln-v1.md).
|
||||
If `proof` is invalid then the message is discarded.
|
||||
If `proof` is invalid then the message MUST be discarded.
|
||||
|
||||
#### Spam detection
|
||||
To enable local spam detection and slashing, routing peers MUST record the `nullifier`, `share_x`, and `share_y` of incoming messages which are not discarded i.e., not found spam or with invalid proof or epoch.
|
||||
##### Spam detection
|
||||
To enable local spam detection and slashing,
|
||||
routing peers MUST record the `nullifier`, `share_x`, and `share_y`
|
||||
of incoming messages which are not discarded i.e., not found spam or with invalid proof or epoch.
|
||||
To spot spam messages, the peer checks whether a message with an identical `nullifier` has already been relayed.
|
||||
1. If such a message exists and its `share_x` and `share_y` components are different from the incoming message, then slashing takes place.
|
||||
That is, the peer uses the `share_x` and `share_y` of the new message and the `share'_x` and `share'_y` of the old record to reconstruct the `sk` of the message owner.
|
||||
The `sk` then can be used to delete the spammer from the group and withdraw a portion `reward_portion` of its staked fund.
|
||||
2. If the `share_x` and `share_y` fields of the previously relayed message are identical to the incoming message, then the message is a duplicate and shall be discarded.
|
||||
3. If none is found, then the message gets relayed.
|
||||
|
||||
1. If such a message exists and its `share_x` and `share_y`
|
||||
components are different from the incoming message, then slashing takes place.
|
||||
That is, the peer uses the `share_x` and `share_y`
|
||||
of the new message and the `share'_x` and `share'_y`
|
||||
of the old record to reconstruct the `sk` of the message owner.
|
||||
The `sk` then MAY be used to delete the spammer from the group and
|
||||
withdraw a portion `reward_portion` of its staked funds.
|
||||
3. If the `share_x` and `share_y` fields of the previously relayed message are identical to the incoming message,
|
||||
then the message is a duplicate and MUST be discarded.
|
||||
4. If none is found, then the message gets relayed.
|
||||
|
||||
An overview of the routing procedure and slashing is provided in Figure 2.
|
||||
|
||||
|
||||
<!-- TODO: may consider [validator functions](https://github.com/libp2p/specs/tree/master/pubsub#topic-validation) or [extended validators](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#extended-validators) for the spam detection -->
|
||||
|
||||

|
||||
|
||||
-------
|
||||
|
||||
## Payloads
|
||||
### Payloads
|
||||
|
||||
Payloads are protobuf messages implemented using [protocol buffers v3](https://developers.google.com/protocol-buffers/).
|
||||
Nodes MAY extend the [14/WAKU2-MESSAGE](../14/message.md) with a `rate_limit_proof` field to indicate that their message is not spam.
|
||||
Nodes MAY extend the [14/WAKU2-MESSAGE](../14/message.md) with a `rate_limit_proof` field to indicate that their message is not spam.
|
||||
|
||||
```diff
|
||||
|
||||
@@ -177,51 +217,52 @@ message WakuMessage {
|
||||
optional uint32 version = 3;
|
||||
optional sint64 timestamp = 10;
|
||||
optional bool ephemeral = 31;
|
||||
+ optional bytes rate_limit_proof = 21;
|
||||
RateLimitProof rate_limit_proof = 21;
|
||||
}
|
||||
|
||||
```
|
||||
### WakuMessage
|
||||
#### WakuMessage
|
||||
|
||||
`rate_limit_proof` holds the information required to prove that the message owner has not exceeded the message rate limit.
|
||||
|
||||
### RateLimitProof
|
||||
#### RateLimitProof
|
||||
Below is the description of the fields of `RateLimitProof` and their types.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| ----: | ----------- | ----------- |
|
||||
| `proof` | array of 256 bytes | the zkSNARK proof as explained in the [Publishing process](##Publishing) |
|
||||
| `proof` | array of 256 bytes uncompressed or 128 bytes compressed | the zkSNARK proof as explained in the [Publishing process](##Publishing) |
|
||||
| `merkle_root` | array of 32 bytes in little-endian order | the root of membership group Merkle tree at the time of publishing the message |
|
||||
| `share_x` and `share_y`| array of 32 bytes each | Shamir secret shares of the user's secret identity key `sk` . `share_x` is the Poseidon hash of the `WakuMessage`'s `payload` concatenated with its `contentTopic` . `share_y` is calculated using [Shamir secret sharing scheme](../../../../vac/32/rln-v1.md) | <!-- todo specify the poseidon hash setting -->
|
||||
| `share_x` and `share_y`| array of 32 bytes each | Shamir secret shares of the user's secret identity key `sk` . `share_x` is the Poseidon hash of the `WakuMessage`'s `payload` concatenated with its `contentTopic` . `share_y` is calculated using [Shamir secret sharing scheme](../../../../vac/32/rln-v1.md) |
|
||||
| `nullifier` | array of 32 bytes | internal nullifier derived from `epoch` and peer's `sk` as explained in [RLN construct](../../../../vac/32/rln-v1.md)|
|
||||
|
||||
|
||||
## Recommended System Parameters
|
||||
The system parameters are summarized in the following table, and the recommended values for a subset of them are presented next.
|
||||
### Recommended System Parameters
|
||||
The system parameters are summarized in the following table, and the RECOMMENDED values for a subset of them are presented next.
|
||||
|
||||
| Parameter | Description |
|
||||
| ----: |----------- |
|
||||
| `period` | the length of `epoch` in seconds |
|
||||
| `staked_fund` | the amount of wei to be staked by peers at the registration |
|
||||
| `staked_fund` | the amount of funds to be staked by peers at the registration |
|
||||
| `reward_portion` | the percentage of `staked_fund` to be rewarded to the slashers |
|
||||
| `max_epoch_gap` | the maximum allowed gap between the `epoch` of a routing peer and the incoming message |
|
||||
| `acceptable_root_window_size` | The maximum number of past Merkle roots to store |
|
||||
|
||||
### Epoch Length
|
||||
#### Epoch Length
|
||||
A sensible value for the `period` depends on the application for which the spam protection is going to be used.
|
||||
For example, while the `period` of `1` second i.e., messaging rate of `1` per second, might be acceptable for a chat application, might be too low for communication among Ethereum network validators.
|
||||
For example, while the `period` of `1` second i.e.,
|
||||
messaging rate of `1` per second, might be acceptable for a chat application,
|
||||
might be too low for communication among Ethereum network validators.
|
||||
One should look at the desired throughput of the application to decide on a proper `period` value.
|
||||
In the proof of concept implementation of `17/WAKU2-RLN-RELAY` protocol which is available in [nim-waku](https://github.com/status-im/nim-waku), the `period` is set to `1` second.
|
||||
Nevertheless, this value is also subject to change depending on user experience.
|
||||
|
||||
### Maximum Epoch Gap
|
||||
We discussed in the [Routing](#routing) section that the gap between the epoch observed by the routing peer and the one attached to the incoming message should not exceed a threshold denoted by `max_epoch_gap` .
|
||||
The value of `max_epoch_gap` can be measured based on the following factors.
|
||||
#### Maximum Epoch Gap
|
||||
We discussed in the [Routing](#routing) section that the gap between the epoch observed by the routing peer and
|
||||
the one attached to the incoming message should not exceed a threshold denoted by `max_epoch_gap`.
|
||||
The value of `max_epoch_gap` can be measured based on the following factors.
|
||||
|
||||
- Network transmission delay `Network_Delay`: the maximum time that it takes for a message to be fully disseminated in the GossipSub network.
|
||||
- Clock asynchrony `Clock_Asynchrony`: The maximum difference between the Unix epoch clocks perceived by network peers which can be due to clock drifts.
|
||||
|
||||
With a reasonable approximation of the preceding values, one can set `max_epoch_gap` as
|
||||
`max_epoch_gap` $= \lceil \frac{\text{Network Delay} + \text{Clock Asynchrony}}{\text{Epoch Length}}\rceil$ where `period` is the length of the `epoch` in seconds.
|
||||
With a reasonable approximation of the preceding values, one can set `max_epoch_gap` as
|
||||
`max_epoch_gap` $= \lceil \frac{\text{Network Delay} + \text{Clock Asynchrony}}{\text{Epoch Length}}\rceil$ where `period` is the length of the `epoch` in seconds.
|
||||
`Network_Delay` and `Clock_Asynchrony` MUST have the same resolution as `period` .
|
||||
By this formulation, `max_epoch_gap` indeed measures the maximum number of `epoch`s that can elapse since a message gets routed from its origin to all the other peers in the network.
|
||||
|
||||
@@ -236,14 +277,16 @@ By this formulation, `acceptable_root_window_size` will provide a lower bound of
|
||||
The `acceptable_root_window_size` should indicate how many blocks may have been mined during the time it takes for a peer to receive a message.
|
||||
This formula represents a lower bound of the number of acceptable roots.
|
||||
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
## References
|
||||
|
||||
1. [RLN documentation](https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view)
|
||||
2. [Public inputs to the RLN circuit](https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Public-Inputs)
|
||||
3. [Shamir secret sharing scheme used in RLN](https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Linear-Equation-amp-SSS)
|
||||
4. [RLN internal nullifier](https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Nullifiers)
|
||||
1. [`11/WAKU2-RELAY`](../11/relay.md)
|
||||
2. [RLN](../../../../vac/32/rln-v1.md)
|
||||
3. [14/WAKU2-MESSAGE](../14/message.md)
|
||||
4. [RLN documentation](https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view)
|
||||
5. [Public inputs to the RLN circuit](https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Public-Inputs)
|
||||
6. [Shamir secret sharing scheme used in RLN](https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Linear-Equation-amp-SSS)
|
||||
7. [RLN internal nullifier](https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Nullifiers)
|
||||
|
||||
315
waku/standards/core/64/network.md
Normal file
@@ -0,0 +1,315 @@
|
||||
---
|
||||
slug: 64
|
||||
title: 64/WAKU2-NETWORK
|
||||
name: Waku v2 Network
|
||||
status: draft
|
||||
category: Best Current Practice
|
||||
tags: waku/application
|
||||
editor: Hanno Cornelius <hanno@status.im>
|
||||
contributors:
|
||||
---
|
||||
|
||||
## Abstract
|
||||
|
||||
This specification describes an opinionated deployment of [10/WAKU2](../10/waku2.md) protocols to form a coherent and
|
||||
shared decentralized messaging network that is open-access,
|
||||
useful for generalized messaging, privacy-preserving, scalable and
|
||||
accessible even to resource-restricted devices.
|
||||
We'll refer to this opinionated deployment simply as
|
||||
_the public Waku Network_, _the Waku Network_ or, if the context is clear, _the network_
|
||||
in the rest of this document.
|
||||
All The Waku Network configuration parameters are listed [here](https://github.com/waku-org/nwaku/blob/8bfad3ab453f96ac545c7cb0af06d0c0f34d1356/waku/factory/networks_config.nim#L31).
|
||||
|
||||
## Theory / Semantics
|
||||
|
||||
### Routing protocol
|
||||
|
||||
The Waku Network is built on the [17/WAKU2-RLN-RELAY](../17/rln-relay.md) routing protocol,
|
||||
which in turn is an extension of [11/WAKU2-RELAY](../11/relay.md) with spam protection measures.
|
||||
|
||||
### Network shards
|
||||
|
||||
Traffic in the Waku Network is sharded into eight [17/WAKU2-RLN-RELAY](../17/rln-relay.md) pubsub topics.
|
||||
Each pubsub topic is named according to the static shard naming format
|
||||
defined in [WAKU2-RELAY-SHARDING](https://github.com/waku-org/specs/blob/master/standards/core/relay-sharding.md)
|
||||
with:
|
||||
* `<cluster_id>` set to `1`
|
||||
* `<shard_number>` occupying the range `0` to `7`.
|
||||
In other words, the Waku Network is a [17/WAKU2-RLN-RELAY](../17/rln-relay.md) network
|
||||
routed on the combination of the eight pubsub topics:
|
||||
```
|
||||
/waku/2/rs/1/0
|
||||
/waku/2/rs/1/1
|
||||
...
|
||||
/waku/2/rs/1/7
|
||||
```
|
||||
|
||||
A node MUST use [WAKU-METADATA](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) protocol to identify the `<cluster_id>` that every
|
||||
inbound/outbound peer that attempts to connect supports. In any of the following cases, the node MUST trigger a disconnection:
|
||||
* [WAKU-METADATA](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) dial fails.
|
||||
* [WAKU-METADATA](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) reports an empty `<cluster_id>`.
|
||||
* [WAKU-METADATA](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) reports a `<cluster_id>` different than `1`.
|
||||
|
||||
## Roles
|
||||
|
||||
There are two distinct roles evident in the network, those of:
|
||||
1) nodes, and
|
||||
2) applications.
|
||||
|
||||
### Nodes
|
||||
|
||||
Nodes are the individual software units
|
||||
using [10/WAKU2](../10/waku2.md) protocols to form a p2p messaging network.
|
||||
Nodes, in turn, can participate in a shard as full relayers, i.e. _relay nodes_,
|
||||
or by running a combination of protocols suitable for resource-restricted environments,
|
||||
i.e. _non-relay nodes_.
|
||||
Nodes can also provide various services to the network,
|
||||
such as storing historical messages or protecting the network against spam.
|
||||
See the section on [default services](#default-services) for more.
|
||||
|
||||
#### Relay nodes
|
||||
|
||||
Relay nodes MUST follow [17/WAKU2-RLN-RELAY](../17/rln-relay.md)
|
||||
to route messages to other nodes in the network
|
||||
for any of the pubsub topics [defined as the Waku Network shards](#network-shards).
|
||||
Relay nodes MAY choose to subscribe to any of these shards,
|
||||
but MUST be subscribed to at least one defined shard.
|
||||
Each relay node SHOULD be subscribed to as many shards as it has resources to support.
|
||||
If a relay node supports an encapsulating application,
|
||||
it SHOULD be subscribed to all the shards servicing that application.
|
||||
If resource restrictions prevent a relay node from servicing all shards used by the encapsulating application,
|
||||
it MAY choose to support some shards as a non-relay node.
|
||||
|
||||
#### Bootstrapping and discovery
|
||||
|
||||
Nodes MAY use any method to bootstrap connection to the network,
|
||||
but it is RECOMMENDED that each node retrieves a list of bootstrap peers to connect to using [EIP-1459 DNS-based discovery](https://eips.ethereum.org/EIPS/eip-1459).
|
||||
Relay nodes SHOULD use [33/WAKU2-DISCV5](../33/discv5.md) to continually discover other peers in the network.
|
||||
Each relay node MUST encode its supported shards into its discoverable ENR,
|
||||
as described in [WAKU2-RELAY-SHARDING](https://github.com/waku-org/specs/blob/master/standards/core/relay-sharding.md/#discovery).
|
||||
The ENR MUST be updated if the set of supported shards change.
|
||||
A node MAY choose to ignore discovered peers that do not support any of the shards in its own subscribed set.
|
||||
|
||||
#### Transports
|
||||
|
||||
Relay nodes MUST follow [10/WAKU2](../10/waku2.md) specifications with regards to supporting different transports.
|
||||
If TCP transport is available, each relay node MUST support it as transport for both dialing and listening.
|
||||
In addition, a relay node SHOULD support secure websockets for bidirectional communication streams,
|
||||
for example to allow connections from and to web browser-based clients.
|
||||
A relay node MAY support unsecure websockets if required by the application or running environment.
|
||||
|
||||
#### Default services
|
||||
|
||||
For each supported shard,
|
||||
each relay node SHOULD enable and support the following protocols as a service node:
|
||||
1. [12/WAKU2-FILTER](../12/filter.md) to allow resource-restricted peers to subscribe to messages matching a specific content filter.
|
||||
2. [13/WAKU2-STORE](../13/store.md) to allow other peers to request historical messages from this node.
|
||||
3. [19/WAKU2-LIGHTPUSH](../19/lightpush.md) to allow resource-restricted peers to request publishing a message to the network on their behalf.
|
||||
4. [WAKU2-PEER-EXCHANGE](https://github.com/waku-org/specs/blob/master/standards/core/peer-exchange.md) to allow resource-restricted peers to discover more peers in a resource efficient way.
|
||||
|
||||
#### Store service nodes
|
||||
|
||||
Each relay node SHOULD support [13/WAKU2-STORE](../13/store.md) as a store service node,
|
||||
for each supported shard.
|
||||
The store SHOULD be configured to retain at least `12` hours of messages per supported shard.
|
||||
Store service nodes SHOULD only store messages with a valid [`rate_limit_proof`](#message-attributes) attribute.
|
||||
|
||||
#### Non-relay nodes
|
||||
|
||||
Nodes MAY opt out of relay functionality on any network shard
|
||||
and instead request services from relay nodes as clients
|
||||
using any of the defined service protocols:
|
||||
1. [12/WAKU2-FILTER](../12/filter.md) to subscribe to messages matching a specific content filter.
|
||||
2. [13/WAKU2-STORE](../13/store.md) to request historical messages matching a specific content filter.
|
||||
3. [19/WAKU2-LIGHTPUSH](../19/lightpush.md) to request publishing a message to the network.
|
||||
4. [WAKU2-PEER-EXCHANGE](https://github.com/waku-org/specs/blob/master/standards/core/peer-exchange.md) to discover more peers in a resource efficient way.
|
||||
|
||||
#### Store client nodes
|
||||
|
||||
Nodes MAY request historical messages from [13/WAKU2-STORE](../13/store.md) service nodes as store clients.
|
||||
A store client SHOULD discard any messages retrieved from a store service node that do not contain a valid [`rate_limit_proof`](#message-attributes) attribute.
|
||||
The client MAY consider service nodes returning messages without a valid [`rate_limit_proof`](#message-attributes) attribute as untrustworthy.
|
||||
The mechanism by which this may happen is currently underdefined.
|
||||
|
||||
### Applications
|
||||
|
||||
Applications are the higher-layer projects or platforms that make use of the generalized messaging capability of the network.
|
||||
In other words, an application defines a payload used in the various [10/WAKU2](../10/waku2.md) protocols.
|
||||
Any participant in an application SHOULD make use of an underlying node in order to communicate on the network.
|
||||
Applications SHOULD make use of an [autosharding](#autosharding) API
|
||||
to allow the underlying node to automatically select the target shard on the Waku Network.
|
||||
See the section on [autosharding](#autosharding) for more.
|
||||
|
||||
## RLN rate-limiting
|
||||
|
||||
The [17/WAKU2-RLN-RELAY](../17/rln-relay.md) protocol uses [RLN-V2](https://github.com/vacp2p/rfc-index/blob/a5b24ac0a27da361312260f9da372a0e6e812212/vac/raw/rln-v2.md) proofs
|
||||
to ensure that a pre-agreed rate limit of `x` messages every `y` seconds is not exceeded by any publisher.
|
||||
While the network is under capacity,
|
||||
individual relayers MAY choose to freely route messages without RLN proofs
|
||||
up to a discretionary bandwidth limit,
|
||||
after which messages without proofs MUST be discarded by relay nodes.
|
||||
This bandwidth limit SHOULD be enforced using a [bandwidth validation mechanism](#free-bandwidth-exceeded) separate from a RLN rate-limiting.
|
||||
This implies that quality of service and reliability is significantly lower for messages without proofs
|
||||
and at times of high network utilization these messages may not be relayed at all.
|
||||
|
||||
### RLN Parameters
|
||||
|
||||
The Waku Network uses the following RLN parameters:
|
||||
|
||||
* `rlnRelayUserMessageLimit=100`: Amount of messages that a membership is allowed to publish per epoch. Configurable between `0` and `MAX_MESSAGE_LIMIT`.
|
||||
* `rlnEpochSizeSec=600`: Size of the epoch in seconds.
|
||||
* `rlnRelayChainId=11155111`: Network in which the RLN contract is deployed, aka Sepolia.
|
||||
* `rlnRelayEthContractAddress=0xCB33Aa5B38d79E3D9Fa8B10afF38AA201399a7e3`: Network address where RLN memberships are stored.
|
||||
* `staked_fund=0`: In other words, the Waku Network does not use RLN staking. Registering a membership just requires to pay gas.
|
||||
* `MAX_MESSAGE_LIMIT=100`: Maximum amount of messages allowed per epoch for any membership. Enforced in the contract.
|
||||
* `max_epoch_gap=20`: Maximum allowed gap in seconds into the past or future compared to the validator's clock.
|
||||
|
||||
Nodes MUST _reject_ messages not respecting any of these parameters.
|
||||
Nodes SHOULD use Network Time Protocol (NTP) to synchronize their own clocks,
|
||||
thereby ensuring valid timestamps for proof generation and validation.
|
||||
Publishers to the Waku Network SHOULD register an RLN membership.
|
||||
|
||||
### RLN Proofs
|
||||
|
||||
Each RLN member MUST generate and attach an RLN proof to every published message
|
||||
as described in [17/WAKU2-RLN-RELAY](../17/rln-relay.md/#publishing) and [RLN-V2](https://github.com/vacp2p/rfc-index/blob/a5b24ac0a27da361312260f9da372a0e6e812212/vac/raw/rln-v2.md).
|
||||
Slashing is not implemented for the Waku Network.
|
||||
Instead, validators will penalise peers forwarding messages exceeding the rate limit
|
||||
as specified for [the rate-limiting validation mechanism](#rate-limit-exceeded).
|
||||
This incentivizes all relay nodes to validate RLN proofs
|
||||
and reject messages violating rate limits
|
||||
in order to continue participating in the network.
|
||||
|
||||
## Network traffic
|
||||
|
||||
All payload on the Waku Network MUST be encapsulated in a [14/WAKU2-MESSAGE](../14/message.md)
|
||||
with rate limit proof extensions defined for [17/WAKU2-RLN-RELAY](../17/rln-relay.md/#payloads).
|
||||
Each message on the Waku Network SHOULD be validated by each relayer,
|
||||
according to the rules discussed under [message validation](#message-validation).
|
||||
|
||||
### Message Attributes
|
||||
|
||||
- The mandatory `payload` attribute MUST contain the message data payload as crafted by the application.
|
||||
- The mandatory `content_topic` attribute MUST specify a string identifier that can be used for content-based filtering.
|
||||
This is also crafted by the application.
|
||||
See [Autosharding](#autosharding) for more on the content topic format.
|
||||
- The optional `meta` attribute MAY be omitted.
|
||||
If present, will form part of the message uniqueness vector described in [14/WAKU2-MESSAGE](../14/message.md).
|
||||
- The optional `version` attribute SHOULD be set to `0`. It MUST be interpreted as `0` if not present.
|
||||
- The mandatory `timestamp` attribute MUST contain the Unix epoch time at which the message was generated by the application.
|
||||
The value MUST be in nanoseconds.
|
||||
It MAY contain a fudge factor of up to 1 seconds in either direction to improve resistance to timing attacks.
|
||||
- The optional `ephemeral` attribute MUST be set to `true` if the message should not be persisted by the Waku Network.
|
||||
- The optional `rate_limit_proof` attribute SHOULD be populated with the RLN proof as set out in [RLN Proofs](#rln-proofs).
|
||||
Messages with this field unpopulated MAY be discarded from the network by relayers.
|
||||
This field MUST be populated if the message should be persisted by the Waku Network.
|
||||
|
||||
### Message Size
|
||||
|
||||
Any [14/WAKU2-MESSAGE](../14/message.md) published to the network MUST NOT exceed an absolute maximum size of `150` kilobytes.
|
||||
This limit applies to the entire message after protobuf serialization, including attributes.
|
||||
It is RECOMMENDED not to exceed an average size of `4` kilobytes for [14/WAKU2-MESSAGE](../14/message.md) published to the network.
|
||||
|
||||
### Message Validation
|
||||
|
||||
Relay nodes MUST apply [gossipsub v1.1 validation](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#extended-validators) to each relayed message and
|
||||
SHOULD apply all of the rules set out in the section below to determine the validity of a message.
|
||||
Validation has one of three outcomes,
|
||||
repeated here from the [gossipsub specification](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#extended-validators) for ease of reference:
|
||||
1. Accept - the message is considered valid and it MUST be delivered and forwarded to the network.
|
||||
2. Reject - the message is considered invalid, MUST be rejected and SHOULD trigger a gossipsub scoring penalty against the transmitting peer.
|
||||
3. Ignore - the message SHOULD NOT be delivered and forwarded to the network, but this MUST NOT trigger a gossipsub scoring penalty against the transmitting peer.
|
||||
|
||||
The following validation rules are defined:
|
||||
|
||||
#### Decoding failure
|
||||
|
||||
If a message fails to decode as a valid [14/WAKU2-MESSAGE](../14/message.md),
|
||||
the relay node MUST _reject_ the message.
|
||||
This SHOULD trigger a penalty against the transmitting peer.
|
||||
|
||||
#### Invalid timestamp
|
||||
|
||||
If a message has a timestamp deviating by more than `20` seconds
|
||||
either into the past or the future
|
||||
when compared to the relay node's internal clock,
|
||||
the relay node MUST _reject_ the message.
|
||||
This allows for some deviation between internal clocks,
|
||||
network routing latency and
|
||||
an optional [fudge factor when timestamping new messages](#message-attributes).
|
||||
|
||||
#### Free bandwidth exceeded
|
||||
|
||||
If a message contains no RLN proof
|
||||
and the current bandwidth utilization on the shard the message was published to
|
||||
equals or exceeds `1` Mbps,
|
||||
the relay node SHOULD _ignore_ the message.
|
||||
|
||||
#### Invalid RLN epoch
|
||||
|
||||
If a message contains an RLN proof
|
||||
and the `epoch` attached to the proof deviates by more than `max_epoch_gap` seconds
|
||||
from the relay node's own `epoch`,
|
||||
the relay node MUST _reject_ the message.
|
||||
`max_epoch_gap` is [set to `20` seconds](#rln-parameters) for the Waku Network.
|
||||
|
||||
#### Invalid RLN proof
|
||||
|
||||
If a message contains an RLN proof
|
||||
and the zero-knowledge proof is invalid
|
||||
according to the verification process described in [RLN-V2](https://github.com/vacp2p/rfc-index/blob/a5b24ac0a27da361312260f9da372a0e6e812212/vac/raw/rln-v2.md),
|
||||
the relay node MUST _ignore_ the message.
|
||||
|
||||
#### Rate limit exceeded
|
||||
|
||||
If a message contains an RLN proof
|
||||
and the relay node detects double signaling
|
||||
according to the verification process described in [RLN-V2](https://github.com/vacp2p/rfc-index/blob/a5b24ac0a27da361312260f9da372a0e6e812212/vac/raw/rln-v2.md),
|
||||
the relay node MUST _reject_ the message
|
||||
for violating the agreed rate limit of `rlnRelayUserMessageLimit` messages every `rlnEpochSizeSec` second.
|
||||
This SHOULD trigger a penalty against the transmitting peer.
|
||||
|
||||
## Autosharding
|
||||
|
||||
Nodes in the Waku Network SHOULD allow encapsulating applications to use autosharding,
|
||||
as defined in [WAKU2-RELAY-SHARDING](https://github.com/waku-org/specs/blob/master/standards/core/relay-sharding.md/#automatic-sharding)
|
||||
by automatically determining the appropriate pubsub topic
|
||||
from the list [of defined Waku Network shards](#network-shards).
|
||||
This allows the application to omit the target pubsub topic
|
||||
when invoking any Waku protocol function.
|
||||
Applications using autosharding MUST use content topics in the format
|
||||
defined in [WAKU2-RELAY-SHARDING](https://github.com/waku-org/specs/blob/master/standards/core/relay-sharding.md/#content-topics-format-for-autosharding)
|
||||
and SHOULD use the short length format:
|
||||
|
||||
```
|
||||
/{application-name}/{version-of-the-application}/{content-topic-name}/{encoding}
|
||||
```
|
||||
|
||||
When an encapsulating application makes use of autosharding
|
||||
the underlying node MUST determine the target pubsub topic(s)
|
||||
from the content topics provided by the application
|
||||
using the hashing mechanism defined in [WAKU2-RELAY-SHARDING](https://github.com/waku-org/specs/blob/master/standards/core/relay-sharding.md/#automatic-sharding).
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
## References
|
||||
|
||||
* [10/WAKU2](../10/waku2.md)
|
||||
* [17/WAKU2-RLN-RELAY](../17/rln-relay.md)
|
||||
* [11/WAKU2-RELAY](../11/relay.md)
|
||||
* [WAKU2-RELAY-SHARDING](../../core/relay-sharding.md)
|
||||
* [WAKU-METADATA](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md)
|
||||
* [EIP-1459 DNS-based discovery](https://eips.ethereum.org/EIPS/eip-1459)
|
||||
* [33/WAKU2-DISCV5](../33/discv5.md)
|
||||
* [12/WAKU2-FILTER](../12/filter.md)
|
||||
* [13/WAKU2-STORE](../13/store.md)
|
||||
* [19/WAKU2-LIGHTPUSH](../19/lightpush.md)
|
||||
* [34/WAKU2-PEER-EXCHANGE](../../core/peer-exchange.md)
|
||||
* [32/RLN-V1](../../../../vac/32/rln-v1.md)
|
||||
* [RLN-V2](https://github.com/vacp2p/rfc-index/blob/a5b24ac0a27da361312260f9da372a0e6e812212/vac/raw/rln-v2.md)
|
||||
* [14/WAKU2-MESSAGE](../14/message.md)
|
||||
* [gossipsub v1.1 validation](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#extended-validators)
|
||||
* [WAKU2-RELAY-SHARDING](https://github.com/waku-org/specs/blob/master/standards/core/relay-sharding.md/)
|
||||
* [The Waku Network Config](https://github.com/waku-org/nwaku/blob/master/waku/factory/networks_config.nim#L31)
|
||||