firoorg / firo

The privacy-focused cryptocurrency
https://firo.org
MIT License
720 stars 355 forks source link

Allow senders to include Spark recovery data in transactions #1402

Open AaronFeickert opened 9 months ago

AaronFeickert commented 9 months ago

Currently, it's not possible for the sender of a Spark transaction to recover certain transaction information unless it caches coin nonces locally. This information includes recipient addresses and memos. It's possible to include this in a way that would ensure senders can obtain these details when restoring a wallet, and would also simplify payment proofs significantly.

One approach would be to encrypt coin nonces and recipient addresses (without the encrypted diversifier) using a key derived from coin public data and the sender's full view key.

For full view key component s_2 and coin serial commitment, S, we can define the disclosure key k_disc = KDF(s_2, S) for a key derivation function KDF. Given a stream cipher keyed with k_disc, the sender encrypts the tuple (k, Q_1, Q_2). Here k is the coin nonce and Q_1, Q_2 are two of the components of the recipient address.

When the user wishes to restore a wallet or otherwise scan using the full view key, it uses the full view key already to derive incoming coin linking tags, which are used to determine when the coin is later spent. On such detection of a spend, the user would derive k_disc and use it to decrypt the tuple. It then uses k and Q_1 to derive the recipient AEAD key k_aead and decrypt the recipient data, which includes the coin memo m, the value v, and the component d of the recipient address.

When the user wishes to produce a payment proof, it follows the approach in the Spark preprint. However, it no longer sends the tuple (k, d, Q_1, Q_2) with the proof; instead, it sends k_disc. Since this key is derived from the user's full view key uniquely using a KDF, this does not leak information about other coins. The verifier uses the key to decrypt (k, Q_1, Q_2), and then continues with proof verification as usual.

Encryption of this additional data can use a fixed (zero) initialization vector and does not require an authentication tag, so encryption using a stream cipher means each coin requires an additional 96 bytes of data included. While the sender could provide a payment proof verifier with a different key than k_disc and obtain different (k, Q_1, Q_2), payment proof security guarantees still apply and cannot be used to fool the verifier.

AaronFeickert commented 9 months ago

Note the correctness and consistency of the sender-encrypted data (like that of existing recipient-encrypted data) cannot be checked by the network. This means that if a user does not wish to have the data included, it can simply encrypt empty or junk data of the expected length.

This doesn't change the guarantees that payment proofs provide, however. If junk or incorrect sender-encrypted data were decrypted during payment proof verification, the verification would fail.

AaronFeickert commented 9 months ago

Interestingly, this could in certain cases replace payment proofs. Payment proofs reveal coin information (value, memo, and recipient address) to the verifier, but also assert that the prover had spend authority for the transaction that created the coin. If the verifier doesn't care about the spend authority assertion, but only the coin information, it could simply ask the prover to provide k_disc without an accompanying payment proof. This is much easier for the prover to produce than a full payment proof.