meowlib/client/identity.go

336 lines
11 KiB
Go
Raw Normal View History

2022-09-06 09:30:45 +02:00
package client
2022-01-15 22:19:29 +01:00
import (
"encoding/json"
"errors"
2023-01-11 21:42:14 +01:00
"math/rand"
2022-12-02 23:18:13 +01:00
"os"
2024-01-12 23:17:34 +01:00
"path/filepath"
2023-01-11 21:42:14 +01:00
"time"
2022-01-15 22:19:29 +01:00
2022-09-06 09:30:45 +02:00
"forge.redroom.link/yves/meowlib"
2022-01-15 22:19:29 +01:00
"github.com/ProtonMail/gopenpgp/v2/helper"
"github.com/google/uuid"
2022-01-15 22:19:29 +01:00
)
2023-01-11 21:42:14 +01:00
const maxHiddenCount = 30
2021-10-18 21:05:44 +02:00
type Identity struct {
2023-11-08 22:01:44 +01:00
Nickname string `json:"nickname,omitempty"`
2023-12-26 17:51:42 +01:00
DefaultAvatar string `json:"default_avatar,omitempty"`
2023-11-08 22:01:44 +01:00
RootKp meowlib.KeyPair `json:"id_kp,omitempty"`
Status string `json:"status,omitempty"`
Peers PeerList `json:"peers,omitempty"`
HiddenPeers [][]byte `json:"hidden_peers,omitempty"`
2023-11-08 22:01:44 +01:00
Device meowlib.KeyPair `json:"device,omitempty"`
KnownServers ServerList `json:"known_servers,omitempty"`
2024-02-08 22:17:16 +01:00
MessageServers ServerStorage `json:"message_servers,omitempty"`
2023-11-08 22:01:44 +01:00
DefaultDbPassword string `json:"default_db_password,omitempty"`
DbPasswordStore bool `json:"db_password_store,omitempty"`
OwnedDevices PeerList `json:"owned_devices,omitempty"`
StaticMtkServerPaths []ServerList `json:"static_mtk_server_paths,omitempty"`
DynamicMtkServeRules []string `json:"dynamic_mtk_serve_rules,omitempty"`
InvitationTimeout int `json:"invitation_timeout,omitempty"`
2024-03-29 18:07:06 +01:00
Uuid string `json:"uuid,omitempty"`
2023-01-08 23:19:08 +01:00
unlockedHiddenPeers PeerList
2021-10-18 21:05:44 +02:00
}
2024-03-29 18:26:41 +01:00
func CreateIdentity(nickname string) (*Identity, error) {
2021-10-18 21:05:44 +02:00
var id Identity
2022-01-15 22:19:29 +01:00
id.Nickname = nickname
2024-03-29 18:07:06 +01:00
id.Uuid = uuid.New().String()
2022-09-06 17:07:35 +02:00
id.RootKp = meowlib.NewKeyPair()
GetConfig().me = &id
2024-02-08 22:17:16 +01:00
id.MessageServers = ServerStorage{DbFile: uuid.NewString()}
2023-01-11 21:42:14 +01:00
id.generateRandomHiddenStuff()
2024-03-29 18:26:41 +01:00
err := id.CreateFolder()
if err != nil {
return nil, err
}
return &id, nil
2021-10-18 21:05:44 +02:00
}
2024-03-29 18:07:06 +01:00
func (id *Identity) CreateFolder() error {
err := os.MkdirAll(filepath.Join(GetConfig().StoragePath, id.Uuid), 0700)
if err != nil {
return err
}
return nil
}
2024-02-08 22:17:16 +01:00
// 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) {
2021-10-18 21:05:44 +02:00
var peer Peer
peer.Uid = uuid.New().String()
peer.MyIdentity = meowlib.NewKeyPair()
peer.MyEncryptionKp = meowlib.NewKeyPair()
peer.MyLookupKp = meowlib.NewKeyPair()
2022-09-06 17:07:35 +02:00
peer.Name = ContactName
peer.InvitationId = uuid.New().String() // todo as param to identify then update url
2024-02-08 22:17:16 +01:00
/* if id.MessageServers.Servers == nil {
return nil, errors.New("no message servers defined in your identity")
2022-11-30 21:35:38 +01:00
}
2024-02-08 22:17:16 +01:00
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)
}*/
2024-02-10 15:05:02 +01:00
/* pullServers, err := id.MessageServers.LoadServerCardsFromUids(MessageServerUids)
if err != nil {
return nil, err
}*/
peer.MyPullServers = MessageServerUids
peer.MyName = MyName
peer.InvitationMessage = InvitationMessage
id.Peers = append(id.Peers, &peer)
2022-01-15 22:19:29 +01:00
return &peer, nil
2021-10-18 21:05:44 +02:00
}
2024-02-08 22:17:16 +01:00
// 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
}
}
// it's an invitation
return false, "", ReceivedContact.Name, ReceivedContact.InvitationMessage
}
2024-02-08 22:17:16 +01:00
// 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 {
var peer Peer
2024-01-03 23:11:23 +01:00
//var myContactCard meowlib.ContactCard
peer.Uid = uuid.New().String()
peer.MyIdentity = meowlib.NewKeyPair()
peer.MyEncryptionKp = meowlib.NewKeyPair()
peer.MyLookupKp = meowlib.NewKeyPair()
if ContactName != "" {
peer.Name = ContactName
} else {
peer.Name = ReceivedContact.Name
}
2024-02-10 22:36:25 +01:00
peer.ContactEncryption = ReceivedContact.EncryptionPublicKey
peer.ContactLookupKey = ReceivedContact.LookupPublicKey
peer.ContactPublicKey = ReceivedContact.ContactPublicKey
peer.InvitationId = ReceivedContact.InvitationId
peer.InvitationMessage = ReceivedContact.InvitationMessage
for srv := range ReceivedContact.PullServers {
peer.ContactPullServers = append(peer.ContactPullServers, ReceivedContact.PullServers[srv].GetUid())
}
2024-02-08 22:17:16 +01:00
/* for _, i := range MessageServerIdxs {
srv := id.MessageServers.Servers[i].GetServerCard()
peer.MyContact.PullServers = append(peer.MyContact.PullServers, srv)
2024-02-08 22:17:16 +01:00
}*/
2024-02-10 15:05:02 +01:00
/* srvCards, err := GetConfig().GetIdentity().MessageServers.LoadServerCardsFromUids(MessageServerIdxs)
if err != nil {
peer.MyContact.PullServers = srvCards
}*/
peer.MyPullServers = MessageServerIdxs
peer.MyName = MyName
2024-01-03 23:11:23 +01:00
peer.InvitationId = ReceivedContact.InvitationId
id.Peers = append(id.Peers, &peer)
return &peer
}
2024-02-08 22:17:16 +01:00
// Finalizes an invitation, returns nil if successful
func (id *Identity) FinalizeInvitation(ReceivedContact *meowlib.ContactCard) error {
for i, p := range id.Peers {
if p.InvitationId == ReceivedContact.InvitationId {
2024-02-10 22:36:25 +01:00
//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{}
2024-02-10 22:36:25 +01:00
for srv := range ReceivedContact.PullServers {
srvs = append(srvs, ReceivedContact.PullServers[srv].GetUid())
2024-02-10 22:36:25 +01:00
}
id.Peers[i].ContactPullServers = srvs
return nil
}
}
return errors.New("no matching contact found for invitationId " + ReceivedContact.InvitationId)
2021-10-18 21:05:44 +02:00
}
2024-02-08 22:17:16 +01:00
// LoadIdentity loads an identity from an encrypted file
func LoadIdentity(filename string, password string) (*Identity, error) {
2022-01-15 22:19:29 +01:00
var id Identity
GetConfig().memoryPassword = password
GetConfig().IdentityFile = filename
2022-12-02 23:18:13 +01:00
indata, err := os.ReadFile(filename)
2022-01-15 22:19:29 +01:00
if err != nil {
return nil, err
}
pass, err := helper.DecryptMessageWithPassword([]byte(password), string(indata))
2022-09-02 12:07:21 +02:00
if err != nil {
2022-01-15 22:19:29 +01:00
return nil, err
}
2022-09-02 12:07:21 +02:00
err = json.Unmarshal([]byte(pass), &id)
2024-02-08 22:17:16 +01:00
if err != nil {
return nil, err
}
GetConfig().me = &id
2022-01-15 22:19:29 +01:00
return &id, err
}
2021-10-18 21:05:44 +02:00
func (id *Identity) Save() error {
if GetConfig().IdentityFile == "" {
return errors.New("identity filename empty")
}
2022-01-15 22:19:29 +01:00
b, _ := json.Marshal(id)
armor, err := helper.EncryptMessageWithPassword([]byte(GetConfig().memoryPassword), string(b))
2022-01-15 22:19:29 +01:00
if err != nil {
return err
}
err = os.WriteFile(GetConfig().IdentityFile, []byte(armor), 0600)
2022-01-15 22:19:29 +01:00
return err
2021-10-18 21:05:44 +02:00
}
2023-01-08 23:19:08 +01:00
func (id *Identity) TryUnlockHidden(password string) error {
found := false
for _, encPeer := range id.HiddenPeers {
p := Peer{}
jsonPeer, err := meowlib.SymDecrypt(password, encPeer)
if err == nil {
err = json.Unmarshal(jsonPeer, &p)
if err != nil {
return err
}
2023-01-11 22:29:31 +01:00
p.dbPassword = password
id.unlockedHiddenPeers = append(id.unlockedHiddenPeers, &p)
2023-01-08 23:19:08 +01:00
found = true
}
}
if found {
return nil
}
return errors.New("no peer found")
}
2023-01-11 21:42:14 +01:00
func (id *Identity) HidePeer(peerIdx int, password string) error {
serializedPeer, err := json.Marshal(id.Peers[peerIdx])
if err != nil {
return err
}
encrypted, err := meowlib.SymEncrypt(password, serializedPeer)
if err != nil {
return err
}
// add encrypted peer data
id.HiddenPeers = append(id.HiddenPeers, encrypted)
// remove clear text peer
id.Peers = append(id.Peers[:peerIdx], id.Peers[peerIdx+1:]...)
return nil
}
func (id *Identity) generateRandomHiddenStuff() {
2024-01-12 23:17:34 +01:00
r := rand.New(rand.NewSource(time.Now().UnixNano()))
count := r.Intn(maxHiddenCount) + 1
2023-01-11 21:42:14 +01:00
for i := 1; i < count; i++ {
var p Peer
p.Name = randomLenString(4, 20)
p.MyEncryptionKp = meowlib.NewKeyPair()
p.MyIdentity = meowlib.NewKeyPair()
p.MyLookupKp = meowlib.NewKeyPair()
2024-02-10 22:36:25 +01:00
p.Name = randomLenString(4, 20)
p.ContactPublicKey = p.MyLookupKp.Public
p.ContactEncryption = p.MyIdentity.Public
p.ContactLookupKey = p.MyEncryptionKp.Public
// p.Contact.AddUrls([]string{randomLenString(14, 60), randomLenString(14, 60)}) // todo add servers
id.Peers = append(id.Peers, &p)
2023-01-11 21:42:14 +01:00
id.HidePeer(0, randomLenString(8, 14))
2024-01-12 23:17:34 +01:00
// TODO Add random conversations
2023-01-11 21:42:14 +01:00
}
}
type BackgroundJob struct {
RootPublic string `json:"root_public,omitempty"`
Device meowlib.KeyPair `json:"device,omitempty"`
Jobs []*RequestsJob `json:"jobs,omitempty"`
}
2024-01-12 23:17:34 +01:00
type RequestsJob struct {
2024-02-08 22:17:16 +01:00
Server *Server `json:"server,omitempty"`
LookupKeys []meowlib.KeyPair `json:"lookup_keys,omitempty"`
2024-01-12 23:17:34 +01:00
}
func (id *Identity) GetRequestJobs() []*RequestsJob {
var list []*RequestsJob
srvs := map[string]*RequestsJob{}
// get all servers
2024-02-08 22:17:16 +01:00
servers, err := id.MessageServers.LoadAllServers()
if err == nil {
// build a server map
2024-02-08 22:17:16 +01:00
for _, server := range servers {
var rj RequestsJob
rj.Server = server
srvs[server.GetServerCard().GetUid()] = &rj
2024-01-12 23:17:34 +01:00
}
2024-02-08 22:17:16 +01:00
// add ids to the map
for _, peer := range id.Peers {
// check if peer inviation is accepted
2024-02-08 22:17:16 +01:00
for _, server := range peer.MyPullServers {
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.MyLookupKp)
}
2024-01-12 23:17:34 +01:00
}
2024-02-08 22:17:16 +01:00
// add hidden peers
for _, peer := range id.unlockedHiddenPeers {
for _, server := range peer.MyPullServers {
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.MyLookupKp)
}
}
// todo add garbage
2024-01-12 23:17:34 +01:00
2024-02-08 22:17:16 +01:00
// todo random reorder
2024-01-12 23:17:34 +01:00
2024-02-08 22:17:16 +01:00
// build list
for _, srv := range srvs {
if len(srv.LookupKeys) > 0 {
list = append(list, srv)
}
2024-02-08 22:17:16 +01:00
}
2024-01-12 23:17:34 +01:00
}
return list
}
func (id *Identity) SaveBackgroundJob() error {
var bj BackgroundJob
bj.Jobs = id.GetRequestJobs()
bj.RootPublic = id.RootKp.Public
bj.Device = id.Device
jsonjobs, err := json.Marshal(bj)
2024-01-12 23:17:34 +01:00
if err != nil {
return err
}
2024-03-29 18:07:06 +01:00
id.CreateFolder()
err = os.WriteFile(filepath.Join(GetConfig().StoragePath, id.Uuid, ".jobs"), jsonjobs, 0600)
2024-01-12 23:17:34 +01:00
if err != nil {
return err
}
return nil
}
2023-01-11 21:42:14 +01:00
func randomLenString(min int, max int) string {
2024-01-12 23:17:34 +01:00
r := rand.New(rand.NewSource(time.Now().UnixNano()))
n := r.Intn(max-min) + min
2023-01-11 21:42:14 +01:00
return randomString(n)
}
func randomString(n int) string {
2024-01-12 23:17:34 +01:00
r := rand.New(rand.NewSource(time.Now().UnixNano()))
2023-01-11 21:42:14 +01:00
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
s := make([]rune, n)
for i := range s {
2024-01-12 23:17:34 +01:00
s[i] = letters[r.Intn(len(letters))]
2023-01-11 21:42:14 +01:00
}
return string(s)
}