From 3b2ef9f49dcc5d850ad10f3009a46092b9db3f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Sat, 24 Aug 2019 23:50:12 +0200 Subject: [PATCH] noise-libp2p spec: rethink message data construction. The current message data construction is not secure enough, as it is vulnerable to replay attacks by a MITM agent that records previous handshakes, and transplants old user data from those handshakes into new ones, as long as the static key hasn't changed. We fix this vulnerability by introducing a signature over the whole message payload against the ephemeral session key. This "seals" the payload so that it's only valid for that exchange. This PR simplifies protobuf field naming. Finally, we formalise in which Noise messages of IK and XX the message payload is to be shared, to guarantee secrecy, integrity and authentication. --- noise/README.md | 86 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 27 deletions(-) diff --git a/noise/README.md b/noise/README.md index d74a1570e..833cfe6d4 100644 --- a/noise/README.md +++ b/noise/README.md @@ -204,54 +204,86 @@ the early data MUST be included as described below. #### The libp2p Signed Handshake Payload -libp2p-specific data, including the signature used for static key -authentication, is transmitted in Noise handshake message payloads. When -decrypted, the message payload has the structure described in [Encrypted +libp2p-specific early data, plus a signature that links the static Noise key +of the session with the libp2p identity key (for authentication), is +transmitted in Noise handshake message payloads. + +These payloads MUST appear in the first handshake message that guarantees +secrecy, sender authentication, and integrity: + +* In XX-initiated handshakes, the initiator will send its payload in message 3 + (closing message), whereas the responder will send it in message 2 (its only + message). +* In IK-initiated optimistic handshakes, the initiator will send its message + payload message 1, as it satisfies the guarantees. + * If the responder continues the IK handshake, it will send its message + payload in message 2. + * If the responder fall backs to XXfallback, it won't be able to decrypt + the initiator's message data from message 1. Therefore, the initiator + MUST rebuild the message payload with the new cryptographic material, + and resend it in the 3rd message. + +To guard against replay attacks, these message payloads are signed with the +ephemeral key of the session. + +When decrypted, the signed payload has the structure described in [Encrypted Payloads](#encrypted-payloads), consisting of a length-prefixed `body` field -followed by optional padding. The `body` of the payload contains a serialized -[protobuf][protobuf] message with the following schema: +followed by optional padding. + +The `body` of the payload contains a serialized [protobuf][protobuf] +`NoiseSignedHandshakePayload` message with the following schema: ``` protobuf +message NoiseSignedHandshakePayload { + NoiseHandshakePayload payload = 1; + bytes payload_sig = 2; +} + message NoiseHandshakePayload { - bytes libp2p_key = 1; - bytes noise_static_key_signature = 2; - bytes libp2p_data = 3; - bytes libp2p_data_signature = 4; + bytes identity_key = 1; + bytes identity_sig = 2; + bytes data = 3; + bytes data_sig = 4; } ``` -The `libp2p_key` field contains a serialized `PublicKey` message as defined in -the [peer id spec][peer-id-spec]. +The `payload` field contains a Protobuf-serialized `NoiseHandshakePayload` +struct. + +The `payload_sig` field contains the signature of the `payload` against the +ephemeral key for the session. + +The `identity_key` field contains a serialized `PublicKey` message as defined +in the [peer id spec][peer-id-spec]. -The `noise_static_key_signature` field is produced using the libp2p identity -private key according to the [signing rules in the peer id +The `identity_sig` field is produced using the libp2p identity private key +according to the [signing rules in the peer id spec][peer-id-spec-signing-rules]. The data to be signed is the UTF-8 string `noise-libp2p-static-key:`, followed by the Noise static public key, encoded according to the rules defined in [section 5 of RFC 7748][rfc-7748-sec-5]. -The `libp2p_data` field contains the "early data" provided to the Noise module -when initiating the handshake, if any. The structure of this data is opaque to +The `data` field contains the "early data" provided to the Noise module when +initiating the handshake, if any. The structure of this data is opaque to noise-libp2p and is expected to be defined in a future iteration of the connection establishment spec. -If `libp2p_data` is non-empty, the `libp2p_data_signature` field MUST contain a -signature produced with the libp2p identity key. The data to be signed is the -UTF-8 string `noise-libp2p-early-data:` followed by the contents of the -`libp2p_data` field. +If `data` is non-empty, the `data_sig` field MUST contain a signature produced +with the libp2p identity key. The data to be signed is the UTF-8 string +`noise-libp2p-early-data:` followed by the contents of the `data` field. -Upon receiving the handshake payload, peers MUST decode the public key from the -`libp2p_key` field into a usable form. The key MUST be used to validate the -`noise_static_key_signature` field against the static Noise key received in the +Upon receiving the handshake payload, peers MUST decode the public key from +the `identity_key` field into a usable form. The key MUST be used to validate +the `identity_sig` field against the static Noise key received in the handshake. If the signature is invalid, the connection MUST be terminated immediately. -If the `libp2p_data` field is non-empty, the `libp2p_data_signature` MUST be -validated against the supplied `libp2p_data`. If the signature is invalid, the -connection MUST be terminated immediately. +If the `data` field is non-empty, the `data_sig` MUST be validated against the +supplied `data`. If the signature is invalid, the connection MUST be +terminated immediately. If a noise-libp2p implementation does not expose an API for early data, they -MUST still validate the signature upon receiving a non-empty `libp2p_data` -field and abort the connection if it is invalid. +MUST still validate the signature upon receiving a non-empty `data` field and +abort the connection if it is invalid. ### Supported Handshake Patterns