package client import ( "fmt" "strings" "time" "forge.redroom.link/yves/meowlib" "github.com/google/uuid" "google.golang.org/protobuf/proto" ) // Server manages server related operations // - Sending messages for server usage // - Two first steps of an invitation // - User message sending with UserKp identification // - Messages lookup requests // - Utility functions for packing/unpacking, encrypting/decrypting messages for server communication // - Server remote management if ManagerKp is available for that server type Server struct { //ServerCard meowlib.ServerCard `json:"server_data,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` PublicKey string `json:"public_key,omitempty"` Url string `json:"url,omitempty"` Login string `json:"login,omitempty"` Password string `json:"password,omitempty"` Presence bool `json:"presence,omitempty"` LastCheck time.Time `json:"last_check,omitempty"` Uptime time.Duration `json:"uptime,omitempty"` UserKp meowlib.KeyPair `json:"user_kp,omitempty"` ManagerKp meowlib.KeyPair `json:"manager_kp,omitempty"` Country string `json:"country,omitempty"` AllowedDelay int `json:"allowed_delay,omitempty"` Backup bool `json:"backup,omitempty"` } // CreateServerFromUrl creates a server from a basic url, ex : https://my.meowserver.example:8443/meow/ func CreateServerFromUrl(url string) *Server { var is Server is.Url = url return &is } // CreateServerFromUid creates a server from a uid string, ex : mylogin:mypassword@https://my.meowserver.example:8443/meow/ func CreateServerFromUid(uid string) *Server { var is Server uidTable := strings.Split(uid, "@") //! Weak test, use regexp if len(uidTable) == 2 { loginpw := strings.Split(uidTable[0], ":") is.Url = uidTable[1] is.Login = loginpw[0] is.Password = loginpw[1] } else { is.Url = uidTable[0] } return &is } // CreateServerFromMeowUrl creates a server from a meow url, ex : meow://mylogin:mypassword@https://my.meowserver.example:8443/meow/ func CreateServerFromMeowUrl(meowurl string) *Server { uid := strings.Replace(meowurl[7:], "//", "://", 1) return CreateServerFromUid(uid) } // CreateServerFromInvitationLink creates a server from a meow url, ex : meow://mylogin:mypassword@https://my.meowserver.example:8443/meow?invitationCode func CreateServerFromInvitationLink(meowurl string) *Server { // remove the invitation code, last token after a / meowurlTable := strings.Split(meowurl, "?") // join all elements with / except the last one meowSrvUrl := meowurlTable[0] return CreateServerFromMeowUrl(meowSrvUrl) } // GetServerCard returns a server card from a server func (ints *Server) GetServerCard() *meowlib.ServerCard { var sc meowlib.ServerCard sc.Name = ints.Name sc.PublicKey = ints.PublicKey sc.Description = ints.Description sc.Url = ints.Url sc.Login = ints.Login sc.Password = ints.Password return &sc } func (sc *Server) GetUid() string { if len(sc.Login) > 0 || len(sc.Password) > 0 { return sc.Login + ":" + sc.Password + "@" + sc.Url } return sc.Url } func (sc *Server) GetMeowUrl() string { if len(sc.Login) > 0 || len(sc.Password) > 0 { return sc.Login + ":" + sc.Password + "@" + sc.Url } return "meow://" + sc.Url } // Create a server from a server card func CreateServerFromServerCard(server *meowlib.ServerCard) *Server { var is Server is.Name = server.Name is.PublicKey = server.PublicKey is.Description = server.Description is.Url = server.Url is.Login = server.Login is.Password = server.Password is.UserKp = meowlib.NewKeyPair() return &is } // AsymEncryptMessage prepares a message to send to a specific internal server func (ints *Server) AsymEncryptMessage(Message []byte) (*meowlib.EncryptedMessage, error) { var enc *meowlib.EncryptedMessage enc, err := meowlib.AsymEncryptAndSign(ints.PublicKey, ints.UserKp.Private, Message) if err != nil { fmt.Println(err.Error()) return nil, err } return enc, err } // AsymDecryptMessage reads a message from a specific internal server func (ints *Server) AsymDecryptMessage(Message []byte, Signature []byte) (DecryptedMessage []byte, err error) { DecryptedMessage, err = meowlib.AsymDecryptAndCheck(ints.UserKp.Private, ints.PublicKey, Message, Signature) if err != nil { fmt.Println(err.Error()) return nil, err } return DecryptedMessage, err } // BuildToServerMessageFromUserMessage creates a basic message to server from a single packed user message and returns a meowlib.ToServerMessage func (ints *Server) BuildToServerMessageFromUserMessage(usermsg *meowlib.PackedUserMessage) *meowlib.ToServerMessage { var msg meowlib.ToServerMessage msg.Uuid = uuid.New().String() msg.Type = "1" msg.From = ints.UserKp.Public msg.Messages = append(msg.Messages, usermsg) return &msg } // BuildMessageSendingMessage creates a basic message to server from a single packed user message and returns it as protobuf serialized byte array func (ints *Server) BuildMessageSendingMessage(usermsg *meowlib.PackedUserMessage) ([]byte, error) { msg := ints.BuildToServerMessageFromUserMessage(usermsg) out, err := proto.Marshal(msg) if err != nil { return nil, err } return out, nil } // BuildMessageRequestMessage creates a message lookup message to server and returns it as protobuf serialized byte array func (ints *Server) BuildMessageRequestMessage(lookupKeys []string) ([]byte, error) { var msg meowlib.ToServerMessage msg.Uuid = uuid.New().String() msg.Type = "1" msg.From = ints.UserKp.Public out, err := proto.Marshal(&msg) if err != nil { return nil, err } return out, nil } // BuildToServerMessageInvitation creates an invitation message to server and returns it as a meowlib.ToServerMessage // it takes as input a contactcard generated by Identity.InvitePeer func (ints *Server) BuildToServerMessageInvitationCreation(invitation *meowlib.ContactCard, password string, timeout int, shortCodeLen int) (*meowlib.ToServerMessage, error) { var msg meowlib.ToServerMessage var inv meowlib.Invitation payload, err := invitation.Compress() if err != nil { return nil, err } msg.Type = "1" msg.From = ints.UserKp.Public inv.Step = 1 inv.Password = password inv.Timeout = int32(timeout) inv.ShortcodeLen = int32(shortCodeLen) inv.Payload = payload msg.Invitation = &inv return &msg, nil } // BuildToServerMessageInvitationRequest requests invitation with provided id from server and returns it as a meowlib.ToServerMessage func (ints *Server) BuildToServerMessageInvitationRequest(shortcode string, password string) (*meowlib.ToServerMessage, error) { var msg meowlib.ToServerMessage var inv meowlib.Invitation msg.Type = "1" msg.From = ints.UserKp.Public inv.Step = 2 inv.Password = password inv.Shortcode = shortcode msg.Invitation = &inv return &msg, nil } // BuildToServerMessageInvitationAnswer creates an invitation answer to server and returns it as a meowlib.ToServerMessage // it takes as input a contactcard generated by Identity.InvitePeer func (ints *Server) BuildToServerMessageInvitationAnswer(invitationAnswer *meowlib.PackedUserMessage, myPublicKeyForThatPeer string, invitation_id string, timeout int) (*meowlib.ToServerMessage, error) { var msg meowlib.ToServerMessage var inv meowlib.Invitation invitationPayload, err := proto.Marshal(invitationAnswer) if err != nil { return nil, err } inv.Step = 3 inv.Uuid = invitation_id msg.Type = "1" msg.From = ints.UserKp.Public inv.From = myPublicKeyForThatPeer inv.Payload = invitationPayload msg.Invitation = &inv return &msg, nil } // BuildToServerMessageInvitationAnswerRequest requests invitation answer with provided id from server and returns it as a meowlib.ToServerMessage func (ints *Server) BuildToServerMessageInvitationAnswerRequest(invitationId string) (*meowlib.ToServerMessage, error) { var msg meowlib.ToServerMessage var inv meowlib.Invitation msg.Type = "1" msg.From = ints.UserKp.Public inv.Step = 4 inv.Uuid = invitationId msg.Invitation = &inv return &msg, nil } // PackServerMessage func (ints *Server) PackServerMessage(payload []byte, signature []byte) (protoPackedMessage []byte, err error) { var msg meowlib.PackedServerMessage msg.From = ints.UserKp.Public msg.Payload = payload msg.Signature = signature out, err := proto.Marshal(&msg) if err != nil { return nil, err } return out, nil } func (ints *Server) UnPackServerMessage(protoPackedMessage []byte) (payload []byte, signature []byte, err error) { msg := &meowlib.PackedServerMessage{} if err := proto.Unmarshal(protoPackedMessage, msg); err != nil { return nil, nil, err } return msg.Payload, msg.Signature, nil } func (srv *Server) ProcessOutboundMessage(toServerMessage *meowlib.ToServerMessage) ([]byte, error) { byteToServerMessage, err := proto.Marshal(toServerMessage) if err != nil { return nil, err } // Encrypting it encToServer, err := srv.AsymEncryptMessage(byteToServerMessage) if err != nil { return nil, err } // Packing it protoPackedServerMsg, err := srv.PackServerMessage(encToServer.Data, encToServer.Signature) if err != nil { return nil, err } return protoPackedServerMsg, nil } func (srv *Server) ProcessInboundServerResponse(msg []byte) (*meowlib.FromServerMessage, error) { fsmsg := &meowlib.FromServerMessage{} payload, signature, err := srv.UnPackServerMessage(msg) if err != nil { return nil, err } decrypted, err := srv.AsymDecryptMessage(payload, signature) if err != nil { return nil, err } if err := proto.Unmarshal(decrypted, fsmsg); err != nil { return nil, err } return fsmsg, nil }