Skip to content

Latest commit

 

History

History
145 lines (119 loc) · 7.47 KB

blip-0031.md

File metadata and controls

145 lines (119 loc) · 7.47 KB
bLIP: 31
Title: Mutual Message Exchange
Status: Active
Author: Matt Corallo <[email protected]>
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 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 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 initiator, 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_nonces 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