invitation process upgrade
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
ycc
2026-04-02 18:50:04 +02:00
committed by yc
parent 9f130a80b7
commit 1906431061
21 changed files with 1185 additions and 638 deletions

View File

@@ -80,89 +80,44 @@ func (id *Identity) WipeFolder() error {
return nil
}
// Creates an invitation for a peer, returns the newly created peer including infos to provide a ContactCard
func (id *Identity) InvitePeer(MyName string, ContactName string, MessageServerUids []string, InvitationMessage string) (*Peer, error) {
// InvitationStep1 creates a minimal pending peer with only a temporary keypair and returns
// the InvitationInitPayload to be transmitted to the invitee (via file, QR code, or server).
// Full keypairs are only generated in InvitationStep3, after the invitee's answer is received.
func (id *Identity) InvitationStep1(MyName string, ContactName string, MessageServerUids []string, InvitationMessage string) (*meowlib.InvitationInitPayload, *Peer, error) {
var peer Peer
var err error
peer.Uid = uuid.New().String()
peer.MyIdentity, err = meowlib.NewKeyPair()
if err != nil {
return nil, err
}
peer.MyEncryptionKp, err = meowlib.NewKeyPair()
if err != nil {
return nil, err
}
peer.MyLookupKp, err = meowlib.NewKeyPair()
if err != nil {
return nil, err
}
peer.Name = ContactName
peer.InvitationId = uuid.New().String() // todo as param to identify then update url
symKeyBytes := make([]byte, 32)
if _, err = rand.Read(symKeyBytes); err != nil {
return nil, err
}
peer.MySymKey = base64.StdEncoding.EncodeToString(symKeyBytes)
/* if id.MessageServers.Servers == nil {
return nil, errors.New("no message servers defined in your identity")
}
for _, i := range MessageServerIdxs {
if i > len(id.MessageServers.Servers)-1 {
return nil, errors.New("requested server out of range of defined message servers")
}
}
for _, i := range MessageServerIdxs {
srv := id.MessageServers.Servers[i].GetServerCard()
peer.MyContact.PullServers = append(peer.MyContact.PullServers, srv)
}*/
/* pullServers, err := id.MessageServers.LoadServerCardsFromUids(MessageServerUids)
if err != nil {
return nil, err
}*/
peer.MyPullServers = MessageServerUids
peer.MyName = MyName
peer.InvitationId = uuid.New().String()
peer.InvitationMessage = InvitationMessage
peer.MyPullServers = MessageServerUids
// Generate DR keypair and root key for the initiator side
drKp, err := doubleratchet.DefaultCrypto{}.GenerateDH()
// Temporary keypair: public key is sent to invitee for step-2 encryption and as
// the server-side lookup key where the invitee will post their answer.
peer.InvitationKp, err = meowlib.NewKeyPair()
if err != nil {
return nil, err
return nil, nil, err
}
peer.DrKpPrivate = base64.StdEncoding.EncodeToString(drKp.PrivateKey())
peer.DrKpPublic = base64.StdEncoding.EncodeToString(drKp.PublicKey())
drRootKey := make([]byte, 32)
if _, err = rand.Read(drRootKey); err != nil {
return nil, err
}
peer.DrRootKey = base64.StdEncoding.EncodeToString(drRootKey)
peer.DrInitiator = true
id.Peers.StorePeer(&peer)
return &peer, nil
}
// Checks if the received contact card is an answer to an invitation, returns true if it is, and the proposed and received nicknames
func (id *Identity) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string, invitationMessage string) {
// invitation Id found, this is an answer to an invitation
/*for _, p := range id.Peers {
if p.InvitationId == ReceivedContact.InvitationId {
return true, p.Name, ReceivedContact.Name, ReceivedContact.InvitationMessage
}
payload := &meowlib.InvitationInitPayload{
Uuid: peer.InvitationId,
Name: MyName,
PublicKey: peer.InvitationKp.Public,
InvitationMessage: InvitationMessage,
}
// it's an invitation
return false, "", ReceivedContact.Name, ReceivedContact.InvitationMessage*/
return id.Peers.CheckInvitation(ReceivedContact)
return payload, &peer, nil
}
// Answers an invitation, returns the newly created peer including infos to provide a ContactCard
func (id *Identity) AnswerInvitation(MyName string, ContactName string, MessageServerIdxs []string, ReceivedContact *meowlib.ContactCard) (*Peer, error) {
// InvitationStep2 creates the invitee's peer entry from the received InvitationInitPayload
// and returns the peer. The invitee generates their full keypairs here.
// The initiator's temporary public key (payload.PublicKey) is used both as the encryption
// target for the step-2 answer and as the server-side lookup address.
func (id *Identity) InvitationStep2(MyName string, ContactName string, MessageServerUids []string, payload *meowlib.InvitationInitPayload) (*Peer, error) {
var peer Peer
var err error
var newsrv *Server
//var myContactCard meowlib.ContactCard
peer.Uid = uuid.New().String()
peer.MyIdentity, err = meowlib.NewKeyPair()
if err != nil {
@@ -179,66 +134,116 @@ func (id *Identity) AnswerInvitation(MyName string, ContactName string, MessageS
if ContactName != "" {
peer.Name = ContactName
} else {
peer.Name = ReceivedContact.Name
peer.Name = payload.Name
}
peer.ContactEncryption = ReceivedContact.EncryptionPublicKey
peer.ContactLookupKey = ReceivedContact.LookupPublicKey
peer.ContactPublicKey = ReceivedContact.ContactPublicKey
peer.MySymKey = ReceivedContact.SymetricKey
peer.InvitationId = ReceivedContact.InvitationId
peer.InvitationMessage = ReceivedContact.InvitationMessage
for srv := range ReceivedContact.PullServers {
peer.ContactPullServers = append(peer.ContactPullServers, ReceivedContact.PullServers[srv].GetUid())
newsrv, err = CreateServerFromUid(ReceivedContact.PullServers[srv].GetUid())
id.MessageServers.StoreServerIfNotExists(newsrv)
}
/* for _, i := range MessageServerIdxs {
srv := id.MessageServers.Servers[i].GetServerCard()
peer.MyContact.PullServers = append(peer.MyContact.PullServers, srv)
}*/
/* srvCards, err := GetConfig().GetIdentity().MessageServers.LoadServerCardsFromUids(MessageServerIdxs)
if err != nil {
peer.MyContact.PullServers = srvCards
}*/
peer.MyPullServers = MessageServerIdxs
// The initiator's temp key is used for both encrypting the answer and as destination.
peer.ContactEncryption = payload.PublicKey
peer.ContactLookupKey = payload.PublicKey
peer.InvitationId = payload.Uuid
peer.InvitationMessage = payload.InvitationMessage
peer.MyPullServers = MessageServerUids
peer.MyName = MyName
peer.InvitationId = ReceivedContact.InvitationId
// Adopt DR material from the initiator's ContactCard
peer.DrRootKey = ReceivedContact.DrRootKey
peer.ContactDrPublicKey = ReceivedContact.DrPublicKey
peer.DrInitiator = false
id.Peers.StorePeer(&peer)
return &peer, nil
}
// Finalizes an invitation, returns nil if successful
func (id *Identity) FinalizeInvitation(ReceivedContact *meowlib.ContactCard) error {
// InvitationStep3 is called by the initiator after receiving and decrypting the invitee's
// ContactCard (step-2 answer). It generates the initiator's full keypairs and DR material,
// updates the pending peer with the invitee's contact info, and returns the initiator's
// full ContactCard to be sent to the invitee (STEP_3_SEND).
func (id *Identity) InvitationStep3(inviteeContact *meowlib.ContactCard) (*meowlib.ContactCard, *Peer, error) {
var err error
var newsrv *Server
/*for i, p := range id.Peers {
if p.InvitationId == ReceivedContact.InvitationId {
//id.Peers[i].Name = ReceivedContact.Name
id.Peers[i].ContactEncryption = ReceivedContact.EncryptionPublicKey
id.Peers[i].ContactLookupKey = ReceivedContact.LookupPublicKey
id.Peers[i].ContactPublicKey = ReceivedContact.ContactPublicKey
srvs := []string{}
for srv := range ReceivedContact.PullServers {
srvs = append(srvs, ReceivedContact.PullServers[srv].GetUid())
}
id.Peers[i].ContactPullServers = srvs
return nil
peer := id.Peers.GetFromInvitationId(inviteeContact.InvitationId)
if peer == nil {
return nil, nil, errors.New("no pending peer found for invitation id " + inviteeContact.InvitationId)
}
// Generate full keypairs now that the invitee's identity is known.
peer.MyIdentity, err = meowlib.NewKeyPair()
if err != nil {
return nil, nil, err
}
peer.MyEncryptionKp, err = meowlib.NewKeyPair()
if err != nil {
return nil, nil, err
}
peer.MyLookupKp, err = meowlib.NewKeyPair()
if err != nil {
return nil, nil, err
}
symKeyBytes := make([]byte, 32)
if _, err = rand.Read(symKeyBytes); err != nil {
return nil, nil, err
}
peer.MySymKey = base64.StdEncoding.EncodeToString(symKeyBytes)
// Generate DR keypair and root key.
drKp, err := doubleratchet.DefaultCrypto{}.GenerateDH()
if err != nil {
return nil, nil, err
}
peer.DrKpPrivate = base64.StdEncoding.EncodeToString(drKp.PrivateKey())
peer.DrKpPublic = base64.StdEncoding.EncodeToString(drKp.PublicKey())
drRootKey := make([]byte, 32)
if _, err = rand.Read(drRootKey); err != nil {
return nil, nil, err
}
peer.DrRootKey = base64.StdEncoding.EncodeToString(drRootKey)
peer.DrInitiator = true
// Store invitee contact info.
peer.ContactPublicKey = inviteeContact.ContactPublicKey
peer.ContactEncryption = inviteeContact.EncryptionPublicKey
peer.ContactLookupKey = inviteeContact.LookupPublicKey
for _, srv := range inviteeContact.PullServers {
peer.ContactPullServers = append(peer.ContactPullServers, srv.GetUid())
newsrv, err := CreateServerFromUid(srv.GetUid())
if err == nil {
id.MessageServers.StoreServerIfNotExists(newsrv)
}
}
return errors.New("no matching contact found for invitationId " + ReceivedContact.InvitationId)*/
for srv := range ReceivedContact.PullServers {
newsrv, err = CreateServerFromUid(ReceivedContact.PullServers[srv].GetUid())
// Drop the temporary invitation keypair — no longer needed.
peer.InvitationKp = nil
id.Peers.StorePeer(peer)
return peer.GetMyContact(), peer, nil
}
// InvitationStep4 is called by the invitee upon receiving the initiator's full ContactCard
// (carried as a regular UserMessage with invitation.step=3). It finalizes the peer entry.
func (id *Identity) InvitationStep4(initiatorContact *meowlib.ContactCard) error {
var err error
var newsrv *Server
for _, srv := range initiatorContact.PullServers {
newsrv, err = CreateServerFromUid(srv.GetUid())
if err != nil {
return err
}
id.MessageServers.StoreServerIfNotExists(newsrv)
}
return id.Peers.FinalizeInvitation(ReceivedContact)
peer := id.Peers.GetFromInvitationId(initiatorContact.InvitationId)
if peer == nil {
return errors.New("no pending peer found for invitation id " + initiatorContact.InvitationId)
}
peer.ContactPublicKey = initiatorContact.ContactPublicKey
peer.ContactEncryption = initiatorContact.EncryptionPublicKey
peer.ContactLookupKey = initiatorContact.LookupPublicKey
peer.MySymKey = initiatorContact.SymetricKey
peer.DrRootKey = initiatorContact.DrRootKey
peer.ContactDrPublicKey = initiatorContact.DrPublicKey
peer.DrInitiator = false
for _, srv := range initiatorContact.PullServers {
peer.ContactPullServers = append(peer.ContactPullServers, srv.GetUid())
}
id.Peers.StorePeer(peer)
return nil
}
// CheckInvitation checks if the received ContactCard is an answer to one of our pending
// invitations. Returns true when it is, with the proposed and received nicknames.
func (id *Identity) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string, invitationMessage string) {
return id.Peers.CheckInvitation(ReceivedContact)
}
// LoadIdentity loads an identity from an encrypted file
@@ -386,9 +391,18 @@ func (id *Identity) GetRequestJobs() []RequestsJob {
return nil
}
for _, peer := range peers {
// check if peer inviation is accepted
for _, server := range peer.MyPullServers {
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.MyLookupKp)
if srvs[server] == nil {
continue
}
if peer.MyLookupKp != nil {
// Active peer — use the permanent lookup key.
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.MyLookupKp)
} else if peer.InvitationKp != nil {
// Step-1 pending peer — poll using the temp invitation keypair so the
// server-stored step-2 answer can be retrieved.
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.InvitationKp)
}
}
}
// add hidden peers