diff --git a/blip-0031.md b/blip-0031.md new file mode 100644 index 0000000..218effb --- /dev/null +++ b/blip-0031.md @@ -0,0 +1,145 @@ +``` +bLIP: 31 +Title: Mutual Message Exchange +Status: Active +Author: Matt Corallo +Created: 2024-01-14 +License: CC0 +``` + +## Abstract + +This bLIP defines a simple protocol by which a paying party can include an encrypted message for a +payment recipient iff both parties have indicated they wish to communicate with each other. + +## Copyright + +This bLIP is licensed under the CC0 license. + +## Specification + +This protocol is defined in terms of two parties: (a) the initiator, and (b) the message-sender. In +the lightning context, the message-sender is the payment sender, and the initiator is the payment +recipient. + +Each party is configured with a private key (`k`) and a list of acceptable public keys they wish to +exchange messages with (`P`). + +Each party stores the set `s` of the result of `H(ECDH_SALT || H(ECDH(k, P_i)))` for each `P_i` in `P`. + +The initiator picks a random 32-byte nonce `initiator_n`. It then generates the init bytes, which are: + * [`u16`:`handshake_count`] + * [`48*handshake_count*byte`:`per_peer_handshake`] + * [`u16`:`repeated_data_len`] + * [`repeated_data_len*byte`:`repeated_data`] + +The initiator: + * MUST set `handshake_count` to a number greater than its trusted peer set count, using round + values to avoid revealing how many trusted peers it has configured. + * MUST fill in each `per_peer_handshake` with either: + * pseudorandom data for any excess unused trusted peer entries, + * `AEAD(initiator_n, s_i, PROTO_SALT, PROTO_AAD)` for each `s_i` in the initiator's `s` set. + * MAY include `repeated_data` which will be received with the message to avoid storing state. + * MUST set `repeated_data_len` to the length of `repeated_data`, if any. + +The message-sender: + * MUST ignore any bytes which are included after the end of `repeated_data`. + * SHOULD walk each `per_peer_handshake` and check whether it is decryptable using each `s_i` it + has stored. + * If a `per_peer_handshake` is decryptable, should send a response including the message. + +The message sender similarly picks a random 32-byte nonce `messenger_n` and responds with: + * [`u16`:`handshake_index`] + * [`48*byte`:`encrypted_nonce`] + * [`u16`:`repeated_data_len`] + * [`repeated_data_len*byte`:`repeated_data`] + * [`u16`:`message_length`] + * [`message_length*byte`:`encrypted_message`] + +The message-sender: + * MUST set `handshake_index` to the index of the `per_peer_handshake` they were able to decrypt + within the init bytes. + * MUST set `encrypted_nonce` to + `AEAD(messenger_n, H(s_i || initiator_n), PROTO_SALT ^ MASK_A, PROTO_AAD)` + * MUST include the same `repeated_data_len` and `repeated_data` as was sent by the initiator. + * MUST set `message_length` to the length of the message they wish to send, *including* the + 16-byte MAC tag. If they only wish to communicate a message separately, `message_length` MUST be + set to 0. + * MAY calculate two message encryption keys by reading 64 bytes from + `ChaCha20(initiator_n ^ messenger^n, KEY_STRETCH_SALT)`. The first 32 bytes are `inline_msg_key`, + the next 32 bytes are `oob_msg_key`. + * If `message_length` is not zero, MUST set `encrypted_message` to + `AEAD(message, inline_msg_key, PROTO_SALT ^ MASK_B, PROTO_AAD)` + * MAY exchange further messages with the initiator, using the `oob_msg_key` to encrypt and + authenticate such messages. + +The initiator (message recipient): + * If `message_length` is between 1 and 15 (inclusive), the handshake should be treated as having + failed. + * MUST ignore any bytes which are included after the end of `encrypted_message`. + * SHOULD decrypt the `encrypted_nonce` and use the contained `messenger_n` to decrypt the + `message`. + * MAY exchange further messages with the message-sender, using the `oob_msg_key` to encrypt and + authenticate such messages. + +### Constants + * `H()` is SHA-256 + * `ECDH()` is ECDH using secp256k1 public/secret keys. + * `AEAD(data, key, salt, aad)` is the ChaCha20Poly1305 RFC variant authenticated encryption + * `ECDH_SALT` is "Mutual Message Exchange ECDH Result" + * `KEY_STRETCH_SALT` is "INLINE KEY STRCH" + * `MASK_A` is all 0xff bytes + * `MASK_B` is all 0xf0 bytes + * `PROTO_SALT` is a salt picked to describe a specific protocol using this message exchange + * `PROTO_AAD` is excess data used to describe a specific instantiation of this protocol, for + example the specific TLV which is being used to communicate a particular type of message. + +## Discussion + +There are various times during which lightning senders may wish to include a message for the +recipient. In many cases, the sender wishes to include such a message no matter the recipient, +often accomplished by simply including that message as an additional TLV entry in the recipient's +onion. However, in some cases the sender wishes to only include their message if there is mutual +trust between the sender and the recipient. + +Prior to BOLT12, this was still a rather simple problem - senders could validate the recipient +node_id against a list of parties they wished to message and then include their message in the +onion as they otherwise would. However, with BOLT12 node_ids are no longer immediately visible. +Worse, as the number of payment protocols continues to proliferate, senders need to have a list +of acceptable messaging peers in multiple formats, maintained separately. + +This bLIP defines a simple protocol by which a paying party can include an encrypted message for a +payment recipient iff both parties have indicated they wish to communicate with each other. It only +requires the ability for the payment recipient to include additional bytes (~48 bytes per peer they +are willing to exchange messages with). From there, the sender only has to include an extra 100-200 +bytes in addition to the message itself in the onion they ultimately use to send the payment. + +If the sender is not in the recipient's trusted peers list, they will not learn anything about who +the recipient is. However if the recipient does trust the sender, the sender may be able to learn +who the recipient is even if the sender does not trust the recipient (and thus no message will be +exchanged). + +This protocol is envisioned for use in BOLT12 payment exchanges, however it can be used in any +payment negotiation protocol, or even in non-payment contexts. + +## Rationale + +There are several alternative protocols which could be considered. + +First, the protocol could be split into one additional message - instead of the initiator being the +payment recipient, the payment sender could initiate. This would result in less data being included +in the final HTLC, however it would result in most of the CPU cost being borne by the (potential) +payment recipient, rather than the payment sender. This could allow for nodes to initiate the +protocol repeatedly with a victim, DoSing them without ever sending a payment. + +Second, the protocol could be made further private by excluding the MACs in the initiator's bytes. +This would result in the responder being forced to include `N*M` `encrypted_nonce`s in their +response (implying the first change above as it would now be too large to include in an onion). This +would result in the responder not learning whether they were in the initiator's trusted set unless +they also trusted the initiator, however at the cost of substantial overhead. Because the +anticipated use cases of this protocol imply mutual trust, this is not considered worth the +trade-off. Further, this would also present challenges in situations where we only have a full RTT +available, namely in the BOLT12 `refund` case. + +## Reference Implementation +* LDK: