refactor invitation
This commit is contained in:
BIN
Unnamed.FCStd
Normal file
BIN
Unnamed.FCStd
Normal file
Binary file not shown.
@@ -11,6 +11,8 @@ import (
|
|||||||
|
|
||||||
"forge.redroom.link/yves/meowlib"
|
"forge.redroom.link/yves/meowlib"
|
||||||
"forge.redroom.link/yves/meowlib/client"
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
invmsgs "forge.redroom.link/yves/meowlib/client/invitation/messages"
|
||||||
|
invsrv "forge.redroom.link/yves/meowlib/client/invitation/server"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
@@ -131,10 +133,10 @@ func ConsumeInboxFile(messageFilename string) ([]string, []string, string, error
|
|||||||
}
|
}
|
||||||
// check if invitation answer (step-2 answer waiting for the initiator)
|
// check if invitation answer (step-2 answer waiting for the initiator)
|
||||||
if fromServerMessage.Invitation != nil {
|
if fromServerMessage.Invitation != nil {
|
||||||
peer, _, _, invErr := InvitationStep3ProcessAnswer(fromServerMessage.Invitation)
|
peer, _, invErr := invmsgs.Step3InitiatorFinalizesInviteeAndCreatesContactCard(fromServerMessage.Invitation)
|
||||||
if invErr == nil && peer != nil {
|
if invErr == nil && peer != nil {
|
||||||
// Auto-send step-3 CC to invitee's servers.
|
// Auto-send step-3 CC to invitee's servers.
|
||||||
msgs, _, sendErr := InvitationStep3Message(peer.InvitationId)
|
msgs, sendErr := invsrv.Step3PostCard(peer.InvitationId)
|
||||||
if sendErr == nil {
|
if sendErr == nil {
|
||||||
for i, bytemsg := range msgs {
|
for i, bytemsg := range msgs {
|
||||||
if i < len(peer.ContactPullServers) {
|
if i < len(peer.ContactPullServers) {
|
||||||
@@ -167,10 +169,10 @@ func ConsumeInboxFile(messageFilename string) ([]string, []string, string, error
|
|||||||
|
|
||||||
// Handle invitation step 3: initiator's full ContactCard arriving at the invitee.
|
// Handle invitation step 3: initiator's full ContactCard arriving at the invitee.
|
||||||
if usermsg.Invitation != nil && usermsg.Invitation.Step == 3 {
|
if usermsg.Invitation != nil && usermsg.Invitation.Step == 3 {
|
||||||
finalizedPeer, _, finalErr := InvitationStep4ProcessStep3(usermsg)
|
finalizedPeer, finalErr := invmsgs.Step4InviteeFinalizesInitiator(usermsg)
|
||||||
if finalErr == nil && finalizedPeer != nil {
|
if finalErr == nil && finalizedPeer != nil {
|
||||||
// Auto-send step-4 confirmation to initiator's servers.
|
// Auto-send step-4 confirmation to initiator's servers.
|
||||||
step4msgs, _, sendErr := InvitationStep4Message(finalizedPeer.InvitationId)
|
step4msgs, sendErr := invsrv.Step4PostConfirmation(finalizedPeer.InvitationId)
|
||||||
if sendErr == nil {
|
if sendErr == nil {
|
||||||
for i, bytemsg := range step4msgs {
|
for i, bytemsg := range step4msgs {
|
||||||
if i < len(finalizedPeer.ContactPullServers) {
|
if i < len(finalizedPeer.ContactPullServers) {
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
package helpers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"forge.redroom.link/yves/meowlib"
|
|
||||||
"forge.redroom.link/yves/meowlib/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InvitationStep2Answer creates the invitee's peer from an InvitationInitPayload and returns
|
|
||||||
// the new peer (STEP_2, invitee side — in-memory, no server involved).
|
|
||||||
func InvitationStep2Answer(payload *meowlib.InvitationInitPayload, nickname string, myNickname string, serverUids []string) (*client.Peer, string, error) {
|
|
||||||
mynick := myNickname
|
|
||||||
if myNickname == "" {
|
|
||||||
mynick = client.GetConfig().GetIdentity().Nickname
|
|
||||||
}
|
|
||||||
peer, err := client.GetConfig().GetIdentity().InvitationStep2(mynick, nickname, serverUids, payload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2Answer: InvitationStep2", err
|
|
||||||
}
|
|
||||||
client.GetConfig().GetIdentity().Save()
|
|
||||||
return peer, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationStep2AnswerFile reads an InvitationInitPayload from a .mwiv file and creates the
|
|
||||||
// invitee's peer. It also writes the invitee's ContactCard response to a file (STEP_2_SEND, file variant).
|
|
||||||
func InvitationStep2AnswerFile(invitationFile string, nickname string, myNickname string, serverUids []string) (string, error) {
|
|
||||||
if _, err := os.Stat(invitationFile); os.IsNotExist(err) {
|
|
||||||
return "InvitationStep2AnswerFile: os.Stat", err
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(invitationFile, ".mwiv") {
|
|
||||||
return "InvitationStep2AnswerFile: unsupported format", errors.New("only .mwiv files are supported")
|
|
||||||
}
|
|
||||||
data, err := os.ReadFile(invitationFile)
|
|
||||||
if err != nil {
|
|
||||||
return "InvitationStep2AnswerFile: os.ReadFile", err
|
|
||||||
}
|
|
||||||
payload, err := meowlib.NewInvitationInitPayloadFromCompressed(data)
|
|
||||||
if err != nil {
|
|
||||||
return "InvitationStep2AnswerFile: NewInvitationInitPayloadFromCompressed", err
|
|
||||||
}
|
|
||||||
|
|
||||||
mynick := myNickname
|
|
||||||
if myNickname == "" {
|
|
||||||
mynick = client.GetConfig().GetIdentity().Nickname
|
|
||||||
}
|
|
||||||
c := client.GetConfig()
|
|
||||||
response, err := c.GetIdentity().InvitationStep2(mynick, nickname, serverUids, payload)
|
|
||||||
if err != nil {
|
|
||||||
return "InvitationStep2AnswerFile: InvitationStep2", err
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := c.StoragePath + string(os.PathSeparator) + mynick + "-" + nickname + ".mwiv"
|
|
||||||
if err := response.GetMyContact().WriteCompressed(filename); err != nil {
|
|
||||||
return "InvitationStep2AnswerFile: WriteCompressed", err
|
|
||||||
}
|
|
||||||
c.GetIdentity().Save()
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationStep2AnswerMessage builds and returns the packed server message that posts the
|
|
||||||
// invitee's ContactCard (encrypted with the initiator's temp key) to the invitation server
|
|
||||||
// (STEP_2_SEND, through-server variant).
|
|
||||||
func InvitationStep2AnswerMessage(invitationId string, invitationServerUid string, timeout int) ([]byte, string, error) {
|
|
||||||
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitationId)
|
|
||||||
if peer == nil {
|
|
||||||
return nil, "InvitationStep2AnswerMessage: peer not found", errors.New("no peer with that invitation id")
|
|
||||||
}
|
|
||||||
|
|
||||||
answermsg, err := peer.BuildInvitationStep2Message(peer.GetMyContact())
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2AnswerMessage: BuildInvitationStep2Message", err
|
|
||||||
}
|
|
||||||
|
|
||||||
invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2AnswerMessage: LoadServer", err
|
|
||||||
}
|
|
||||||
|
|
||||||
packedMsg, err := peer.ProcessOutboundUserMessage(answermsg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2AnswerMessage: ProcessOutboundUserMessage", err
|
|
||||||
}
|
|
||||||
|
|
||||||
toServerMessage, err := invitationServer.BuildToServerMessageInvitationAnswer(packedMsg, peer.MyIdentity.Public, invitationId, timeout)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2AnswerMessage: BuildToServerMessageInvitationAnswer", err
|
|
||||||
}
|
|
||||||
|
|
||||||
bytemsg, err := invitationServer.ProcessOutboundMessage(toServerMessage)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2AnswerMessage: ProcessOutboundMessage", err
|
|
||||||
}
|
|
||||||
return bytemsg, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationStep2AnswerMessageReadResponse reads the server acknowledgement of a Step2 answer.
|
|
||||||
func InvitationStep2AnswerMessageReadResponse(invitationData []byte, invitationServerUid string) (*meowlib.Invitation, string, error) {
|
|
||||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2AnswerMessageReadResponse: LoadServer", err
|
|
||||||
}
|
|
||||||
serverMsg, err := server.ProcessInboundServerResponse(invitationData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2AnswerMessageReadResponse: ProcessInboundServerResponse", err
|
|
||||||
}
|
|
||||||
return serverMsg.Invitation, "", nil
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package helpers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"forge.redroom.link/yves/meowlib"
|
|
||||||
"forge.redroom.link/yves/meowlib/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InvitationStep2GetMessage builds and returns the packed server message that retrieves
|
|
||||||
// the InvitationInitPayload from the server using the shortcode URL (STEP_2, invitee side).
|
|
||||||
func InvitationStep2GetMessage(invitationUrl string, serverPublicKey string, invitationPassword string) ([]byte, string, error) {
|
|
||||||
meowurl := strings.Split(invitationUrl, "?")
|
|
||||||
shortcode := meowurl[1]
|
|
||||||
|
|
||||||
srv, err := client.CreateServerFromMeowUrl(meowurl[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2GetMessage: CreateServerFromMeowUrl", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reuse the server entry if already known.
|
|
||||||
dbsrv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(srv.Url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2GetMessage: LoadServer", err
|
|
||||||
}
|
|
||||||
if dbsrv == nil {
|
|
||||||
srv.PublicKey = serverPublicKey
|
|
||||||
k, err := meowlib.NewKeyPair()
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2GetMessage: NewKeyPair", err
|
|
||||||
}
|
|
||||||
srv.UserKp = k
|
|
||||||
if err := client.GetConfig().GetIdentity().MessageServers.StoreServer(srv); err != nil {
|
|
||||||
return nil, "InvitationStep2GetMessage: StoreServer", err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if dbsrv.PublicKey != serverPublicKey {
|
|
||||||
dbsrv.PublicKey = serverPublicKey
|
|
||||||
}
|
|
||||||
srv = dbsrv
|
|
||||||
}
|
|
||||||
|
|
||||||
toSrvMsg, err := srv.BuildToServerMessageInvitationRequest(shortcode, invitationPassword)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2GetMessage: BuildToServerMessageInvitationRequest", err
|
|
||||||
}
|
|
||||||
bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2GetMessage: ProcessOutboundMessage", err
|
|
||||||
}
|
|
||||||
return bytemsg, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationStep2ReadResponse decodes the server response to a Step2 get-message and returns
|
|
||||||
// the InvitationInitPayload sent by the initiator.
|
|
||||||
func InvitationStep2ReadResponse(invitationData []byte, invitationServerUid string) (*meowlib.InvitationInitPayload, string, error) {
|
|
||||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2ReadResponse: LoadServer", err
|
|
||||||
}
|
|
||||||
serverMsg, err := server.ProcessInboundServerResponse(invitationData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2ReadResponse: ProcessInboundServerResponse", err
|
|
||||||
}
|
|
||||||
payload, err := meowlib.NewInvitationInitPayloadFromCompressed(serverMsg.Invitation.Payload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep2ReadResponse: NewInvitationInitPayloadFromCompressed", err
|
|
||||||
}
|
|
||||||
return payload, "", nil
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
package helpers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"forge.redroom.link/yves/meowlib"
|
|
||||||
"forge.redroom.link/yves/meowlib/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InvitationStep1CreatePeer creates a minimal pending peer and returns the InvitationInitPayload
|
|
||||||
// to be transmitted to the invitee (STEP_1).
|
|
||||||
func InvitationStep1CreatePeer(contactName string, myNickname string, invitationMessage string, serverUids []string) (*meowlib.InvitationInitPayload, *client.Peer, string, error) {
|
|
||||||
mynick := myNickname
|
|
||||||
if myNickname == "" {
|
|
||||||
mynick = client.GetConfig().GetIdentity().Nickname
|
|
||||||
}
|
|
||||||
payload, peer, err := client.GetConfig().GetIdentity().InvitationStep1(mynick, contactName, serverUids, invitationMessage)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, "InvitationStep1CreatePeer: InvitationStep1", err
|
|
||||||
}
|
|
||||||
client.GetConfig().GetIdentity().Save()
|
|
||||||
return payload, peer, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationStep1File creates a pending peer and writes the InvitationInitPayload to a file
|
|
||||||
// (format: "qr" for QR-code PNG, anything else for compressed binary .mwiv).
|
|
||||||
func InvitationStep1File(contactName string, myNickname string, invitationMessage string, serverUids []string, format string) (*client.Peer, string, error) {
|
|
||||||
payload, peer, errdata, err := InvitationStep1CreatePeer(contactName, myNickname, invitationMessage, serverUids)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdata, err
|
|
||||||
}
|
|
||||||
c := client.GetConfig()
|
|
||||||
if format == "qr" {
|
|
||||||
filename := c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".png"
|
|
||||||
if err := payload.WriteQr(filename); err != nil {
|
|
||||||
return nil, "InvitationStep1File: WriteQr", err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
filename := c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".mwiv"
|
|
||||||
if err := payload.WriteCompressed(filename); err != nil {
|
|
||||||
return nil, "InvitationStep1File: WriteCompressed", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return peer, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationStep1Message builds and returns the packed server message that posts the
|
|
||||||
// InvitationInitPayload to the invitation server (STEP_1 through-server variant).
|
|
||||||
func InvitationStep1Message(invitationId string, invitationServerUid string, timeOut int, urlLen int, password string) ([]byte, string, error) {
|
|
||||||
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitationId)
|
|
||||||
if peer == nil {
|
|
||||||
return nil, "InvitationStep1Message: peer not found", nil
|
|
||||||
}
|
|
||||||
if peer.InvitationKp == nil {
|
|
||||||
return nil, "InvitationStep1Message: peer has no InvitationKp", nil
|
|
||||||
}
|
|
||||||
initPayload := &meowlib.InvitationInitPayload{
|
|
||||||
Uuid: peer.InvitationId,
|
|
||||||
Name: peer.MyName,
|
|
||||||
PublicKey: peer.InvitationKp.Public,
|
|
||||||
InvitationMessage: peer.InvitationMessage,
|
|
||||||
}
|
|
||||||
invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep1Message: LoadServer", err
|
|
||||||
}
|
|
||||||
msg, err := invitationServer.BuildToServerMessageInvitationStep1(initPayload, password, timeOut, urlLen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep1Message: BuildToServerMessageInvitationStep1", err
|
|
||||||
}
|
|
||||||
bytemsg, err := invitationServer.ProcessOutboundMessage(msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep1Message: ProcessOutboundMessage", err
|
|
||||||
}
|
|
||||||
return bytemsg, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationStep1ReadResponse reads the server response to a Step1 message (shortcode URL + expiry).
|
|
||||||
func InvitationStep1ReadResponse(invitationServerUid string, invitationResponse []byte) (*meowlib.Invitation, string, error) {
|
|
||||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep1ReadResponse: LoadServer", err
|
|
||||||
}
|
|
||||||
serverMsg, err := server.ProcessInboundServerResponse(invitationResponse)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep1ReadResponse: ProcessInboundServerResponse", err
|
|
||||||
}
|
|
||||||
return serverMsg.Invitation, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationSetUrlInfo stores the shortcode URL and expiry on the pending peer.
|
|
||||||
func InvitationSetUrlInfo(invitationId string, url string, expiry int64) {
|
|
||||||
id := client.GetConfig().GetIdentity()
|
|
||||||
peer := id.Peers.GetFromInvitationId(invitationId)
|
|
||||||
if peer == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
peer.InvitationUrl = url
|
|
||||||
peer.InvitationExpiry = time.Unix(expiry, 0)
|
|
||||||
id.Peers.StorePeer(peer)
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
package helpers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"forge.redroom.link/yves/meowlib"
|
|
||||||
"forge.redroom.link/yves/meowlib/client"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InvitationStep3ProcessAnswer is called by the initiator's background service when a
|
|
||||||
// step-2 answer (invitee's ContactCard) arrives via the invitation server poll.
|
|
||||||
// It decrypts the answer, calls InvitationStep3 to generate the initiator's full keypairs,
|
|
||||||
// and returns the peer and the initiator's ContactCard ready for STEP_3_SEND.
|
|
||||||
func InvitationStep3ProcessAnswer(invitation *meowlib.Invitation) (*client.Peer, *meowlib.ContactCard, string, error) {
|
|
||||||
var invitationAnswer meowlib.PackedUserMessage
|
|
||||||
if err := proto.Unmarshal(invitation.Payload, &invitationAnswer); err != nil {
|
|
||||||
return nil, nil, "InvitationStep3ProcessAnswer: Unmarshal PackedUserMessage", err
|
|
||||||
}
|
|
||||||
|
|
||||||
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitation.Uuid)
|
|
||||||
if peer == nil {
|
|
||||||
return nil, nil, "InvitationStep3ProcessAnswer: peer not found", errors.New("no peer for invitation uuid " + invitation.Uuid)
|
|
||||||
}
|
|
||||||
// Guard against duplicate delivery (e.g., same answer from multiple servers).
|
|
||||||
if peer.InvitationKp == nil {
|
|
||||||
return nil, nil, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt invitee's ContactCard using the initiator's temporary InvitationKp.
|
|
||||||
usermsg, err := peer.ProcessInboundStep2UserMessage(&invitationAnswer, invitation.From)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, "InvitationStep3ProcessAnswer: ProcessInboundStep2UserMessage", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var inviteeCC meowlib.ContactCard
|
|
||||||
if err := proto.Unmarshal(usermsg.Invitation.Payload, &inviteeCC); err != nil {
|
|
||||||
return nil, nil, "InvitationStep3ProcessAnswer: Unmarshal ContactCard", err
|
|
||||||
}
|
|
||||||
|
|
||||||
myCC, peer, err := client.GetConfig().GetIdentity().InvitationStep3(&inviteeCC)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, "InvitationStep3ProcessAnswer: InvitationStep3", err
|
|
||||||
}
|
|
||||||
client.GetConfig().GetIdentity().Save()
|
|
||||||
return peer, myCC, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationStep3Message builds and returns the packed server messages that send the
|
|
||||||
// initiator's full ContactCard to the invitee through the invitee's servers (STEP_3_SEND).
|
|
||||||
func InvitationStep3Message(invitationId string) ([][]byte, string, error) {
|
|
||||||
id := client.GetConfig().GetIdentity()
|
|
||||||
peer := id.Peers.GetFromInvitationId(invitationId)
|
|
||||||
if peer == nil {
|
|
||||||
return nil, "InvitationStep3Message: peer not found", errors.New("no peer for invitation id " + invitationId)
|
|
||||||
}
|
|
||||||
|
|
||||||
step3msg, err := peer.BuildInvitationStep3Message(peer.GetMyContact())
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep3Message: BuildInvitationStep3Message", err
|
|
||||||
}
|
|
||||||
// Step-3 must NOT use DR or sym layers: the invitee hasn't received those
|
|
||||||
// keys yet (they are carried inside this very message). Use plain asym only.
|
|
||||||
serialized, err := peer.SerializeUserMessage(step3msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep3Message: SerializeUserMessage", err
|
|
||||||
}
|
|
||||||
enc, err := peer.AsymEncryptMessage(serialized)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep3Message: AsymEncryptMessage", err
|
|
||||||
}
|
|
||||||
packedMsg := peer.PackUserMessage(enc.Data, enc.Signature)
|
|
||||||
|
|
||||||
var results [][]byte
|
|
||||||
for _, srvUid := range peer.ContactPullServers {
|
|
||||||
srv, err := id.MessageServers.LoadServer(srvUid)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
toSrvMsg := srv.BuildToServerMessageFromUserMessage(packedMsg)
|
|
||||||
bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
results = append(results, bytemsg)
|
|
||||||
}
|
|
||||||
if len(results) == 0 {
|
|
||||||
return nil, "InvitationStep3Message: no reachable invitee server", errors.New("could not build message for any invitee server")
|
|
||||||
}
|
|
||||||
return results, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationStep4ProcessStep3 is called by the invitee's message processing when a UserMessage
|
|
||||||
// with invitation.step==3 is received. It finalizes the initiator's peer entry.
|
|
||||||
func InvitationStep4ProcessStep3(usermsg *meowlib.UserMessage) (*client.Peer, string, error) {
|
|
||||||
if usermsg.Invitation == nil || usermsg.Invitation.Step != 3 {
|
|
||||||
return nil, "InvitationStep4ProcessStep3: unexpected step", errors.New("expected invitation step 3")
|
|
||||||
}
|
|
||||||
var initiatorCC meowlib.ContactCard
|
|
||||||
if err := proto.Unmarshal(usermsg.Invitation.Payload, &initiatorCC); err != nil {
|
|
||||||
return nil, "InvitationStep4ProcessStep3: Unmarshal ContactCard", err
|
|
||||||
}
|
|
||||||
// Patch the invitation ID from the outer message in case it was not set in the CC.
|
|
||||||
if initiatorCC.InvitationId == "" {
|
|
||||||
initiatorCC.InvitationId = usermsg.Invitation.Uuid
|
|
||||||
}
|
|
||||||
if err := client.GetConfig().GetIdentity().InvitationStep4(&initiatorCC); err != nil {
|
|
||||||
return nil, "InvitationStep4ProcessStep3: InvitationStep4", err
|
|
||||||
}
|
|
||||||
client.GetConfig().GetIdentity().Save()
|
|
||||||
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(initiatorCC.InvitationId)
|
|
||||||
return peer, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationStep4Message builds and returns the packed server messages that send the
|
|
||||||
// invitee's confirmation to the initiator through the initiator's servers (STEP_4).
|
|
||||||
func InvitationStep4Message(invitationId string) ([][]byte, string, error) {
|
|
||||||
id := client.GetConfig().GetIdentity()
|
|
||||||
peer := id.Peers.GetFromInvitationId(invitationId)
|
|
||||||
if peer == nil {
|
|
||||||
return nil, "InvitationStep4Message: peer not found", errors.New("no peer for invitation id " + invitationId)
|
|
||||||
}
|
|
||||||
|
|
||||||
step4msg, err := peer.BuildInvitationStep4Message()
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep4Message: BuildInvitationStep4Message", err
|
|
||||||
}
|
|
||||||
packedMsg, err := peer.ProcessOutboundUserMessage(step4msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "InvitationStep4Message: ProcessOutboundUserMessage", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var results [][]byte
|
|
||||||
for _, srvUid := range peer.ContactPullServers {
|
|
||||||
srv, err := id.MessageServers.LoadServer(srvUid)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
toSrvMsg := srv.BuildToServerMessageFromUserMessage(packedMsg)
|
|
||||||
bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
results = append(results, bytemsg)
|
|
||||||
}
|
|
||||||
if len(results) == 0 {
|
|
||||||
return nil, "InvitationStep4Message: no reachable initiator server", errors.New("could not build message for any initiator server")
|
|
||||||
}
|
|
||||||
return results, "", nil
|
|
||||||
}
|
|
||||||
30
client/invitation/files/step1.go
Normal file
30
client/invitation/files/step1.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package files
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
"forge.redroom.link/yves/meowlib/client/invitation/messages"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step1Write creates a pending peer and writes the InvitationInitPayload to a file.
|
||||||
|
// format: "qr" writes a QR-code PNG; anything else writes a compressed binary .mwiv file.
|
||||||
|
func Step1Write(contactName string, myNickname string, invitationMessage string, serverUids []string, format string) (*client.Peer, error) {
|
||||||
|
payload, peer, err := messages.Step1InitiatorCreatesInviteeAndTempKey(contactName, myNickname, invitationMessage, serverUids)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c := client.GetConfig()
|
||||||
|
if format == "qr" {
|
||||||
|
filename := c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".png"
|
||||||
|
if err := payload.WriteQr(filename); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filename := c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".mwiv"
|
||||||
|
if err := payload.WriteCompressed(filename); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peer, nil
|
||||||
|
}
|
||||||
44
client/invitation/files/step2.go
Normal file
44
client/invitation/files/step2.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package files
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"forge.redroom.link/yves/meowlib"
|
||||||
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
"forge.redroom.link/yves/meowlib/client/invitation/messages"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step2ReadAndAnswer reads an InvitationInitPayload from a .mwiv file, creates the
|
||||||
|
// invitee's peer entry, and writes the invitee's ContactCard response to a .mwiv file.
|
||||||
|
func Step2ReadAndAnswer(invitationFile string, nickname string, myNickname string, serverUids []string) error {
|
||||||
|
if _, err := os.Stat(invitationFile); os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(invitationFile, ".mwiv") {
|
||||||
|
return errors.New("only .mwiv files are supported")
|
||||||
|
}
|
||||||
|
data, err := os.ReadFile(invitationFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
payload, err := meowlib.NewInvitationInitPayloadFromCompressed(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mynick := myNickname
|
||||||
|
if mynick == "" {
|
||||||
|
mynick = client.GetConfig().GetIdentity().Nickname
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := messages.Step2InviteeCreatesInitiatorAndEncryptedContactCard(payload, nickname, mynick, serverUids)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := client.GetConfig()
|
||||||
|
filename := c.StoragePath + string(os.PathSeparator) + mynick + "-" + nickname + ".mwiv"
|
||||||
|
return response.GetMyContact().WriteCompressed(filename)
|
||||||
|
}
|
||||||
22
client/invitation/messages/step1.go
Normal file
22
client/invitation/messages/step1.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package messages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"forge.redroom.link/yves/meowlib"
|
||||||
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step1InitiatorCreatesInviteeAndTempKey creates a minimal pending peer and a temporary
|
||||||
|
// keypair, and returns the InvitationInitPayload to be transmitted to the invitee
|
||||||
|
// via any transport (file, QR, server…).
|
||||||
|
func Step1InitiatorCreatesInviteeAndTempKey(contactName string, myNickname string, invitationMessage string, serverUids []string) (*meowlib.InvitationInitPayload, *client.Peer, error) {
|
||||||
|
mynick := myNickname
|
||||||
|
if mynick == "" {
|
||||||
|
mynick = client.GetConfig().GetIdentity().Nickname
|
||||||
|
}
|
||||||
|
payload, peer, err := client.GetConfig().GetIdentity().InvitationStep1(mynick, contactName, serverUids, invitationMessage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
client.GetConfig().GetIdentity().Save()
|
||||||
|
return payload, peer, nil
|
||||||
|
}
|
||||||
22
client/invitation/messages/step2.go
Normal file
22
client/invitation/messages/step2.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package messages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"forge.redroom.link/yves/meowlib"
|
||||||
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step2InviteeCreatesInitiatorAndEncryptedContactCard creates the invitee's peer entry
|
||||||
|
// from an InvitationInitPayload and generates the encrypted ContactCard to be sent back
|
||||||
|
// to the initiator via any transport.
|
||||||
|
func Step2InviteeCreatesInitiatorAndEncryptedContactCard(payload *meowlib.InvitationInitPayload, nickname string, myNickname string, serverUids []string) (*client.Peer, error) {
|
||||||
|
mynick := myNickname
|
||||||
|
if mynick == "" {
|
||||||
|
mynick = client.GetConfig().GetIdentity().Nickname
|
||||||
|
}
|
||||||
|
peer, err := client.GetConfig().GetIdentity().InvitationStep2(mynick, nickname, serverUids, payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client.GetConfig().GetIdentity().Save()
|
||||||
|
return peer, nil
|
||||||
|
}
|
||||||
46
client/invitation/messages/step3.go
Normal file
46
client/invitation/messages/step3.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package messages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"forge.redroom.link/yves/meowlib"
|
||||||
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step3InitiatorFinalizesInviteeAndCreatesContactCard is called by the initiator when a
|
||||||
|
// step-2 answer (invitee's encrypted ContactCard) arrives. It decrypts the card, upgrades
|
||||||
|
// the invitee's peer entry with the real keys, and returns the initiator's own ContactCard
|
||||||
|
// ready to be sent to the invitee via any transport.
|
||||||
|
func Step3InitiatorFinalizesInviteeAndCreatesContactCard(invitation *meowlib.Invitation) (*client.Peer, *meowlib.ContactCard, error) {
|
||||||
|
var invitationAnswer meowlib.PackedUserMessage
|
||||||
|
if err := proto.Unmarshal(invitation.Payload, &invitationAnswer); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitation.Uuid)
|
||||||
|
if peer == nil {
|
||||||
|
return nil, nil, errors.New("no peer for invitation uuid " + invitation.Uuid)
|
||||||
|
}
|
||||||
|
// Guard against duplicate delivery (e.g., same answer from multiple servers).
|
||||||
|
if peer.InvitationKp == nil {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
usermsg, err := peer.ProcessInboundStep2UserMessage(&invitationAnswer, invitation.From)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var inviteeCC meowlib.ContactCard
|
||||||
|
if err := proto.Unmarshal(usermsg.Invitation.Payload, &inviteeCC); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
myCC, peer, err := client.GetConfig().GetIdentity().InvitationStep3(&inviteeCC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
client.GetConfig().GetIdentity().Save()
|
||||||
|
return peer, myCC, nil
|
||||||
|
}
|
||||||
32
client/invitation/messages/step4.go
Normal file
32
client/invitation/messages/step4.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package messages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"forge.redroom.link/yves/meowlib"
|
||||||
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step4InviteeFinalizesInitiator is called by the invitee's message processor when a
|
||||||
|
// UserMessage with invitation.step == 3 arrives. It unmarshals the initiator's ContactCard
|
||||||
|
// and completes the invitee's peer entry with the initiator's real keys.
|
||||||
|
func Step4InviteeFinalizesInitiator(usermsg *meowlib.UserMessage) (*client.Peer, error) {
|
||||||
|
if usermsg.Invitation == nil || usermsg.Invitation.Step != 3 {
|
||||||
|
return nil, errors.New("expected invitation step 3")
|
||||||
|
}
|
||||||
|
var initiatorCC meowlib.ContactCard
|
||||||
|
if err := proto.Unmarshal(usermsg.Invitation.Payload, &initiatorCC); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Patch the invitation ID from the outer message in case it was not set in the CC.
|
||||||
|
if initiatorCC.InvitationId == "" {
|
||||||
|
initiatorCC.InvitationId = usermsg.Invitation.Uuid
|
||||||
|
}
|
||||||
|
if err := client.GetConfig().GetIdentity().InvitationStep4(&initiatorCC); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client.GetConfig().GetIdentity().Save()
|
||||||
|
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(initiatorCC.InvitationId)
|
||||||
|
return peer, nil
|
||||||
|
}
|
||||||
61
client/invitation/server/step1.go
Normal file
61
client/invitation/server/step1.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"forge.redroom.link/yves/meowlib"
|
||||||
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step1Post builds and returns the packed server message that posts the
|
||||||
|
// InvitationInitPayload to the invitation server.
|
||||||
|
func Step1Post(invitationId string, invitationServerUid string, timeOut int, urlLen int, password string) ([]byte, error) {
|
||||||
|
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitationId)
|
||||||
|
if peer == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if peer.InvitationKp == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
initPayload := &meowlib.InvitationInitPayload{
|
||||||
|
Uuid: peer.InvitationId,
|
||||||
|
Name: peer.MyName,
|
||||||
|
PublicKey: peer.InvitationKp.Public,
|
||||||
|
InvitationMessage: peer.InvitationMessage,
|
||||||
|
}
|
||||||
|
invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg, err := invitationServer.BuildToServerMessageInvitationStep1(initPayload, password, timeOut, urlLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return invitationServer.ProcessOutboundMessage(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step1ReadResponse reads the server response to a Step1 post and returns the
|
||||||
|
// shortcode URL and expiry wrapped in an Invitation.
|
||||||
|
func Step1ReadResponse(invitationServerUid string, invitationResponse []byte) (*meowlib.Invitation, error) {
|
||||||
|
srv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
serverMsg, err := srv.ProcessInboundServerResponse(invitationResponse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return serverMsg.Invitation, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUrlInfo stores the shortcode URL and expiry on the pending peer.
|
||||||
|
func SetUrlInfo(invitationId string, url string, expiry int64) {
|
||||||
|
id := client.GetConfig().GetIdentity()
|
||||||
|
peer := id.Peers.GetFromInvitationId(invitationId)
|
||||||
|
if peer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
peer.InvitationUrl = url
|
||||||
|
peer.InvitationExpiry = time.Unix(expiry, 0)
|
||||||
|
id.Peers.StorePeer(peer)
|
||||||
|
}
|
||||||
107
client/invitation/server/step2.go
Normal file
107
client/invitation/server/step2.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"forge.redroom.link/yves/meowlib"
|
||||||
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step2Fetch builds and returns the packed server message that retrieves the
|
||||||
|
// InvitationInitPayload from the server using the shortcode URL.
|
||||||
|
func Step2Fetch(invitationUrl string, serverPublicKey string, invitationPassword string) ([]byte, error) {
|
||||||
|
meowurl := strings.Split(invitationUrl, "?")
|
||||||
|
shortcode := meowurl[1]
|
||||||
|
|
||||||
|
srv, err := client.CreateServerFromMeowUrl(meowurl[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reuse the server entry if already known.
|
||||||
|
dbsrv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(srv.Url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dbsrv == nil {
|
||||||
|
srv.PublicKey = serverPublicKey
|
||||||
|
k, err := meowlib.NewKeyPair()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
srv.UserKp = k
|
||||||
|
if err := client.GetConfig().GetIdentity().MessageServers.StoreServer(srv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if dbsrv.PublicKey != serverPublicKey {
|
||||||
|
dbsrv.PublicKey = serverPublicKey
|
||||||
|
}
|
||||||
|
srv = dbsrv
|
||||||
|
}
|
||||||
|
|
||||||
|
toSrvMsg, err := srv.BuildToServerMessageInvitationRequest(shortcode, invitationPassword)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return srv.ProcessOutboundMessage(toSrvMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step2ReadResponse decodes the server response to a Step2Fetch and returns
|
||||||
|
// the InvitationInitPayload sent by the initiator.
|
||||||
|
func Step2ReadResponse(invitationData []byte, invitationServerUid string) (*meowlib.InvitationInitPayload, error) {
|
||||||
|
srv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
serverMsg, err := srv.ProcessInboundServerResponse(invitationData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return meowlib.NewInvitationInitPayloadFromCompressed(serverMsg.Invitation.Payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step2PostAnswer builds and returns the packed server message that posts the
|
||||||
|
// invitee's ContactCard (encrypted with the initiator's temp key) to the invitation server.
|
||||||
|
func Step2PostAnswer(invitationId string, invitationServerUid string, timeout int) ([]byte, error) {
|
||||||
|
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitationId)
|
||||||
|
if peer == nil {
|
||||||
|
return nil, errors.New("no peer with that invitation id")
|
||||||
|
}
|
||||||
|
|
||||||
|
answermsg, err := peer.BuildInvitationStep2Message(peer.GetMyContact())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
packedMsg, err := peer.ProcessOutboundUserMessage(answermsg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
toServerMessage, err := invitationServer.BuildToServerMessageInvitationAnswer(packedMsg, peer.MyIdentity.Public, invitationId, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invitationServer.ProcessOutboundMessage(toServerMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step2PostAnswerReadResponse reads the server acknowledgement of a Step2PostAnswer.
|
||||||
|
func Step2PostAnswerReadResponse(invitationData []byte, invitationServerUid string) (*meowlib.Invitation, error) {
|
||||||
|
srv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
serverMsg, err := srv.ProcessInboundServerResponse(invitationData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return serverMsg.Invitation, nil
|
||||||
|
}
|
||||||
51
client/invitation/server/step3.go
Normal file
51
client/invitation/server/step3.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step3PostCard builds and returns the packed server messages that send the
|
||||||
|
// initiator's full ContactCard to the invitee through the invitee's servers.
|
||||||
|
// Step 3 must NOT use DR or sym layers: the invitee hasn't received those keys yet
|
||||||
|
// (they are carried inside this very message). Plain asym encryption is used.
|
||||||
|
func Step3PostCard(invitationId string) ([][]byte, error) {
|
||||||
|
id := client.GetConfig().GetIdentity()
|
||||||
|
peer := id.Peers.GetFromInvitationId(invitationId)
|
||||||
|
if peer == nil {
|
||||||
|
return nil, errors.New("no peer for invitation id " + invitationId)
|
||||||
|
}
|
||||||
|
|
||||||
|
step3msg, err := peer.BuildInvitationStep3Message(peer.GetMyContact())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
serialized, err := peer.SerializeUserMessage(step3msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
enc, err := peer.AsymEncryptMessage(serialized)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
packedMsg := peer.PackUserMessage(enc.Data, enc.Signature)
|
||||||
|
|
||||||
|
var results [][]byte
|
||||||
|
for _, srvUid := range peer.ContactPullServers {
|
||||||
|
srv, err := id.MessageServers.LoadServer(srvUid)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
toSrvMsg := srv.BuildToServerMessageFromUserMessage(packedMsg)
|
||||||
|
bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results = append(results, bytemsg)
|
||||||
|
}
|
||||||
|
if len(results) == 0 {
|
||||||
|
return nil, errors.New("could not build message for any invitee server")
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
44
client/invitation/server/step4.go
Normal file
44
client/invitation/server/step4.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"forge.redroom.link/yves/meowlib/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step4PostConfirmation builds and returns the packed server messages that send the
|
||||||
|
// invitee's confirmation to the initiator through the initiator's servers.
|
||||||
|
func Step4PostConfirmation(invitationId string) ([][]byte, error) {
|
||||||
|
id := client.GetConfig().GetIdentity()
|
||||||
|
peer := id.Peers.GetFromInvitationId(invitationId)
|
||||||
|
if peer == nil {
|
||||||
|
return nil, errors.New("no peer for invitation id " + invitationId)
|
||||||
|
}
|
||||||
|
|
||||||
|
step4msg, err := peer.BuildInvitationStep4Message()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
packedMsg, err := peer.ProcessOutboundUserMessage(step4msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var results [][]byte
|
||||||
|
for _, srvUid := range peer.ContactPullServers {
|
||||||
|
srv, err := id.MessageServers.LoadServer(srvUid)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
toSrvMsg := srv.BuildToServerMessageFromUserMessage(packedMsg)
|
||||||
|
bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results = append(results, bytemsg)
|
||||||
|
}
|
||||||
|
if len(results) == 0 {
|
||||||
|
return nil, errors.New("could not build message for any initiator server")
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user