143 lines
4.8 KiB
Go
143 lines
4.8 KiB
Go
package messages_test
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
|
|
"forge.redroom.link/yves/meowlib"
|
|
"forge.redroom.link/yves/meowlib/client"
|
|
"forge.redroom.link/yves/meowlib/client/invitation/messages"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// setupIdentity creates a fresh identity and sets it as the active config identity.
|
|
// Returns the identity and a cleanup function.
|
|
func setupIdentity(t *testing.T, nickname string) (*client.Identity, func()) {
|
|
t.Helper()
|
|
cfg := client.GetConfig()
|
|
cfg.SetMemPass("testpass") //nolint:errcheck
|
|
|
|
id, err := client.CreateIdentity(nickname)
|
|
require.NoError(t, err)
|
|
cfg.SetIdentity(id)
|
|
|
|
cleanup := func() {
|
|
os.RemoveAll(cfg.StoragePath + "/" + id.Uuid)
|
|
}
|
|
return id, cleanup
|
|
}
|
|
|
|
// TestStep2ProducesPackedUserMessage verifies that Step2 returns a PackedUserMessage
|
|
// (not just a peer) and that the message is encrypted with the initiator's temp key
|
|
// so Step3 can decrypt it.
|
|
func TestStep2ProducesPackedUserMessage(t *testing.T) {
|
|
cfg := client.GetConfig()
|
|
cfg.SetMemPass("testpass") //nolint:errcheck
|
|
|
|
// --- STEP 1: initiator creates temp keypair and payload ---
|
|
initiator, cleanInit := setupIdentity(t, "alice")
|
|
defer cleanInit()
|
|
|
|
payload, initPeer, err := messages.Step1InitiatorCreatesInviteeAndTempKey("Bob", "Alice", "Hello!", nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, payload)
|
|
require.NotNil(t, initPeer)
|
|
// Initiator has only the temp keypair at this stage.
|
|
assert.Nil(t, initPeer.MyIdentity)
|
|
assert.NotNil(t, initPeer.InvitationKp)
|
|
|
|
// --- STEP 2: invitee receives payload, creates peer, returns packed message ---
|
|
_, cleanInvitee := setupIdentity(t, "bob")
|
|
defer cleanInvitee()
|
|
|
|
packed, inviteePeer, err := messages.Step2InviteeCreatesInitiatorAndEncryptedContactCard(
|
|
payload, "Alice", "Bob", nil,
|
|
)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, packed, "step2 must return a PackedUserMessage, not just a peer")
|
|
require.NotNil(t, inviteePeer)
|
|
|
|
// The packed message destination is the initiator's temp key (used as lookup key).
|
|
assert.Equal(t, payload.PublicKey, packed.Destination)
|
|
|
|
// The invitee peer has full keypairs now.
|
|
assert.NotNil(t, inviteePeer.MyIdentity)
|
|
assert.NotNil(t, inviteePeer.MyEncryptionKp)
|
|
assert.NotNil(t, inviteePeer.MyLookupKp)
|
|
|
|
// --- STEP 3: initiator decrypts invitee's packed message and finalises ---
|
|
cfg.SetIdentity(initiator)
|
|
|
|
// Simulate how the server delivers the step-2 answer: marshal the PackedUserMessage
|
|
// into an Invitation.Payload.
|
|
packedBytes, err := proto.Marshal(packed)
|
|
require.NoError(t, err)
|
|
|
|
invitation := &meowlib.Invitation{
|
|
Uuid: payload.Uuid,
|
|
Step: 2,
|
|
From: inviteePeer.MyIdentity.Public,
|
|
Payload: packedBytes,
|
|
}
|
|
|
|
peer, myCC, err := messages.Step3InitiatorFinalizesInviteeAndCreatesContactCard(invitation)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, peer)
|
|
require.NotNil(t, myCC, "step3 must produce the initiator's ContactCard to send to invitee")
|
|
|
|
// Initiator's peer must now hold invitee's real keys.
|
|
assert.Equal(t, inviteePeer.MyIdentity.Public, peer.ContactPublicKey)
|
|
assert.Equal(t, inviteePeer.MyEncryptionKp.Public, peer.ContactEncryption)
|
|
assert.Equal(t, inviteePeer.MyLookupKp.Public, peer.ContactLookupKey)
|
|
assert.Nil(t, peer.InvitationKp, "temp keypair must be cleared after step3")
|
|
assert.NotEmpty(t, myCC.DrRootKey)
|
|
assert.NotEmpty(t, myCC.DrPublicKey)
|
|
}
|
|
|
|
// TestStep2Step3RoundTripPayload verifies that the PackedUserMessage produced by step2
|
|
// actually carries the invitee's ContactCard when decrypted by the initiator.
|
|
func TestStep2Step3RoundTripPayload(t *testing.T) {
|
|
cfg := client.GetConfig()
|
|
cfg.SetMemPass("testpass") //nolint:errcheck
|
|
|
|
initiator, cleanInit := setupIdentity(t, "alice2")
|
|
defer cleanInit()
|
|
|
|
payload, _, err := messages.Step1InitiatorCreatesInviteeAndTempKey("Bob", "Alice", "", nil)
|
|
require.NoError(t, err)
|
|
|
|
_, cleanInvitee := setupIdentity(t, "bob2")
|
|
defer cleanInvitee()
|
|
|
|
packed, inviteePeer, err := messages.Step2InviteeCreatesInitiatorAndEncryptedContactCard(payload, "Alice", "Bob", nil)
|
|
require.NoError(t, err)
|
|
|
|
// Confirm the message serialises cleanly (transport simulation).
|
|
packedBytes, err := proto.Marshal(packed)
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, packedBytes)
|
|
|
|
// Switch back to initiator and run step3.
|
|
cfg.SetIdentity(initiator)
|
|
|
|
var roundTripped meowlib.PackedUserMessage
|
|
require.NoError(t, proto.Unmarshal(packedBytes, &roundTripped))
|
|
|
|
invitation := &meowlib.Invitation{
|
|
Uuid: payload.Uuid,
|
|
Step: 2,
|
|
From: inviteePeer.MyIdentity.Public,
|
|
Payload: packedBytes,
|
|
}
|
|
|
|
_, myCC, err := messages.Step3InitiatorFinalizesInviteeAndCreatesContactCard(invitation)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, myCC)
|
|
|
|
// The initiator's CC must reference the invitee's invitation ID so the invitee
|
|
// can match it when step4 arrives.
|
|
assert.Equal(t, payload.Uuid, myCC.InvitationId)
|
|
}
|