Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Refactoring of private message delivery section of yellow paper #4628

Merged
merged 10 commits into from
Feb 18, 2024
2 changes: 1 addition & 1 deletion yellow-paper/docs/addresses-and-keys/address.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ public_keys_hash = pedersen([
], GENERATOR__PUBLIC_KEYS)
```

This recommended hash format is compatible with the [encryption precompiles](./precompiles.md#encryption-and-tagging-precompiles) initially defined in the protocol and advertised in the canonical [registry](../private-message-delivery/registry.md) for private message delivery. An address that chooses to use a different format for its keys will not be compatible with apps that rely on the registry for note encryption. Nevertheless, new precompiles introduced in future versions of the protocol could use different public keys formats.
This recommended hash format is compatible with the [encryption precompiles](./precompiles.md#encryption-and-tagging-precompiles) initially defined in the protocol and advertised in the canonical [registry](../pre-compiled-contracts/registry.md) for private message delivery. An address that chooses to use a different format for its keys will not be compatible with apps that rely on the registry for note encryption. Nevertheless, new precompiles introduced in future versions of the protocol could use different public keys formats.

<!-- TODO(cryptography): Can we restrict "x" components of public keys to all be the same sign, so we don't need to encode "y"'s signs? -->
2 changes: 1 addition & 1 deletion yellow-paper/docs/addresses-and-keys/precompiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Precompiled contracts, which borrow their name from Ethereum's, are contracts no

Note that, unlike user-defined contracts, the address of a precompiled [contract instance](../contract-deployment/instances.md) and the [identifier of its class](../contract-deployment/classes.md#class-identifier) both have no known preimage.

The rationale for precompiled contracts is to provide a set of vetted primitives for [note encryption](../private-message-delivery/encryption-and-decryption.md) and [tagging](../private-message-delivery/note-discovery.md) that applications can use safely. These primitives are guaranteed to be always-satisfiable when called with valid arguments. This allows account contracts to choose their preferred method of encryption and tagging from any primitive in this set, and application contracts to call into them without the risk of calling into a untrusted code, which could potentially halt the execution flow via an unsatisfiable constraint. Furthermore, by exposing these primitives in a reserved set of well-known addresses, applications can be forward-compatible and incorporate new encryption and tagging methods as accounts opt into them.
The rationale for precompiled contracts is to provide a set of vetted primitives for [note encryption](../private-message-delivery/private-msg-delivery.md#encryption-and-decryption) and [tagging](../private-message-delivery/private-msg-delivery.md#note-tagging) that applications can use safely. These primitives are guaranteed to be always-satisfiable when called with valid arguments. This allows account contracts to choose their preferred method of encryption and tagging from any primitive in this set, and application contracts to call into them without the risk of calling into a untrusted code, which could potentially halt the execution flow via an unsatisfiable constraint. Furthermore, by exposing these primitives in a reserved set of well-known addresses, applications can be forward-compatible and incorporate new encryption and tagging methods as accounts opt into them.

## Constants

Expand Down
5 changes: 5 additions & 0 deletions yellow-paper/docs/pre-compiled-contracts/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Pre-Compiled Contracts

import DocCardList from '@theme/DocCardList';

<DocCardList />

This file was deleted.

6 changes: 0 additions & 6 deletions yellow-paper/docs/private-message-delivery/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@
title: Private Message Delivery
---

<!--
Review notes for this chapter:
- Define "tagging", and briefly explain the motivation behind tagging. Why do we tag, instead of just emit the ciphertext for brute-force discovery? Jay did a good write up to justify tagging - I wonder if we can extract the essence from that (in a very condensed explanation)? Maybe tagging needs a short page of its own?
-
-->

# Private Message Delivery

Private message delivery encompasses the encryption, tagging, and broadcasting of private messages on the Aztec Network.
Expand Down
21 changes: 0 additions & 21 deletions yellow-paper/docs/private-message-delivery/note-discovery.md

This file was deleted.

47 changes: 40 additions & 7 deletions yellow-paper/docs/private-message-delivery/private-msg-delivery.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ If Alice executes a function that generates a note for Bob:

1. Alice will need to **encrypt** that note such that Bob, and only Bob is able to decrypt it.
2. Alice will need to **broadcast** the encrypted note ciphertext so as to make it available for Bob to retrieve.
3. Alice will need to **broadcast a 'tag'** alongside the encrypted note ciphertext. This tag must be identifiable by Bob's chosen [note discovery protocol](./note-discovery.md) but not identifiable by any third party as "intended for Bob".
3. Alice will need to **broadcast a 'tag'** alongside the encrypted note ciphertext. This tag must be identifiable by Bob's chosen [note discovery protocol](./private-msg-delivery.md#note-discovery-protocol-selection) but not identifiable by any third party as "intended for Bob".

## Requirements

Expand All @@ -19,20 +19,53 @@ If Alice executes a function that generates a note for Bob:

## Constraining Message Delivery

The protocol will enable app developers to constrain the correctness of the following:
The protocol will enable account contract developers to constrain the correctness of the following:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I agree with this change. It's the app who decides whether to constrain note encryption and tagging. The user just signals their preferred method via the registry, but it's the app who can decide to follow it or not, and whether to constrain it or not. Account contract devs do not deal with message delivery (wallet clients devs do, though).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right.


1. The encryption of a user's note.
2. The generation of the tag for that note.
3. The publication of that note and tag to the correct data availability layer.

Each app will define whether to constrain each such step. Encryption and tagging will be done through a set of [precompiled contracts](../addresses-and-keys/precompiles.md), each contract offering a different mechanism, and users will advertise their preferred mechanisms in a canonical [registry](./registry.md).
Each account contract will define whether to constrain each such step. Encryption and tagging will be done through a set of [precompiled contracts](../addresses-and-keys/precompiles.md), each contract offering a different mechanism, and users will advertise their preferred mechanisms in a canonical [registry](../pre-compiled-contracts/registry.md).

The advantages of this approach are:

<!-- Perhaps we need a section justifying why a user should choose their note discovery preference vs an app, and all the painful discussions we had relating to that -->

1. It enables a user to select their preferred [note discovery protocol](./note-discovery.md) and [encryption scheme](./encryption-and-decryption.md).
1. It enables a user to select their preferred [note discovery protocol](./private-msg-delivery.md#note-discovery-protocol-selection) and [encryption scheme](./private-msg-delivery.md#encryption-and-decryption).
2. It ensures that notes are correctly encrypted with a user's public encryption key.
3. It ensures that notes are correctly tagged for a user's chosen [note discovery protocol](./note-discovery.md).
3. It ensures that notes are correctly tagged for a user's chosen note discovery protocol.
4. It provides scope for upgrading these functions or introducing new schemes as the field progresses.
5. It protects applications from malicious unprovable functions.

## Note Discovery Protocol Selection

In order for a user to consume notes that belong to them, they need to identify, retrieve and decrypt them. A simple, privacy-preserving approach to this would be to download all of the notes and attempt decryption. However, the total number of encrypted notes published by the network will be substantial, making it infeasible for some users to do this. Those users will want to utilize a note discovery protocol to privately identify their notes.

Selection of the encryption and tagging mechanisms to use for a particular note are the responsibilty of a user's account contract rather than the application generating the note. This is to ensure the note is produced in a way compatible with the user's chosen note discovery scheme. Leaving this decision to applications could result in user's having to utilise multiple note discovery schemes, a situation we want to avoid.
PhilWindle marked this conversation as resolved.
Show resolved Hide resolved

## User Handshaking

Even if Alice correctly encrypts the note she creates for Bob and generates the correct tag to go with it, how does Bob know that Alice has sent him a note? Bob's note discovery protocol may require him to speculatively 'look' for notes with the tags that Alice (and his other counterparties) have generated. If Alice and Bob know each other then they can communicate out-of-protocol. But if they have no way of interacting then the network needs to provide a mechanism by which Bob can be alerted to the need to start searching for a specific sequence of tags.

To facilitate this we will deploy a canonical 'handshake' contract that can be used to create a private note for a recipient containing the sender's information (e.g. public key). It should only be necessary for a single handshake to take place between two users. The notes generated by this contract will be easy to identify enabling users to retrieve these notes, decrypt them and use the contents in any deterministic tag generation used by their chosen note discovery protocol.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't user handshaking relevant to only a subset of note discovery protocols? If so, I'd clarify that this depends on the method.

Also, are we "deploying a canonical handshake contract", or is the handshake going to be handled by the same precompile that deals with the note tagging, so as to avoid an extra function call?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point. I had thought this could be a single global handshake that happens once between 2 users and re-used for all tagging schemes. But now I'm wondering if that is realistic and each tagging contract would generate it's own handshake.


## Encryption and Decryption

Applications should be able to provably encrypt data for a target user, as part of private message delivery. As stated in the Keys section, we define three types of encrypted data, based on the sender and the recipient, from the perspective of a user:

Incoming data: data created by someone else, encrypted for and sent to the user.
Outgoing data: data created by the user to be sent to someone else, encrypted for the user.
Internal incoming data: data created by the user, encrypted for and sent to the user.
Encryption mechanisms support these three types of encryption, which may rely on different keys advertised by the user.

### Key Abstraction
To support different kinds of encryption mechanisms, the protocol does not make any assumptions on the type of public keys advertised by each user. Validation of their public keys is handled by the precompile contract selected by the user.

### Provable Decryption
While provable encryption is required to guarantee correct private message delivery, provable decryption is required for disclosing activity within an application. This allows auditability and compliance use cases, as well as being able to prove that a user did not execute certain actions. To support this, encryption precompiles also allow for provable decryption.

## Note Tagging

Note discovery schemes typically require notes to be accompanied by a stream of bytes generated specifically for the note discovery protocol. This 'tag' is then used in the procss of note identification. Whilst the tag itself is nnot sufficient to enable efficient note retrieval, the addition of it alongside the note enables the note discovery protocol to privately select a subset of the global set of notes to be returned to the user. This subset may still require some degree of trial-decryption but this is much more feasible given the reduced dataset.
PhilWindle marked this conversation as resolved.
Show resolved Hide resolved

When applications produce notes, they will need to call a protocol defined function within the account contract of the recipient and request that a tag be generated. From the protocol's perspective, this tag will simply be a stream of bytes relevant only to the recipient's note discovery protocol. It will be up to the account contract to constrain that the correct tag has been generated and from there the protocol circuits along with the rollup contract will ensure that the tag is correctly published along with the note.
PhilWindle marked this conversation as resolved.
Show resolved Hide resolved

Constraining tag generation is not solely about ensuring that the generated tag is of the correct format. It is also necessary to constrain that tags are generated in the correct sequence. A tag sequence with duplicate or missing tags makes it much more difficult for the recipient to retrieve their notes. This will likely require tags to be nullified once used.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Application contracts are in control of creating, encrypting, tagging, and broadcasting private notes to users. As such, each application is free to follow whatever scheme it prefers, choosing to override user preferences or use custom encryption and note tagging mechanisms. However, this may hinder composability, or not be compatible with existing wallet software.

In order to satisfy the requirements established for private message delivery, we suggest the following guidelines when building applications, which leverage the canonical [registry](./registry.md) contract.
In order to satisfy the requirements established for private message delivery, we suggest the following guidelines when building applications, which leverage the canonical [registry](../pre-compiled-contracts/registry.md) contract.

## Provably Sending a Note

Expand All @@ -16,7 +16,7 @@ Execution of the precompile that implements the recipient's choice for encryptio

## Pseudocode

The following pseudocode covers how to provably send a note to a recipient, given an `encryption_type` <!-- I think this should be `private_message_type`. Selecting the kind of key is separate from selecting an encryption scheme --> (incoming, outgoing, or internal incoming). Should the registry support [multiple entries for a given recipient](./registry.md#multiple-recipients-per-address), this method must execute a batched call per each entry recovered from the registry.
The following pseudocode covers how to provably send a note to a recipient, given an `encryption_type` <!-- I think this should be `private_message_type`. Selecting the kind of key is separate from selecting an encryption scheme --> (incoming, outgoing, or internal incoming). Should the registry support [multiple entries for a given recipient](../pre-compiled-contracts/registry.md#multiple-recipients-per-address), this method must execute a batched call per each entry recovered from the registry.

```
fn provably_send_note(recipient, note, encryption_type)
Expand Down Expand Up @@ -56,7 +56,7 @@ Last, applications with strong compliance and auditability requirements may choo

## Delivering Messages to Multiple Recipients via Shared Secrets

As an alternative to registering [multiple recipients for a given address](./registry.md#multiple-recipients-per-address), multisig participants may deploy a contract using a shared secret derived among them. This makes it cheaper to broadcast messages to the group, since every note does not need to be individually encrypted for each of them. However, it forces all recipients in the group to use the same encryption and tagging method, and adds an extra address they need to monitor for note discovery.
As an alternative to registering [multiple recipients for a given address](../pre-compiled-contracts/registry.md#multiple-recipients-per-address), multisig participants may deploy a contract using a shared secret derived among them. This makes it cheaper to broadcast messages to the group, since every note does not need to be individually encrypted for each of them. However, it forces all recipients in the group to use the same encryption and tagging method, and adds an extra address they need to monitor for note discovery.

## Discussions

Expand Down
Loading
Loading