mirror of https://github.com/waku-org/go-noise.git
191 lines
7.3 KiB
Go
191 lines
7.3 KiB
Go
package noise
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestWakuPairing(t *testing.T) {
|
|
// Pairing Phase
|
|
// ==========
|
|
|
|
// Alice static/ephemeral key initialization and commitment
|
|
aliceStaticKey, _ := DH25519.GenerateKeypair()
|
|
aliceEphemeralKey, _ := DH25519.GenerateKeypair()
|
|
s := generateRandomBytes(t, 32)
|
|
aliceCommittedStaticKey := CommitPublicKey(sha256.New, aliceStaticKey.Public, s)
|
|
|
|
// Bob static/ephemeral key initialization and commitment
|
|
bobStaticKey, _ := DH25519.GenerateKeypair()
|
|
bobEphemeralKey, _ := DH25519.GenerateKeypair()
|
|
r := generateRandomBytes(t, 32)
|
|
bobCommittedStaticKey := CommitPublicKey(sha256.New, bobStaticKey.Public, r)
|
|
|
|
prologue := generateRandomBytes(t, 100)
|
|
messageNametag := BytesToMessageNametag(generateRandomBytes(t, MessageNametagLength))
|
|
|
|
// We initialize the Handshake states.
|
|
// Note that we pass the whole qr serialization as prologue information
|
|
aliceHS, err := NewHandshake_WakuPairing_25519_ChaChaPoly_SHA256(aliceStaticKey, aliceEphemeralKey, true, prologue, bobEphemeralKey.Public)
|
|
require.NoError(t, err)
|
|
|
|
bobHS, err := NewHandshake_WakuPairing_25519_ChaChaPoly_SHA256(bobStaticKey, bobEphemeralKey, false, prologue, bobEphemeralKey.Public)
|
|
require.NoError(t, err)
|
|
|
|
// Pairing Handshake
|
|
// ==========
|
|
|
|
// Write and read calls alternate between Alice and Bob: the handhshake progresses by alternatively calling stepHandshake for each user
|
|
|
|
// 1st step
|
|
// -> eA, eAeB {H(sA||s)} [authcode]
|
|
|
|
// The messageNametag for the first handshake message is randomly generated and exchanged out-of-band
|
|
// and corresponds to qrMessageNametag
|
|
|
|
// We set the transport message to be H(sA||s)
|
|
sentTransportMessage := aliceCommittedStaticKey
|
|
|
|
// By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
|
|
// and the (encrypted) transport message
|
|
// The message is sent with a messageNametag equal to the one received through the QR code
|
|
aliceStep, err := aliceHS.Step(nil, sentTransportMessage, messageNametag)
|
|
require.NoError(t, err)
|
|
|
|
// Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
|
|
// Note that Bob verifies if the received payloadv2 has the expected messageNametag set
|
|
bobStep, err := bobHS.Step(aliceStep.PayloadV2, nil, messageNametag)
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, bytes.Equal(bobStep.TransportMessage, sentTransportMessage))
|
|
|
|
// We generate an authorization code using the handshake state
|
|
aliceAuthcode, err := aliceHS.Authcode()
|
|
require.NoError(t, err)
|
|
|
|
bobAuthcode, err := bobHS.Authcode()
|
|
require.NoError(t, err)
|
|
|
|
// We check that they are equal. Note that this check has to be confirmed with a user interaction.
|
|
require.Equal(t, aliceAuthcode, bobAuthcode)
|
|
|
|
// 2nd step
|
|
// <- sB, eAsB {r}
|
|
|
|
// Alice and Bob update their local next messageNametag using the available handshake information
|
|
// During the handshake, messageNametag = HKDF(h), where h is the handshake hash value at the end of the last processed message
|
|
aliceMessageNametag, err := aliceHS.ToMessageNametag()
|
|
require.NoError(t, err)
|
|
|
|
bobMessageNametag, err := bobHS.ToMessageNametag()
|
|
require.NoError(t, err)
|
|
|
|
// We set as a transport message the commitment randomness r
|
|
sentTransportMessage = r
|
|
|
|
// At this step, Bob writes and returns a payload
|
|
bobStep, err = bobHS.Step(nil, sentTransportMessage, bobMessageNametag)
|
|
require.NoError(t, err)
|
|
|
|
// While Alice reads and returns the (decrypted) transport message
|
|
aliceStep, err = aliceHS.Step(bobStep.PayloadV2, nil, aliceMessageNametag)
|
|
require.NoError(t, err)
|
|
require.Equal(t, aliceStep.TransportMessage, sentTransportMessage)
|
|
|
|
// Alice further checks if Bob's commitment opens to Bob's static key she just received
|
|
expectedBobCommittedStaticKey := CommitPublicKey(WakuPairing.hashFn, aliceHS.hs.rs, aliceStep.TransportMessage)
|
|
require.True(t, bytes.Equal(expectedBobCommittedStaticKey, bobCommittedStaticKey))
|
|
|
|
// 3rd step
|
|
// -> sA, sAeB, sAsB {s}
|
|
|
|
// Alice and Bob update their local next messageNametag using the available handshake information
|
|
aliceMessageNametag, err = aliceHS.ToMessageNametag()
|
|
require.NoError(t, err)
|
|
|
|
bobMessageNametag, err = bobHS.ToMessageNametag()
|
|
require.NoError(t, err)
|
|
|
|
// We set as a transport message the commitment randomness s
|
|
sentTransportMessage = s
|
|
|
|
// Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message
|
|
aliceStep, err = aliceHS.Step(nil, sentTransportMessage, aliceMessageNametag)
|
|
require.NoError(t, err)
|
|
|
|
// Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
|
|
bobStep, err = bobHS.Step(aliceStep.PayloadV2, nil, bobMessageNametag)
|
|
require.NoError(t, err)
|
|
require.True(t, bytes.Equal(bobStep.TransportMessage, sentTransportMessage))
|
|
|
|
// Bob further checks if Alice's commitment opens to Alice's static key he just received
|
|
expectedAliceCommittedStaticKey := CommitPublicKey(WakuPairing.hashFn, bobHS.hs.rs, bobStep.TransportMessage)
|
|
|
|
require.True(t, bytes.Equal(expectedAliceCommittedStaticKey, aliceCommittedStaticKey))
|
|
|
|
// Secure Transfer Phase
|
|
// ==========
|
|
|
|
aliceHSResult, err := aliceHS.FinalizeHandshake()
|
|
require.NoError(t, err)
|
|
|
|
bobHSResult, err := bobHS.FinalizeHandshake()
|
|
require.NoError(t, err)
|
|
|
|
// We test read/write of random messages exchanged between Alice and Bob
|
|
// Note that we exchange more than the number of messages contained in the nametag buffer to test if they are filled correctly as the communication proceeds
|
|
for i := 0; i < 1; i++ { //10*MessageNametagBufferSize; i++ {
|
|
// Alice writes to Bob
|
|
message := generateRandomBytes(t, 32)
|
|
payload, err := aliceHSResult.WriteMessage(message, nil)
|
|
require.NoError(t, err)
|
|
|
|
readMessage, err := bobHSResult.ReadMessage(payload, nil)
|
|
require.NoError(t, err)
|
|
require.True(t, bytes.Equal(message, readMessage))
|
|
|
|
// Bob writes to Alice
|
|
message = generateRandomBytes(t, 32)
|
|
payload, err = bobHSResult.WriteMessage(message, nil)
|
|
require.NoError(t, err)
|
|
|
|
readMessage, err = aliceHSResult.ReadMessage(payload, nil)
|
|
require.NoError(t, err)
|
|
require.True(t, bytes.Equal(message, readMessage))
|
|
}
|
|
|
|
// We test how nametag buffers help in detecting lost messages
|
|
// Alice writes two messages to Bob, but only the second is received
|
|
message := generateRandomBytes(t, 32)
|
|
_, err = aliceHSResult.WriteMessage(message, nil)
|
|
require.NoError(t, err)
|
|
|
|
message = generateRandomBytes(t, 32)
|
|
payload2, err := aliceHSResult.WriteMessage(message, nil)
|
|
require.NoError(t, err)
|
|
|
|
_, err = bobHSResult.ReadMessage(payload2, nil)
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, ErrNametagNotExpected)
|
|
|
|
// We adjust bob nametag buffer for next test (i.e. the missed message is correctly recovered)
|
|
bobHS.hsResult.nametagsInbound.Delete(2)
|
|
message = generateRandomBytes(t, 32)
|
|
payload2, err = bobHSResult.WriteMessage(message, nil)
|
|
require.NoError(t, err)
|
|
readMessage, err := aliceHSResult.ReadMessage(payload2, nil)
|
|
require.NoError(t, err)
|
|
require.True(t, bytes.Equal(message, readMessage))
|
|
|
|
// We test if a missing nametag is correctly detected
|
|
message = generateRandomBytes(t, 32)
|
|
payload2, err = aliceHSResult.WriteMessage(message, nil)
|
|
require.NoError(t, err)
|
|
bobHS.hsResult.nametagsInbound.Delete(1)
|
|
_, err = bobHSResult.ReadMessage(payload2, nil)
|
|
require.ErrorIs(t, err, ErrNametagNotFound)
|
|
}
|