18 KiB
Secure Transfers with Noise
In this document we describe a compound protocol to enable two devices to mutually authenticate and securely exchange (arbitrary) information. It consists of two main subprotocols or phases:
- Device Pairing: the devices exchange and authenticate their long term device ID static keys;
- Secure Transfer: the devices securely exchange in encrypted form information using key material obtained during a successful pairing phase.
Device Pairing
In the pairing phase, a device B requests to be paired to a device A. Once the two devices are paired, the devices will be mutually authenticated and will share a Noise session within which they can securely exchange information.
The request is made by exposing a QR code that, by default, has to be scanned by device A.
If device A doesn't have a camera while device B does, it is possible to execute a slightly different pairing (with same security guarantees), where A is exposing a QR code instead.
Employed Cryptographic Primitives
H: the underlying hash function, i.e. SHA-256;HKDF: a key derivation function (based on SHA-256);Curve25519: the underlying elliptic curve for Diffie-Hellman (DH) operations.
The WakuPairing Noise Handshake
The devices execute a custom handshake derived from X1X1, where they mutually exchange and authenticate their device static keys by exchanging messages over the content topic
contentTopic = /{application-name}/{application-version}/wakunoise/1/sessions-{shard-id}/proto
The handshake, detailed in next section, can be summarized as:
WakuPairing:
0. <- eB {H(sB||r), contentTopicParams, messageNametag}
...
1. -> eA, eAeB {H(sA||s)} [authcode]
2. <- sB, eAsB {r}
3. -> sA, sAeB, sAsB {s}
{}: payload, []: user interaction
Protocol Flow
-
The device
Bexposes through a QR code a Base64 serialization of:- An ephemeral public key
eB; - The content topic parameters
contentTopicParams = {application-name}, {application-version}, {shard-id}. - A randomly generated 8-bytes long
messageNametag. - A commitment
H(sB||r)for its static keysBwhereris a random fixed-lenght value.
- An ephemeral public key
-
The device
A:- scans the QR code;
- obtains
eB,contentTopicParams,messageNametag,Hash(sB|r); - checks if
{application-name}and{application-version}fromcontentTopicParamsmatch the local application name and version: if not, aborts the pairing. - initializes the Noise handshake by passing
contentTopicParams,messageNametagandHash(sB||r)to the handshake prologue; - executes the pre-handshake message, i.e. processes the key
eB; - executes the first handshake message over
contentTopic, i.e.- processes and sends a Waku message containing an ephemeral key
eA; - performs
DH(eA,eB)(which computes a symmetric encryption key); - attach as payload to the handshake message a commitment
H(sA|s)forA's static keysA, wheresis a random fixed-lenght value;
- processes and sends a Waku message containing an ephemeral key
- an 8-digits authorization code
authcodeobtained asHKDF(h) mod 10^8is displayed on the device, wherehis the handshake value obtained once the first handshake message is processed.
-
The device
B:- listens to messages sent to
/{application-name}/{application-version}/wakunoise/1/sessions-{shard-id}/protoand locally filters only those with Waku payload starting withmessageNametag. If any, continues. - initializes the Noise handshake by passing
contentTopicParams,messageNametagandHash(sB||r)to the handshake prologue; - executes the pre-handshake message, i.e. processes its static key
eB; - executes the first handshake message, i.e.
- obtains from the received message a public key
eA. IfeAis not a valid public key, the protocol is aborted. - performs
DH(eA,eB)(which computes a symmetric encryption key); - decrypts the commitment
H(sA||s)forA's static keysA.
- obtains from the received message a public key
- an 8-digits authorization code
authcodeobtained asHKDF(h) mod 10^8is displayed on the device, wherehis the handshake value obtained once the first handshake message is processed.
- listens to messages sent to
-
Device
AandBwait the user to confirm with an interaction (button press) that the authorization code displayed on both devices are the same. If not, the protocol is aborted. -
The device
B:- executes the second handshake message, i.e.
- processes and sends his (encrypted) device static key
sBovercontentTopic; - performs
DH(eA,sB)(which updates the symmetric encryption key); - attaches as payload the (encrypted) commitment randomness
rused to computeH(sB||r).
- processes and sends his (encrypted) device static key
- executes the second handshake message, i.e.
-
The device
A:- listens to messages sent to
/{application-name}/{application-version}/wakunoise/1/sessions-{shard-id}/protoand locally filters only those with Waku payload starting withmessageNametag. If any, continues. - obtains from decrypting the received message a public key
sB. IfsBis not a valid public key, the protocol is aborted. - performs
DH(eA,sB)(which updates a symmetric encryption key); - decrypts the payload to obtain the randomness
r. - Computes
H(sB||r)and checks if this value corresponds to the commitment obtained in step 2. If not, the protocol is aborted. - executes the third handshake message, i.e.
- processes and sends his (encrypted) device static key
sAovercontentTopic; - performs
DH(sA,eB)(which updates the symmetric encryption key); - performs
DH(sA,sB)(which updates the symmetric encryption key); - attaches as payload the (encrypted) commitment randomness
sused to computeH(sA||s).
- processes and sends his (encrypted) device static key
- Calls Split() and obtains two cipher states to encrypt inbound and outbound messages.
- listens to messages sent to
-
The device
B:- listens to messages sent to
/{application-name}/{application-version}/wakunoise/1/sessions-{shard-id}/protoand locally filters only those with Waku payload starting withmessageNametag. If any, continues. - obtains from decrypting the received message a public key
sA. IfsAis not a valid public key, the protocol is aborted. - performs
DH(sA,eB)(which updates a symmetric encryption key); - performs
DH(sA,sB)(which updates a symmetric encryption key); - decrypts the payload to obtain the randomness
s. - Computes
H(sA||s)and checks if this value corresponds to the commitment obtained in step 6. If not, the protocol is aborted. - Calls Split() and obtains two cipher states to encrypt inbound and outbound messages.
- listens to messages sent to
The WakuPairing for Devices without a Camera
In the above pairing handshake, the QR is by default exposed by device B and not by A because device B locally stores no relevant cryptographic material, so an active local attacker that scans the QR code first would only be able to transfer his own session information and get nothing from A.
However, since the user confirms at the end of message 1 that the authorization code is the same on both devices, the role of handhsake initiator and responder can be safely swapped in message 0 and 1.
This allows pairing in case device A does not have a camera to scan a QR (e.g. a desktop client) while device B has.
The resulting handshake would then be:
WakuPairing2:
0. -> eA {H(sB||r), contentTopicParams, messageNametag}
...
1. <- eB, eAeB {H(sB||r)} [authcode]
2. <- sB, eAsB {r}
3. -> sA, sAeB, sAsB {s}
{}: payload, []: user interaction
Security Analysis
Assumptions
-
The attacker is active, i.e. can interact with both devices
AandBby sending messages overcontentTopic. -
The attacker has access to the QR code, that is knows the ephemeral key
eB, the commitmentH(sB||r)and thecontentTopicexposed by the deviceB. -
Devices
AandBare considered trusted (otherwise the attacker will simply exfiltrate the relevant information from the attacked device). -
As common for Noise, we assume that ephemeral keys cannot be compromised, while static keys might be later compromised. However, we enforce in the pairing phase extra security mechanisms (i.e. use of commitments for static keys) that will prevent some attacks possible when ephemeral keys are weak or get compromised.
Rationale
-
The device
Bexposes a commitment to its static keysBbecause:- if the private key of
eBis weak or gets compromised, an attacker can impersonateBby sending in message2to deviceAhis own static key and successfully complete the pairing phase. Note that being able to compromiseeBis not contemplated by our security assumptions. Bcannot adaptively choose a static key based on the state of the Noise handshake at the end of message1, i.e. after the authentication code is confirmed. Note that deviceBis trusted in our security assumptions.- Confirming the authentication code after processing message
1will ensure that no Man-in-the-Middle (MitM) can send a static key different thansB.
- if the private key of
-
The device
Asends a commitment to its static keysAbecause:Acannot adaptively choose a static key based on the state of the Noise handshake at the end of message1, i.e. after the authentication code is confirmed. Note that deviceAis trusted in our security assumptions.- Confirming the authentication code after processing message
1will ensure that no MitM can send a static key different thansA.
-
The authorization code is shown and has to be confirmed at the end of message
1because:- an attacker that frontruns device
Aby sending faster his own ephemeral key would be detected before he's able to know deviceBstatic keysB; - it ensures that no MitM attacks will happen during the whole pairing handshake, since commitments to the (later exchanged) device static keys will be implicitly acknowledged by the authorization code confirmation;
- it enables to safely swap the role of handshake initiator and responder (see above);
- an attacker that frontruns device
-
Device
Bsends his static key first because:- by being the pairing requester, it cannot probe device
Aidentity without revealing its own (static key) first. Note that deviceBstatic key and its commitment can be binded to other cryptographic material (e.g., seed phrase).
- by being the pairing requester, it cannot probe device
-
Device
Bopens a commitment to its static key at message2.because:- if device
Areplies concluding the handshake according to the protocol, deviceBacknowledges that deviceAcorrectly received his static keysB, sincerwas encrypted under an encryption key derived from the static keysBand the genuine (due to the previousauthcodeverification) ephemeral keyseAandeB.
- if device
-
Device
Aopens a commitment to its static key at message3.because:- if device
Bdoesn't abort the pairing, deviceAacknowledges that deviceBcorrectly received his static keysA, sinceswas encrypted under an encryption key derived from the static keyssAandsBand the genuine (due to the previousauthcodeverification) ephemeral keyseAandeB.
- if device
Secure Transfer
Once the handshake is concluded, sensitive information can be exchanged using the encryption keys agreed during the pairing phase. If stronger security guarantees are required, some additional tweaks are possible.
In the following subsections we report the details of applications which are currently under development, mainly in order to implement 35/WAKU2-SESSION.
However, the pairing and transfer phases descriptions are designed to be application-agnostic, and should be flexible enough to mutually authenticate and allow secure communication of two devices over a distributed network of Waku2 nodes.
N11M session management mechanism
In this scenario, one of Alice's devices is already communicating with one of Bob's devices within an active Noise session, e.g. after a successful execution of a Noise handshake.
Alice and Bob would then share some cryptographic key material, used to encrypt their communications. According to 37/WAKU2-NOISE-SESSIONS this information consists of:
- A
session-id(32 bytes) - Two cipher state
CSOutbound,CSInbound, where each of them contains:- an encryption key
k(2x32bytes) - a nonce
n(2x8bytes) - (optionally) an internal state hash
h(2x32bytes)
- an encryption key
for a total of 176 bytes of information.
In a N11M session mechanism scenario, all (synced) Alice's devices that are communicating with Bob, share the same Noise session cryptographic material.
Hence, if Alice wishes to add a new device, she must securely transfer a copy of such data from one of her device A to a new device B in her possession.
In order to do so she can:
- pair device
AwithBin order to have a Noise session between them; - securely transfer within such session the 176 bytes serializing the active session with Bob;
- manually instantiate in
Ba Noise session with Bob from the received session serialization.
Additional Possible Tweaks
Randomized Rekey
The Noise framework supports Rekey() in order to update encryption keys "so that a compromise of cipherstate keys will not decrypt older [exchanged] messages". However, if a certain cipherstate key is compromised, it will become possible for the attacker not only to decrypt messages encrypted under that key, but also all those messages encrypted under any successive new key obtained through a call to Rekey().
This can be mitigated by:
- keeping the full Handhshake State even after the handshake is complete (by Noise specification a call to
Split()should delete the Handshake State) - continuing updating the Handshake State by processing every after-handshake exchanged message (i.e. the
payload) according to the Noise processing rules (i.e. by callingEncryptAndHash(payload)andDecryptAndHash(payload)); - adding to each (or every few) message exchanged in the transfer phase a random ephemeral key
eand perform Diffie-Hellman operations with the other party's ephemeral/static keys in order to update the underlying CipherState and recover new random inbound/outbound encryption keys by callingSplit().
In short, the transfer phase would look like (but not necessarily the same as):
TransferPhase:
-> eA, eAeB, eAsB {payload}
<- eB, eAeB, sAeB {payload}
...
{}: payload
Messages Nametag Derivation
To reduce metadata leakages and increase devices's anonymity over the p2p network, 35/WAKU2-NOISE suggests to use some common secrets ctsInbound, ctsOutbound (e.g. ctsInbound, ctsOutbound = HKDF(h) where h is the handshake hash value of the Handshake State at some point of the pairing phase) in order to frequently and deterministically change the messageNametag of messages exchanged during the pairing and transfer phase - ideally, at each message exchanged.
Given the proposed construction,
the ctsInbound and ctsOutbound secrets can be used to iteratively generate the messageNametag field of Waku payloads for inbound and outbound messages, respectively.
The derivation of messageNametag should be deterministic only for communicating devices and independent from message content, otherwise lost messages will prevent computing the next message nametag. A possible approach consists in computing the n-th messageNametag as H( ctsInbound || n), where n is serialized as uint64.
In this way, sender's and recipient's devices can keep updated a buffer of messageNametag to sieve while listening to messages sent over /{application-name}/{application-version}/wakunoise/1/sessions-{shard-id}/ (i.e., the next 50 not yet seen), and become then able to further identify if one or more messages were eventually lost/not-yet-delivered during the communication.
This approach brings also the advantage that communicating devices can efficiently identify encrypted messages addressed to them.
We note that since the ChaChaPoly cipher used to encrypt messages supports additional data, an encrypted payload can be further authenticated by passing the messageNametag as additional data to the encryption/decryption routine. In this way, an attacker would be unable to craft an authenticated Waku message even in case the currently used symmetric encryption key is compromised, unless ctsInbound, ctsOutbound or the messageNametag buffer lists were compromised too.
Future Work: n-to-1 Device Pairing
The above protocol pairs a single device A with B, creating the conditions for a secure transfer. However, we would like to efficiently address scenarios (e.g. the NM session management mechanism) where a device B is paired with multiple devices A1, A2, ..., An, which were, in turn, already paired two-by-two. A naive approach requires B to be paired with each of such devices, but exposing/scanning n QRs would quickly become impractical as the number of devices increases.
As a future work, we wish to design a n-to-1 pairing protocol, where only one out of n devices scans the QR exposed by the pairing requester device and the latter can efficiently (in term of exchanged messages) be securely paired to all of them.
A possible approach requires that all already paired devices share a list of pairing key bundles, that device B can securely receive from the device it has been paired with and use to complete multiple pairings, in a similarly fashion as X3DH.