Encrypted notes
- Linguagem predominante
- Rust
- Estrelas
- 132
- Forks
- 167
- Merge médio
- 1d 23h
- PRs com merge (30d)
- 110
Descrição
Initially, we are planning to support 2 types of notes:
1. Off-chain notes.
2. Public on-chain notes.
Off-chain notes enable privacy but require a side channel to communicate note information to the recipient. Public on-chain notes don't require a side channel as all info goes through the blockchain, but, as the name implies, they don't provide any privacy as note data is stored in plaintext. So, after support for off-chain and public on-chain notes is implemented, we should also implement encrypted on-chain notes.
To support these we'd need to make the following changes to the transaction kernel:
First, we need to implement note types as described in #402. Then, for every encrypted note the transaction kernel would need to encrypt note contents, compute a commitment to the resulting cyphertext, and output this commitment as note ID.
One property we want to achieve is that given a commitment and a cyphertext, anyone should be able to verify that the commitment was derived from this chyphertext. This is important so that cyphertext cannot be swapped out for something else as notes propagate through the network. Another important property is that upon decryption, the user should have sufficient data to execute the note.
Thus, the first step would be encrypting full note contents. This includes:
1. Note assets.
2. Note inputs.
3. Note script.
4. Serial number.
The encryption should probably happen in the transaction epilogue. By that time, we would have all of the above data in kernel memory, except for the note script (transaction kernel has access only to the note script root). Thus, the script itself (i.e., the MASM file) would need to be provided non-deterministically via the advice provider.
One unfortunate thing here is that the transaction kernel would have no way of verifying whether the provided MASM code matches the script root (compiling MASM to MAST inside the kernel would be prohibitively expensive). So, the sender would be able to create an invalid note where the provided MASM does not match the script root). This is an attack vector, though I'm not sure how serious it is (the sender can always craft an invalid note anyway).
To summarize, we'd want to encrypt the note as follows:
```
cyphertext = encrypt(script_root || assets || inputs || serial_num || script_masm, iv)
```
Where `iv` is the initialization vector used for encryption and required for decryption.
**Open question**: do we actually need the `iv` value or can we assume it is always 0? Usually, this is not a good idea, but in this case, `serial_num` is supposed to be random and unique for each note - and so, no two notes should ever have the same cyphertext.
Assuming we do need the `iv`, we can put it into note metadata. This way, the value of `iv` is associated with the note but is also public (i.e., accessible to the recipient without a side channel).
Once we have the cyphertext, we would compute note ID as follows:
```
note_id = hash(cyphertext)
```
**Open question**: with the above approach, we'd need to process the entire note contents twice: once during the encryption, and the other time during hashing. Is there a way to do this only once while still maintaining the property that given `(note_id, cyphertext)` anyone can verify that these 2 go together.
Also, to do the above efficiently, we probably need to implement efficient encryption mechanism within the VM. Small notes (e.g., `P2ID`) would probably be not too costly to encrypt as their serialized versions should probably be under 200 bytes, but I can also imagine notes which are several KB in size.
Guia de contribuição
Avaliação
Esta issue ainda não foi avaliada.