bLIP: 31
Title: Mutual Message Exchange
Status: Active
Author: Matt Corallo <[email protected]>
Created: 2024-01-14
License: CC0
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.
This bLIP is licensed under the CC0 license.
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 eachs_i
in the initiator'ss
set.
- MAY include
repeated_data
which will be received with the message to avoid storing state. - MUST set
repeated_data_len
to the length ofrepeated_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 eachs_i
it has stored. - If a
peer_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 theper_peer_handshake
they were able to decrypt within the init bytes. - MUST set
encrypted_nonce
toAEAD(messenger_n, H(s_i || initiator_n), PROTO_SALT ^ MASK_A, PROTO_AAD)
- MUST include the same
repeated_data_len
andrepeated_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 areinline_msg_key
, the next 32 bytes areoob_msg_key
. - If
message_length
is not zero, MUST setencrypted_message
toAEAD(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 containedmessenger_n
to decrypt themessage
. - MAY exchange further messages with the initiator, using the
oob_msg_key
to encrypt and authenticate such messages.
H()
is SHA-256ECDH()
is ECDH using secp256k1 public/secret keys.AEAD(data, key, salt, aad)
is the ChaCha20Poly1305 RFC variant authenticated encryptionECDH_SALT
is "Mutual Message Exchange ECDH Result"KEY_STRETCH_SALT
is "INLINE KEY STRCH"MASK_A
is all 0xff bytesMASK_B
is all 0xf0 bytesPROTO_SALT
is a salt picked to describe a specific protocol using this message exchangePROTO_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.
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.
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.