cleaner invitation step messages
This commit is contained in:
@@ -29,114 +29,108 @@ func setupIdentity(t *testing.T, nickname string) (*client.Identity, func()) {
|
||||
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) {
|
||||
// TestStep1ReturnsBinaryPayload verifies that Step1 returns non-empty bytes that
|
||||
// deserialise to a valid InvitationInitPayload, and that the pending peer is stored
|
||||
// with only a temp keypair (no real identity keys yet).
|
||||
func TestStep1ReturnsBinaryPayload(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)
|
||||
step1Bytes, err := messages.Step1InitiatorCreatesInviteeAndTempKey("Bob", "Alice", "Hello!", nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, payload)
|
||||
require.NotEmpty(t, step1Bytes)
|
||||
|
||||
var payload meowlib.InvitationInitPayload
|
||||
require.NoError(t, proto.Unmarshal(step1Bytes, &payload))
|
||||
assert.NotEmpty(t, payload.Uuid)
|
||||
assert.NotEmpty(t, payload.PublicKey)
|
||||
assert.Equal(t, "Alice", payload.Name)
|
||||
assert.Equal(t, "Hello!", payload.InvitationMessage)
|
||||
|
||||
// Peer is saved with temp keypair only — no real identity keys yet.
|
||||
initPeer := initiator.Peers.GetFromName("Bob")
|
||||
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")
|
||||
// TestFullInvitationFlow runs all four steps end-to-end, passing the binary output of
|
||||
// each step directly to the next, and verifies that both peers end up with each other's
|
||||
// real keys after the exchange completes.
|
||||
func TestFullInvitationFlow(t *testing.T) {
|
||||
cfg := client.GetConfig()
|
||||
cfg.SetMemPass("testpass") //nolint:errcheck
|
||||
|
||||
// --- STEP 1: initiator creates temp keypair, gets binary payload ---
|
||||
initiator, cleanInit := setupIdentity(t, "alice2")
|
||||
defer cleanInit()
|
||||
|
||||
step1Bytes, err := messages.Step1InitiatorCreatesInviteeAndTempKey("Bob", "Alice", "", nil)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, step1Bytes)
|
||||
|
||||
// --- STEP 2: invitee creates peer, returns serialized Invitation (step=2) ---
|
||||
invitee, cleanInvitee := setupIdentity(t, "bob2")
|
||||
defer cleanInvitee()
|
||||
|
||||
packed, inviteePeer, err := messages.Step2InviteeCreatesInitiatorAndEncryptedContactCard(
|
||||
payload, "Alice", "Bob", nil,
|
||||
)
|
||||
step2Bytes, err := messages.Step2InviteeCreatesInitiatorAndEncryptedContactCard(step1Bytes, "Alice", "Bob", nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, packed, "step2 must return a PackedUserMessage, not just a peer")
|
||||
require.NotEmpty(t, step2Bytes, "step2 must return non-empty invitation bytes")
|
||||
|
||||
// Invitee now has a peer with full keypairs.
|
||||
inviteePeer := invitee.Peers.GetFromName("Alice")
|
||||
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 ---
|
||||
// The step-2 wire format is a serialized Invitation.
|
||||
var inv2 meowlib.Invitation
|
||||
require.NoError(t, proto.Unmarshal(step2Bytes, &inv2))
|
||||
assert.EqualValues(t, 2, inv2.Step)
|
||||
assert.NotEmpty(t, inv2.Uuid)
|
||||
assert.Equal(t, inviteePeer.MyIdentity.Public, inv2.From)
|
||||
assert.NotEmpty(t, inv2.Payload)
|
||||
|
||||
// --- STEP 3: initiator decrypts invitee's card, returns serialized Invitation (step=3) ---
|
||||
cfg.SetIdentity(initiator)
|
||||
|
||||
// Simulate how the server delivers the step-2 answer: marshal the PackedUserMessage
|
||||
// into an Invitation.Payload.
|
||||
packedBytes, err := proto.Marshal(packed)
|
||||
step3Bytes, err := messages.Step3InitiatorFinalizesInviteeAndCreatesContactCard(step2Bytes)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, step3Bytes)
|
||||
|
||||
invitation := &meowlib.Invitation{
|
||||
Uuid: payload.Uuid,
|
||||
Step: 2,
|
||||
From: inviteePeer.MyIdentity.Public,
|
||||
Payload: packedBytes,
|
||||
}
|
||||
// Initiator's peer must now hold invitee's real keys; temp keypair must be gone.
|
||||
initPeer := initiator.Peers.GetFromName("Bob")
|
||||
require.NotNil(t, initPeer)
|
||||
assert.Equal(t, inviteePeer.MyIdentity.Public, initPeer.ContactPublicKey)
|
||||
assert.Equal(t, inviteePeer.MyEncryptionKp.Public, initPeer.ContactEncryption)
|
||||
assert.Equal(t, inviteePeer.MyLookupKp.Public, initPeer.ContactLookupKey)
|
||||
assert.Nil(t, initPeer.InvitationKp, "temp keypair must be cleared after step3")
|
||||
assert.NotEmpty(t, initPeer.DrKpPublic)
|
||||
assert.NotEmpty(t, initPeer.DrRootKey)
|
||||
|
||||
peer, myCC, err := messages.Step3InitiatorFinalizesInviteeAndCreatesContactCard(invitation)
|
||||
// The step-3 wire format is a serialized Invitation.
|
||||
var inv3 meowlib.Invitation
|
||||
require.NoError(t, proto.Unmarshal(step3Bytes, &inv3))
|
||||
assert.EqualValues(t, 3, inv3.Step)
|
||||
assert.NotEmpty(t, inv3.Uuid)
|
||||
assert.NotEmpty(t, inv3.Payload)
|
||||
|
||||
// --- STEP 4: invitee finalises initiator ---
|
||||
cfg.SetIdentity(invitee)
|
||||
|
||||
finalPeer, err := messages.Step4InviteeFinalizesInitiator(step3Bytes)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, peer)
|
||||
require.NotNil(t, myCC, "step3 must produce the initiator's ContactCard to send to invitee")
|
||||
require.NotNil(t, finalPeer)
|
||||
|
||||
// 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)
|
||||
// Invitee's peer must now hold initiator's real keys and the invitation must be complete.
|
||||
assert.Equal(t, initPeer.MyIdentity.Public, finalPeer.ContactPublicKey)
|
||||
assert.Equal(t, initPeer.MyEncryptionKp.Public, finalPeer.ContactEncryption)
|
||||
assert.Equal(t, initPeer.MyLookupKp.Public, finalPeer.ContactLookupKey)
|
||||
assert.False(t, finalPeer.InvitationPending(), "invitation must be fully finalized")
|
||||
assert.NotEmpty(t, finalPeer.DrRootKey)
|
||||
assert.NotEmpty(t, finalPeer.ContactDrPublicKey)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user