diff --git a/asymcrypt.go b/asymcrypt.go index 0728f6e..e39790f 100644 --- a/asymcrypt.go +++ b/asymcrypt.go @@ -66,41 +66,41 @@ func Decrypt(privateKey string, data []byte) ([]byte, error) { if err != nil { log.Error().Msg("Message decryption b64 failed") } - decrypted, err := helper.DecryptBinaryMessageArmored(string(priv), []byte(""), string(data)) + decrypted, err := helper.DecryptBinaryMessageArmored(string(priv), nil, string(data)) if err != nil { log.Error().Msg("Message decryption failed") } return []byte(decrypted), err } -func EncryptAndSign(publicKey string, privateKey string, data []byte) ([]byte, []byte, error) { - pub, err := base64.StdEncoding.DecodeString(publicKey) +func EncryptAndSign(publicEncKey string, privateSignKey string, data []byte) ([]byte, []byte, error) { + pub, err := base64.StdEncoding.DecodeString(publicEncKey) if err != nil { log.Error().Msg("Message encryption and sign b64 failed") } - priv, err := base64.StdEncoding.DecodeString(privateKey) + priv, err := base64.StdEncoding.DecodeString(privateSignKey) if err != nil { log.Error().Msg("Message encryption and sign b64 failed") } - armor, signature, err := helper.EncryptSignBinaryDetached(string(pub), string(priv), []byte(""), data) + encrypted, signature, err := helper.EncryptSignBinaryDetached(string(pub), string(priv), nil, data) if err != nil { log.Error().Msg("Message encryption and sign failed") } - return []byte(armor), []byte(signature), err + return []byte(encrypted), []byte(signature), err } -func DecryptAndSign(publicKey string, privateKey string, data []byte, signature []byte) ([]byte, error) { - pub, err := base64.StdEncoding.DecodeString(publicKey) +func DecryptAndCheck(MyPrivateEncryptionKey string, peerContactPublicKey string, data []byte, signature []byte) (DecryptedMessage []byte, err error) { + pub, err := base64.StdEncoding.DecodeString(MyPrivateEncryptionKey) if err != nil { log.Error().Msg("Message decryption and sign b64 failed") } - priv, err := base64.StdEncoding.DecodeString(privateKey) + priv, err := base64.StdEncoding.DecodeString(peerContactPublicKey) if err != nil { log.Error().Msg("Message decryption and sign b64 failed") } - decrypted, err := helper.DecryptVerifyBinaryDetached(string(pub), string(priv), []byte(""), data, string(signature)) + DecryptedMessage, err = helper.DecryptVerifyBinaryDetached(string(pub), string(priv), nil, data, string(signature)) if err != nil { log.Error().Msg("Message decryption and sign failed") } - return decrypted, err + return DecryptedMessage, err } diff --git a/asymcrypt_test.go b/asymcrypt_test.go index 8c407f2..d2c00f9 100644 --- a/asymcrypt_test.go +++ b/asymcrypt_test.go @@ -5,6 +5,8 @@ import ( "fmt" "log" "testing" + + "github.com/stretchr/testify/assert" ) func TestNewKeyPair(t *testing.T) { @@ -21,7 +23,36 @@ func TestGetKey(t *testing.T) { // fmt.Println(key.Armor()) Armpubkey, _ := key.GetArmoredPublicKey() pubkey := base64.StdEncoding.EncodeToString([]byte(Armpubkey)) - if kp.Public != pubkey { - log.Fatal("error in public key") - } + assert.Equal(t, kp.Public, pubkey, "The two public keys should be the same.") + //if kp.Public != pubkey { + // log.Fatal("error in public key") + //} +} + +func TestEncryptDecrypt(t *testing.T) { + kp := NewKeyPair() + foo := "totoaimelesfrites!" + encMess, err := Encrypt(kp.Public, []byte(foo)) + if err != nil { + log.Println(err.Error()) + } + decMess, err2 := Decrypt(kp.Private, encMess) + if err2 != nil { + log.Println(err2.Error()) + } + assert.Equal(t, foo, decMess, "The two messages should be the same.") +} + +func TestEncryptDecryptSigned(t *testing.T) { + kp := NewKeyPair() + foo := "totoaimelesfrites!" + encMess, sign, err := EncryptAndSign(kp.Public, kp.Private, []byte(foo)) + if err != nil { + log.Println(err.Error()) + } + decMess, err2 := DecryptAndCheck(kp.Private, kp.Public, encMess, sign) + if err2 != nil { + log.Println(err2.Error()) + } + assert.Equal(t, foo, string(decMess), "The two messages should be the same.") } diff --git a/doc/protocol.tex b/doc/protocol.tex index f7ea59b..5392d9e 100644 --- a/doc/protocol.tex +++ b/doc/protocol.tex @@ -35,12 +35,12 @@ No phone number or email check will be performed, unlike main instant messaging \subsubsection{Trustable server based communication} Like most widely available messaging softwares, (Whatsapp, Signal, Viber, Telegram...), \textffm{Meow} provides a simple server based messaging. -The main difference is that allows to explicitly choose which server you want to use. +The main difference is it allows to explicitly choose which server you want to use. The server code being open source, we strongly encourage you to run your own server at home or in your company. The server requires very few ressources and will run on any low cost single board computer. \subsubsection{Anonymized message transfer} -\textffm{Meow} also provides an anonymizing transfer services very similar to the Tor Onion protocol, we called it the Matriochka protocol. +\textffm{Meow} also provides an anonymizing transfer services very similar to the Tor Onion protocol, we call it the Matriochka protocol. Any server can be used for building the transfer chain. Some of them might be marked as trusted. It is strongly advised to use trusted servers as your first node and message server (the one that holds your incoming messages). @@ -51,16 +51,16 @@ The presence protocol is simply activated by setting a flag in the message poll If that flag is set, your encrypted IP will be published on the server, allowing your only your peer(s) to decrypt it and directly communicate with your terminal. \subsubsection{Peer based privacy settings} -You might define specific communication privacy preferences for each of your contacts : +You might define specific communication privacy preferences for each of your contacts: \begin{itemize} \item simple server based communication allowed for Joe, - \item direct communication prefered with Julian, fallback to my own server, - \item matriochka protocol required for Edward, first node is one of my trusted servers, my message node is my own server, randomly switch from trusted server lists for others. + \item preferred direct communication with Julian, fallback to my own server, + \item required matriochka protocol for Edward, first node is one of my trusted servers, my message node is my own server, randomly switch from trusted server lists for others. \item ... \end{itemize} \subsection{Multiple devices support} \textffm{Meow} allows you to be connected from multiple devices and offers chat synchronization capability. -A device might be revoqued anytime from an other any one. Proof of your identity (password or other) shall be provided in order to grant device revocation. +A device might be revoqued anytime from any other one. Proof of your identity (password or other) shall be provided in order to grant device revocation. \subsection{Adding contacts} If you want to add a new contact, keys and uuids will be generated, then a rendez-vous card will be created. @@ -68,8 +68,8 @@ That rendez-vous card might be sent by any trustable communication means, or pre In return your contact will provide the exact same data, encrypted with your public key and delivered to the address specified in the initial rendez-vous card. \subsection{Contacts forwarding} -Using the \textffm{Meow} protocol a user won't be able to forward your contact information without your consent. -Each user knows you as a different identity, thus forwarding a known identity to another user is meaningless. Any message to that identity signed by another user thna you would be discarded. +By using the \textffm{Meow} protocol a user won't be able to forward your contact information without your consent. +Each user knows you as a different identity, thus forwarding a known identity to another user is meaningless. Any message to that identity signed by another user than you would be discarded. \subsection{Group conversation} @@ -81,18 +81,18 @@ A local (server based) emergency broadcast service will be provided. It will pro \subsection{Public networks shortage resilience} -\textffm{Meow} may run without Internet connection, either on an isolated wifi access point, either on a meshed network of wifi routers or even via serial IOT transport layers (LoRa,...) +\textffm{Meow} may run without Internet connection, either on an isolated wifi access point, or on a meshed network of wifi routers or even via serial IOT transport layers (LoRa,...) \subsection{User directory service} -This service allows to restore a lost functionality of Internet historic chat services (like ICQ). You could simply set a "Free for chat" status that allowed other people to contact you, either randomly or based on a short description that you might provide. +This service allows to restore a lost functionality of Internet historic chat services (like ICQ). You could simply set a "Free for chat" status that would allow other people to contact you, either randomly or based on a short description that you might provide. Why providing that service while the internet is suffocating due to the abundance of social networks ?\\ -Well, that option offered a few advantages : +Well, that option offers a few advantages : \begin{itemize} - \item you're still an anonymous user chatting with other anonymous users. - \item no social network algorithm will select people that think/behave/vote/eat... just like you. Diversity makes a better world. - \item a smaller community of users, skilled enough to operate a \textffm{Meow} chat app... that might provide a first filter. + \item you are still an anonymous user chatting with other anonymous users; + \item no social network algorithm will select people that think/behave/vote/eat... just like you. Diversity makes a better world; + \item a smaller community of users, skilled enough to operate a \textffm{Meow} chat app... that might provide a first filter; It's a bit like in the old times, when people had to be able to start a win98 computer, connect it to internet, then download and install ICQ... If you lost some time in social networks, and experienced ICQ in the 2000's, you know what I mean. \end{itemize} @@ -104,45 +104,43 @@ Each \textffm{Meow} user has a unique identity. That identity is strictly privat Let's call that one the User Key Pair (Ukp) \subsection{Contact identity} -Each of your contacts will know you as a different identity, we'll call that one the Contact Key Pair (Ckp) -That contact Key Pair will not change once it's agreed between the two peerr : An initial key will be exchanged as part of the peer invitation process. -As other people myth have seen your key -This means that : +Each of your contacts will know you under a different identity, we'll call that one the Contact Key Pair (Ckp) +That contact Key Pair will not change once it's agreed between both peers: an initial key will be exchanged as part of the peer invitation process. +As other people might have seen your key, this means that : \begin{itemize} - \item none of your contacts will be able to forward your id to another person without your consent - \item any message to that Ckp, not signed by the user associated to it, will be discarded. + \item none of your contacts will be able to forward your id to another person without your consent; + \item any message to that Ckp, not signed by its associated user, will be discarded. \end{itemize} \subsection{Conversation encryption} -Each conversation with one of your contacts will be encrypted using an encryption keypair (Ekp) +Each conversation with one of your contacts will be encrypted using an encryption keypair (Ekp) allowing cyphering your conversation. The Ekp might be changed anytime by its owner and the new public key will be sent along the last message. -The Ekp is used to cypher your conversation. \subsection{Conversation lookup} A contact conversation Lookup Key Pair(Lkp) is also associated with your conversation. The Lkp public key is used to identify your conversation on a server. -the private key allows you to sign your request and prove the server that you are the legitimate recipient for a message. -This Lkp can be changed anytime by it's owner and the new public key will be sent along the last message. -The Lkp and the Ekp are only changed, once the change has beeen acknowledged by your contact. +The private key allows you to sign your request and prove the server that you are the legitimate recipient for a message. +This Lkp can be changed anytime by its owner and the new public key will be sent along the last message. +The Lkp and the Ekp are only changed once the change has beeen acknowledged by your contact. \subsection{Server identity} Each server has a Server key (Skp). That key allows you to cypher the messages that you're sending to the server. \subsection{Device identity} -Each device is identified by a key (Dkp), that device key allows you to perform secured exchanges between your devices for synchronization/revocation purposes. -Communication between devices is achieved using the same principle as the user to user communication. A device might be considered as any another user. The messages content are based on a synchronization protocol. +Each device is identified by a device key (Dkp) that allows you to perform secured exchanges between your devices for synchronization/revocation purposes. +Communication between devices is achieved using the same principle as the user to user communication. A device might be considered as any another user. The messages content is based on a synchronization protocol. \section{Contact management} \subsection{Adding a contact} Rendez-vous card, containing : \begin{itemize} - \item Your public key for that contact - \item An initial conversation public key for getting encrypted messages from that contact - \item An initial conversation uuid that you'll use to lookup for incoming messages on the servers - \item A list of your prefered message servers - \item A signature to prevent transmission of tampered data + \item Your public key for that contact; + \item An initial conversation public key for getting encrypted messages from that contact; + \item An initial conversation uuid that you'll use to lookup for incoming messages on the servers; + \item A list of your preferred message servers; + \item A signature to prevent transmission of tampered data. \end{itemize} \subsection{Sharing a contact} -If a user wants to forward one of his contacts to you, that will be handled as a double request : +If a user wants to forward one of his contacts to you, it will be handled as a double request: \begin{enumerate} \item I'm receiving a contact name, without any key \item @@ -164,7 +162,7 @@ TODO \section{Transport protocols} \subsection{URLs} Server urls do define the protocol used for communicating with the server. -Some of the protocols will be described hereafter, but that list is not exhaustive, and might be extended in the future.\\ +Some of the protocols will be described hereafter, but that list is not exhaustive and might be extended in the future.\\ Examples of a valid url: \begin{verbatim} http://myserver.com diff --git a/endtoend_test.go b/endtoend_test.go index 30ca649..c554b68 100644 --- a/endtoend_test.go +++ b/endtoend_test.go @@ -12,7 +12,7 @@ func TestEndToEnd(t *testing.T) { // Create my own identity // fmt.Println("Trying to load identity from file.") - me, err := LoadIdentity("test.id") + me, err := LoadIdentity("id.enc") if err != nil { fmt.Println("Failed : creating New identity...") me = CreateIdentity("myname") @@ -36,6 +36,8 @@ func TestEndToEnd(t *testing.T) { // Simulate peer invitation response : generate the friend's keypair fmt.Println("Simulating first friend answer...") var receivedContact ContactCard + + // Friend simulated invitation firstFriendContactKp := NewKeyPair() firstFriendEncryptionKp := NewKeyPair() firstFriendLookupKp := NewKeyPair() @@ -45,9 +47,12 @@ func TestEndToEnd(t *testing.T) { receivedContact.LookupPublicKey = firstFriendLookupKp.Public var friendsMessageServers ServerList friendsMessageServers.AddUrls([]string{"http://myfriend.org/meow/"}) + // end Friend simulated invitation + for _, srv := range friendsMessageServers.Servers { receivedContact.PullServers = append(receivedContact.PullServers, srv.ServerData) } + // End simulating contact invitation response // @@ -55,7 +60,7 @@ func TestEndToEnd(t *testing.T) { // Finalize the contact with the invitation response // me.FinalizeInvitation(myFirstFriend, &receivedContact) - err = me.Save("test.id") + err = me.Save("id.enc") if err != nil { fmt.Println(err.Error()) } @@ -63,10 +68,27 @@ func TestEndToEnd(t *testing.T) { a, _ = json.Marshal(me) ioutil.WriteFile("id.json", a, 0644) fmt.Println(string(a)) - } - // go me.CheckMessages() - //myFirstFriend.SetComMethod() - //msg := myFirstFriend.SendText() + // create message to simulated friend + sentmessage := "Hello friend!" + lookupK, EncMsg, MsgSignature, Servers, err := myFirstFriend.CreateMessage([]byte(sentmessage)) + if err != nil { + fmt.Println(err.Error()) + } + fmt.Println(lookupK) + fmt.Println(len(Servers)) + // simulated friend decoding the message + //ReadMessage + + // simulates if peer can decrypt my message + //Message := "toto" + //Signature := "test" + decMess, err2 := DecryptAndCheck(myFirstFriend.EncryptionKp.Private, myFirstFriend.Contact.EncryptionPublicKey, []byte(EncMsg), MsgSignature) + if err2 != nil { + fmt.Println(err2.Error()) + } + fmt.Println(decMess) + // + } } diff --git a/go.mod b/go.mod index f07383c..cda29ec 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,10 @@ go 1.16 require ( github.com/ProtonMail/gopenpgp/v2 v2.2.4 github.com/go-resty/resty/v2 v2.6.0 + github.com/golang/protobuf v1.5.2 // indirect github.com/google/uuid v1.3.0 github.com/makiuchi-d/gozxing v0.1.1 github.com/rs/zerolog v1.25.0 github.com/stretchr/testify v1.4.0 - google.golang.org/protobuf v1.27.1 + google.golang.org/protobuf v1.28.1 ) diff --git a/go.sum b/go.sum index a2a9892..57516f1 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/go-resty/resty/v2 v2.6.0 h1:joIR5PNLM2EFqqESUjCMGXrWmXNHEU9CEiK813oKY github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -82,8 +84,11 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/https.go b/https.go deleted file mode 100644 index c3aabff..0000000 --- a/https.go +++ /dev/null @@ -1,33 +0,0 @@ -package meowlib - -import ( - "crypto/tls" - - "github.com/go-resty/resty/v2" - "github.com/rs/zerolog/log" -) - -type Https struct { - url string -} - -func (https *Https) Send(msg []byte) ([]byte, error) { - client := resty.New().SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) - - resp, err := client.R(). - SetHeader("Content-Type", "application/json"). - SetBody(msg). - Post(https.url + "/message/add/") - if err != nil { - log.Error().Msg(err.Error()) - } - return resp.Body(), err -} - -func (https *Https) Start(callback *func() []InternalMessage) { - -} - -func (https *Https) Stop() { - -} diff --git a/id.enc b/id.enc new file mode 100644 index 0000000..cfa00d5 --- /dev/null +++ b/id.enc @@ -0,0 +1,243 @@ +-----BEGIN PGP MESSAGE----- +Comment: https://gopenpgp.org +Version: GopenPGP 2.2.4 + +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 +=zCs9 +-----END PGP MESSAGE----- \ No newline at end of file diff --git a/id.json b/id.json new file mode 100644 index 0000000..cee582d --- /dev/null +++ b/id.json @@ -0,0 +1 @@ +{"nickname":"myname","public_key":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuMi40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWXc0VmR4WUpLd1lCQkFIYVJ3OEJBUWRBT0hkU1JiS1RNZFJlbHJGNjJTL2dISmZkamVMbEhIL1hDWEpDCmNJYjJQOHZOQzI1aGJXVWdQRzFoYVd3K3dvd0VFeFlJQUQ0RkFtTU9GWGNKa08wNnJra09obE9jRnFFRWpydHIKbGwvbVplRlJjMEo3N1RxdVNRNkdVNXdDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdJaUFRQUFMYVVCQUtscgozWm5zUFczVVlIM0g2ZHg5K0hsWGVkMmZXRWVpNFNTWnFGMElLd05oQVA5Mm1XcHIxYjY4ajF1VUpwNksrZFllCldITzlsWlJCSHFSKzZDTGRYQU1WRHM0NEJHTU9GWGNTQ2lzR0FRUUJsMVVCQlFFQkIwRDNXbTF3N0lWZXhwa2gKdXhkUWxaYW9OUHNzbUovWk4yUG1hZXY4aGV5UVZRTUJDZ25DZUFRWUZnZ0FLZ1VDWXc0VmR3bVE3VHF1U1E2RwpVNXdXb1FTT3UydVdYK1psNFZGelFudnRPcTVKRG9aVG5BSWJEQUFBZUxrQSt3ZGkxb3o4c0xqbHBXOGI0eXdOCm9ZVmwxV0lPQUFVK2lveVhoUTlZbXFkMEFQOTVIREVpbVVqYXdnWUJ1YmsreGlTTmRWc1hMNDYxK1ZWM2hGcksKR005SEJnPT0KPVFKeTUKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ==","private_key":"LS0tLS1CRUdJTiBQR1AgUFJJVkFURSBLRVkgQkxPQ0stLS0tLQpWZXJzaW9uOiBHb3BlblBHUCAyLjIuNApDb21tZW50OiBodHRwczovL2dvcGVucGdwLm9yZwoKeFZnRVl3NFZkeFlKS3dZQkJBSGFSdzhCQVFkQU9IZFNSYktUTWRSZWxyRjYyUy9nSEpmZGplTGxISC9YQ1hKQwpjSWIyUDhzQUFQOXRuOTFabEd1UVVxYWNOVkFYYW9jQzRtd3BuaUsyKzlsVmxlRS9aUFphN0JGZXpRdHVZVzFsCklEeHRZV2xzUHNLTUJCTVdDQUErQlFKakRoVjNDWkR0T3E1SkRvWlRuQmFoQkk2N2E1WmY1bVhoVVhOQ2UrMDYKcmtrT2hsT2NBaHNEQWg0QkFoa0JBd3NKQndJVkNBTVdBQUlDSWdFQUFDMmxBUUNwYTkyWjdEMXQxR0I5eCtuYwpmZmg1VjNuZG4xaEhvdUVrbWFoZENDc0RZUUQvZHBscWE5Vyt2STlibENhZWl2bldIbGh6dlpXVVFSNmtmdWdpCjNWd0RGUTdIWFFSakRoVjNFZ29yQmdFRUFaZFZBUVVCQVFkQTkxcHRjT3lGWHNhWklic1hVSldXcURUN0xKaWYKMlRkajVtbnIvSVhza0ZVREFRb0pBQUQvZXFZc3VvdE9taE9TeFpUNlFOTGxacjd6SlN2MUVuQjhzVXNVaFhDbQp4d0FSTThKNEJCZ1dDQUFxQlFKakRoVjNDWkR0T3E1SkRvWlRuQmFoQkk2N2E1WmY1bVhoVVhOQ2UrMDZya2tPCmhsT2NBaHNNQUFCNHVRRDdCMkxXalB5d3VPV2xieHZqTEEyaGhXWFZZZzRBQlQ2S2pKZUZEMWlhcDNRQS8za2MKTVNLWlNOckNCZ0c1dVQ3R0pJMTFXeGN2anJYNVZYZUVXc29ZejBjRwo9MG1QdQotLS0tLUVORCBQR1AgUFJJVkFURSBLRVkgQkxPQ0stLS0tLQ==","peers":[{"name":"myfirstfriend","me":{"public":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuMi40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWXc0VmR4WUpLd1lCQkFIYVJ3OEJBUWRBTlVMTWVnRmk0Z1BMVmdubXp6cWtHYkhGZEtnTzA3RzIzWUVzCjdzOEdOa3ZOQzI1aGJXVWdQRzFoYVd3K3dvd0VFeFlJQUQ0RkFtTU9GWGNKa0lZQUN2dnZ0eHlCRnFFRTZVWmMKdDlyRlVRdHdzMUZDaGdBSysrKzNISUVDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdJaUFRQUF6SlVBL1JaRApiRTgyZnZBNDlnVnVnOWEydzQyQWFQeTlyVjlkdnZQNDNqV3RQRllYQVA5SHdwY2ZWYlBNOFR6ZGV0cm03L29PCjV1SUdaRTJpb1ArN29tS0x3Nlg4Q000NEJHTU9GWGNTQ2lzR0FRUUJsMVVCQlFFQkIwRDZ4WlJvclpmbmwrYjAKc2IxOVlrWTQrOXhPL2diZG8xa0VQcjJkbXFaOFBnTUJDZ25DZUFRWUZnZ0FLZ1VDWXc0VmR3bVFoZ0FLKysrMwpISUVXb1FUcFJseTMyc1ZSQzNDelVVS0dBQXI3NzdjY2dRSWJEQUFBY2EwQkFLSGh5UklBejhFZjlkVzhOUjRWCkpaci9YREZuTks5Rkp0T3pwSDZxN0FXUEFRRG4yblR6WjR3TjhYaWorMDdpU08xYVBtVkY3K0xlUmxmK1hsTTYKaFZOWkN3PT0KPXNaVzcKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ==","private":"LS0tLS1CRUdJTiBQR1AgUFJJVkFURSBLRVkgQkxPQ0stLS0tLQpWZXJzaW9uOiBHb3BlblBHUCAyLjIuNApDb21tZW50OiBodHRwczovL2dvcGVucGdwLm9yZwoKeFZnRVl3NFZkeFlKS3dZQkJBSGFSdzhCQVFkQU5VTE1lZ0ZpNGdQTFZnbm16enFrR2JIRmRLZ08wN0cyM1lFcwo3czhHTmtzQUFRQ0N6dlcvM3RIeWRwTXQxTzFMMXQwaXN6c3JHVHIyL1ZkWVI5S0lzUkUrc3hJZnpRdHVZVzFsCklEeHRZV2xzUHNLTUJCTVdDQUErQlFKakRoVjNDWkNHQUFyNzc3Y2NnUmFoQk9sR1hMZmF4VkVMY0xOUlFvWUEKQ3Z2dnR4eUJBaHNEQWg0QkFoa0JBd3NKQndJVkNBTVdBQUlDSWdFQUFNeVZBUDBXUTJ4UE5uN3dPUFlGYm9QVwp0c09OZ0dqOHZhMWZYYjd6K040MXJUeFdGd0QvUjhLWEgxV3p6UEU4M1hyYTV1LzZEdWJpQm1STm9xRC91NkppCmk4T2wvQWpIWFFSakRoVjNFZ29yQmdFRUFaZFZBUVVCQVFkQStzV1VhSzJYNTVmbTlMRzlmV0pHT1B2Y1R2NEcKM2FOWkJENjluWnFtZkQ0REFRb0pBQUQvWW9IZ2RvSFY0aERacFRDamtoSjdBQ2VKOG9PQkc5OWl2Y1BML25Jbgowd0FScDhKNEJCZ1dDQUFxQlFKakRoVjNDWkNHQUFyNzc3Y2NnUmFoQk9sR1hMZmF4VkVMY0xOUlFvWUFDdnZ2CnR4eUJBaHNNQUFCeHJRRUFvZUhKRWdEUHdSLzExYncxSGhVbG12OWNNV2MwcjBVbTA3T2tmcXJzQlk4QkFPZmEKZFBObmpBM3hlS1A3VHVKSTdWbytaVVh2NHQ1R1YvNWVVenFGVTFrTAo9SHRSMwotLS0tLUVORCBQR1AgUFJJVkFURSBLRVkgQkxPQ0stLS0tLQ==","generated":"2022-08-30T15:49:43.9615543+02:00"},"contact":{"name":"I'm the friend","contact_public_key":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuMi40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWXc0VmR4WUpLd1lCQkFIYVJ3OEJBUWRBSEtFeEtUWUh1Q1FHUUR6bkNwTHJvcm1FOUROeUN4cXEwM3lpCmxQbkVvemZOQzI1aGJXVWdQRzFoYVd3K3dvd0VFeFlJQUQ0RkFtTU9GWGNKa0NNZndzQXN0dTdnRnFFRWdzVEIKL1VPWmZlRmVJNE1wSXgvQ3dDeTI3dUFDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdJaUFRQUFJZE1CQUpDcApXWU1wVEVNNk5LQk02VWgzQlNudGpxajk1NmtyR1lCOW5xd1VmSTNlQVFEMytsR2txdDlxRDJaMnI3VzRNSHA0CjYyVjc5ejY2d3czb25YWUZxT1JkQ2M0NEJHTU9GWGNTQ2lzR0FRUUJsMVVCQlFFQkIwQnZoN3VZMEJiWEk4N2kKNXFJdGZyMDA5T0VFWThlRjBGNXZ6Vk85UjFNbU13TUJDZ25DZUFRWUZnZ0FLZ1VDWXc0VmR3bVFJeC9Dd0N5Mgo3dUFXb1FTQ3hNSDlRNWw5NFY0amd5a2pIOExBTExidTRBSWJEQUFBaHlFQS9pemxOMEJVUGFSOGZhRTlwdFlvClR4SnhYdGp0MFlLNm5qT0x3NnZ4dGVDMkFRRHlhQUtiRk5zK3Y2YWx0UVBjMk5yZXMwSmsvOTBTWVpLV2t4VkwKd1lVQUF3PT0KPU1EK0wKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ==","encryption_public_key":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuMi40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWXc0VmR4WUpLd1lCQkFIYVJ3OEJBUWRBMEFERXNKV1RnbEFqQ3RMRnpVaU5PaFcvcFc3c0lKMWV3S0J2ClN2N0RmZkROQzI1aGJXVWdQRzFoYVd3K3dvd0VFeFlJQUQ0RkFtTU9GWGNKa082UzdHRmpjd0xORnFFRVVQUVgKUC9BTndqQUlwK292N3BMc1lXTnpBczBDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdJaUFRQUFCVFVCQUpldgo0UHMxd3RGdVhwU3RidDdGKzVYelNwNU44Q2IyeXowNlRLakZIV3BLQVFELytOQzRaWit4YW84clRESG9nWTJlCjRCYlplTzBwcnBvSkdocktuTkhRQzg0NEJHTU9GWGNTQ2lzR0FRUUJsMVVCQlFFQkIwQ2RVTU9oTFYxeWVaSjcKRFIzOXdYU2FhaDY4TE80aytSdW9tTVN0SnFydEZBTUJDZ25DZUFRWUZnZ0FLZ1VDWXc0VmR3bVE3cExzWVdOegpBczBXb1FSUTlCYy84QTNDTUFpbjZpL3VrdXhoWTNNQ3pRSWJEQUFBUTlnQkFMd1lCb3RpU2hXR3lacHB6TWExCko4T0FRd3ZETWRhUjRmM280YTFxOFYwVUFQOXIwdXEwTDBqRm5QVHJOaER5bUZGRkJEdlhuSTg1ZGpVWnZ5aEMKV1AwYUF3PT0KPTlEOEgKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ==","lookup_public_key":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuMi40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWXc0VmR4WUpLd1lCQkFIYVJ3OEJBUWRBajdBcVM5ck1BUzdQVEVZS05NUWtHTlpVM1ROZ0U2bVhLRXRxClV4dDl0cEhOQzI1aGJXVWdQRzFoYVd3K3dvd0VFeFlJQUQ0RkFtTU9GWGNKa1BZQzlVc2pneSs0RnFFRTF4WkMKb1hRNThJVkJST0tyOWdMMVN5T0RMN2dDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdJaUFRQUF6WFFCQVB3TQp1ZmtJZnZZMkIxVUhGMDVSZzNrWkY0VXZWSkxyajV1eTBUWUxrSklkQVFETFNBZ0ZJcExRT1o2a25LK1o3cEs5Ckh4Q0l2elIrWEV1em43K2VDT3ZyQ2M0NEJHTU9GWGNTQ2lzR0FRUUJsMVVCQlFFQkIwQloxTzVFaWVYZC85bVkKWlA3bjR6Tmg1TWRGSk1pU0tCWmlaeDM2UzFROFl3TUJDZ25DZUFRWUZnZ0FLZ1VDWXc0VmR3bVE5Z0wxU3lPRApMN2dXb1FUWEZrS2hkRG53aFVGRTRxdjJBdlZMSTRNdnVBSWJEQUFBM29jQkFNOUgzclV4OVdsV3JJMEo5UHZjCm1vSmJtMFA4L0lLeE4rbGZjUHpQdWllNEFRQ0dsemM4T3ZQK0oxSjZORnV2NDlyQ3Q4QVNvSys4cWRHOGdZYm4KZFJSYUR3PT0KPUpIUUcKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ==","pull_servers":[{"Url":"http://myfriend.org/meow/"}]},"last_message":"0001-01-01T00:00:00Z","conversation_kp":{"public":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuMi40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWXc0VmR4WUpLd1lCQkFIYVJ3OEJBUWRBZlRvNnBSbVhPSTBFUTZ6UGZWeExxNDI3YThTVzhNMS9WLzdVCm9Oa3B2MmZOQzI1aGJXVWdQRzFoYVd3K3dvd0VFeFlJQUQ0RkFtTU9GWGNKa0hNcG9TQm0rbG1RRnFFRVNHTFMKQkloRzFiVmRMWFkxY3ltaElHYjZXWkFDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdJaUFRQUEwN2NBLzExZwp4ZXR5dUtnNXdGV1p1cEtoZmhMV2MvaGdyQ1R6bk9SN3BvUXBoTkQwQVB3S1JrVldQSEFrYUtMbzJ4NVVVVVJ6CnM4SG4ra2ZiNjhWT2R4M3RDckp3QTg0NEJHTU9GWGNTQ2lzR0FRUUJsMVVCQlFFQkIwQXB6bmZxYUMzMWVEd04KbHN4c2dYL0lNQW1McTJZeFZqOGRlK2VMb1J1SFBRTUJDZ25DZUFRWUZnZ0FLZ1VDWXc0VmR3bVFjeW1oSUdiNgpXWkFXb1FSSVl0SUVpRWJWdFYwdGRqVnpLYUVnWnZwWmtBSWJEQUFBeUNrQS8zeEszZ1RGU1VmcW1RVVcrN1RNCkkwcFVFR3BsNU81elF1NDZ5RDZ0cW1YNkFQd0xGK2NpQ2V4Z1JPOG5HeFYwQWlibkRoMkNRSHV0bXpxSU1iY1UKcklIdkN3PT0KPUlWVzkKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ==","private":"LS0tLS1CRUdJTiBQR1AgUFJJVkFURSBLRVkgQkxPQ0stLS0tLQpWZXJzaW9uOiBHb3BlblBHUCAyLjIuNApDb21tZW50OiBodHRwczovL2dvcGVucGdwLm9yZwoKeFZnRVl3NFZkeFlKS3dZQkJBSGFSdzhCQVFkQWZUbzZwUm1YT0kwRVE2elBmVnhMcTQyN2E4U1c4TTEvVi83VQpvTmtwdjJjQUFRRGFmZ2ovWlZCck8yNXM1bkNWRzVQenhuMzJUclhQYVN3SWhmN05FaUZNc2hCS3pRdHVZVzFsCklEeHRZV2xzUHNLTUJCTVdDQUErQlFKakRoVjNDWkJ6S2FFZ1p2cFprQmFoQkVoaTBnU0lSdFcxWFMxMk5YTXAKb1NCbStsbVFBaHNEQWg0QkFoa0JBd3NKQndJVkNBTVdBQUlDSWdFQUFOTzNBUDlkWU1YcmNyaW9PY0JWbWJxUwpvWDRTMW5QNFlLd2s4NXprZTZhRUtZVFE5QUQ4Q2taRlZqeHdKR2lpNk5zZVZGRkVjN1BCNS9wSDIrdkZUbmNkCjdRcXljQVBIWFFSakRoVjNFZ29yQmdFRUFaZFZBUVVCQVFkQUtjNTM2bWd0OVhnOERaYk1iSUYveURBSmk2dG0KTVZZL0hYdm5pNkViaHowREFRb0pBQUQvVG1iU2xBUDV1eTYxckJ0dTNpL1ozWkRqS2dDOWlCZXVsRmZWcjBKMwpVZ0FRek1KNEJCZ1dDQUFxQlFKakRoVjNDWkJ6S2FFZ1p2cFprQmFoQkVoaTBnU0lSdFcxWFMxMk5YTXBvU0JtCitsbVFBaHNNQUFESUtRRC9mRXJlQk1WSlIrcVpCUmI3dE13alNsUVFhbVhrN25OQzdqcklQcTJxWmZvQS9Bc1gKNXlJSjdHQkU3eWNiRlhRQ0p1Y09IWUpBZTYyYk9vZ3h0eFNzZ2U4TAo9enFsbQotLS0tLUVORCBQR1AgUFJJVkFURSBLRVkgQkxPQ0stLS0tLQ==","generated":"2022-08-30T15:49:43.9620978+02:00"},"lookup_kp":{"public":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuMi40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWXc0VmR4WUpLd1lCQkFIYVJ3OEJBUWRBYXIwWmUrT3NpSk50cDd2d1RHQ3BIZ0tIaEVhRDF5bW9tUXFDCkNGTmc2OHpOQzI1aGJXVWdQRzFoYVd3K3dvd0VFeFlJQUQ0RkFtTU9GWGNKa05SUzJUUVkwanJQRnFFRS8ydFIKemllc0RJZEJNOWtBMUZMWk5CalNPczhDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdJaUFRQUFPSWdBLzNVTwpQL1djL2VSTmhnMXJCb2xEU3VBckEyeTF0NUR0K1BEMlR2U010M2Q5QVFDQ2hJQVRNVDJyUkZueDJ4VlJ0bkQyCk50cWdFVE1jS0dPQ0Q5Wjk4bmRXQ000NEJHTU9GWGNTQ2lzR0FRUUJsMVVCQlFFQkIwRGg3UnBQMC9LQ2VlaUUKaVFIT1hlQzVoclhmYXFlck00b2lENFA4K2IyOEhBTUJDZ25DZUFRWUZnZ0FLZ1VDWXc0VmR3bVExRkxaTkJqUwpPczhXb1FUL2ExSE9KNndNaDBFejJRRFVVdGswR05JNnp3SWJEQUFBcWJRQkFKdjV5U0c4VDVEZUdCMWxMV0d1CkRDTFpjNDRRODFMSmxrT1AvSkhQc0s5SEFQOWVqK3dsTnZXVU5MVWFicWNGUDFtV3FwNDBTUVBtWWs3R010QTQKZHdIREFnPT0KPUcyWFAKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ==","private":"LS0tLS1CRUdJTiBQR1AgUFJJVkFURSBLRVkgQkxPQ0stLS0tLQpWZXJzaW9uOiBHb3BlblBHUCAyLjIuNApDb21tZW50OiBodHRwczovL2dvcGVucGdwLm9yZwoKeFZnRVl3NFZkeFlKS3dZQkJBSGFSdzhCQVFkQWFyMFplK09zaUpOdHA3dndUR0NwSGdLSGhFYUQxeW1vbVFxQwpDRk5nNjh3QUFRRFhyMllFZTkxTWJHRTRHRFRPSnk2dWFGbTFlczRPdzBCeld3OWNTWU1zV0EwUHpRdHVZVzFsCklEeHRZV2xzUHNLTUJCTVdDQUErQlFKakRoVjNDWkRVVXRrMEdOSTZ6eGFoQlA5clVjNG5yQXlIUVRQWkFOUlMKMlRRWTBqclBBaHNEQWg0QkFoa0JBd3NKQndJVkNBTVdBQUlDSWdFQUFEaUlBUDkxRGovMW5QM2tUWVlOYXdhSgpRMHJnS3dOc3RiZVE3Zmp3OWs3MGpMZDNmUUVBZ29TQUV6RTlxMFJaOGRzVlViWnc5amJhb0JFekhDaGpnZy9XCmZmSjNWZ2pIWFFSakRoVjNFZ29yQmdFRUFaZFZBUVVCQVFkQTRlMGFUOVB5Z25ub2hJa0J6bDNndVlhMTMycW4KcXpPS0lnK0QvUG05dkJ3REFRb0pBQUQvYy9qVzJGdUg1cHNTcGZKVyttUlFxVGZla3BOcWR4V1VOSTNlTzN4Nwpnd2dSOGNKNEJCZ1dDQUFxQlFKakRoVjNDWkRVVXRrMEdOSTZ6eGFoQlA5clVjNG5yQXlIUVRQWkFOUlMyVFFZCjBqclBBaHNNQUFDcHRBRUFtL25KSWJ4UGtONFlIV1V0WWE0TUl0bHpqaER6VXNtV1E0LzhrYyt3cjBjQS8xNlAKN0NVMjlaUTB0UnB1cHdVL1daYXFualJKQStaaVRzWXkwRGgzQWNNQwo9RkFmSQotLS0tLUVORCBQR1AgUFJJVkFURSBLRVkgQkxPQ0stLS0tLQ==","generated":"2022-08-30T15:49:43.9620978+02:00"}}],"known_servers":{"Name":"","Servers":null},"message_servers":{"Name":"Message Servers","Servers":[{"server_data":{"Url":"http://127.0.0.1/meow/"},"last_check":"0001-01-01T00:00:00Z","me":{"generated":"0001-01-01T00:00:00Z"}},{"server_data":{"Url":"mqtt://127.0.0.1"},"last_check":"0001-01-01T00:00:00Z","me":{"generated":"0001-01-01T00:00:00Z"}},{"server_data":{"Url":"meow://127.0.0.1"},"last_check":"0001-01-01T00:00:00Z","me":{"generated":"0001-01-01T00:00:00Z"}}]}} \ No newline at end of file diff --git a/identity.go b/identity.go index 3a098b0..7341aa9 100644 --- a/identity.go +++ b/identity.go @@ -53,14 +53,6 @@ func (*Identity) FinalizeInvitation(peer *Peer, receivedContact *ContactCard) { } -func (*Identity) AddPeer(name string, peerPublicKey string) string { - var peer Peer - peer.Me = NewKeyPair() - peer.Contact.Name = name - peer.Contact.ContactPublicKey = peerPublicKey - return peer.Me.Public -} - func LoadIdentity(file string) (*Identity, error) { var id Identity indata, err := ioutil.ReadFile(file) @@ -68,10 +60,10 @@ func LoadIdentity(file string) (*Identity, error) { return nil, err } pass, err := helper.DecryptMessageWithPassword([]byte(key), string(indata)) - if err == nil { - err = json.Unmarshal([]byte(pass), &id) + if err != nil { return nil, err } + err = json.Unmarshal([]byte(pass), &id) return &id, err } diff --git a/identity_test.go b/identity_test.go index adcfb97..10768a8 100644 --- a/identity_test.go +++ b/identity_test.go @@ -3,6 +3,8 @@ package meowlib import ( "log" "testing" + + "github.com/stretchr/testify/assert" ) func TestCreate(t *testing.T) { @@ -10,21 +12,10 @@ func TestCreate(t *testing.T) { id.Save("test.id") } -func TestSave(t *testing.T) { - id := Identity{Nickname: "myname", - PublicKey: "pubk", - PrivateKey: "privk", - Status: "online", - } - id.Save("test.id") -} - func TestLoad(t *testing.T) { id, err := LoadIdentity("test.id") if err != nil { - log.Fatal((err.Error())) - } - if id.Nickname != "myname" { - log.Fatal("failed") + log.Println(err.Error()) } + assert.Equal(t, id.Nickname, "myname", "The two words should be the same.") } diff --git a/invitation.png b/invitation.png new file mode 100644 index 0000000..d9bde59 Binary files /dev/null and b/invitation.png differ diff --git a/messages.pb.go b/messages.pb.go index f534e5d..d39f800 100644 --- a/messages.pb.go +++ b/messages.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 -// protoc v3.6.1 +// protoc-gen-go v1.28.1 +// protoc v3.21.5 // source: messages.proto package meowlib @@ -688,15 +688,17 @@ type UserMessage_ConversationStatus struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - LocalUuid string `protobuf:"bytes,1,opt,name=LocalUuid,proto3" json:"LocalUuid,omitempty"` - LocalSequence uint64 `protobuf:"varint,2,opt,name=LocalSequence,proto3" json:"LocalSequence,omitempty"` - Sent uint64 `protobuf:"varint,3,opt,name=Sent,proto3" json:"Sent,omitempty"` - Received uint64 `protobuf:"varint,4,opt,name=Received,proto3" json:"Received,omitempty"` - Processed uint64 `protobuf:"varint,5,opt,name=Processed,proto3" json:"Processed,omitempty"` - NextCcid string `protobuf:"bytes,6,opt,name=NextCcid,proto3" json:"NextCcid,omitempty"` - NextCcidAck bool `protobuf:"varint,7,opt,name=NextCcidAck,proto3" json:"NextCcidAck,omitempty"` // false when proposing a new id, true for accepting it - NextCcpkey string `protobuf:"bytes,8,opt,name=NextCcpkey,proto3" json:"NextCcpkey,omitempty"` - NextKeyCcpkeyAck bool `protobuf:"varint,9,opt,name=NextKeyCcpkeyAck,proto3" json:"NextKeyCcpkeyAck,omitempty"` // false when proposing a new key, true for accpeting it + LocalUuid string `protobuf:"bytes,1,opt,name=LocalUuid,proto3" json:"LocalUuid,omitempty"` + LocalSequence uint64 `protobuf:"varint,2,opt,name=LocalSequence,proto3" json:"LocalSequence,omitempty"` + Sent uint64 `protobuf:"varint,3,opt,name=Sent,proto3" json:"Sent,omitempty"` + Received uint64 `protobuf:"varint,4,opt,name=Received,proto3" json:"Received,omitempty"` + Processed uint64 `protobuf:"varint,5,opt,name=Processed,proto3" json:"Processed,omitempty"` + NextCkey string `protobuf:"bytes,6,opt,name=NextCkey,proto3" json:"NextCkey,omitempty"` // conversation key + NextCkeyAck bool `protobuf:"varint,7,opt,name=NextCkeyAck,proto3" json:"NextCkeyAck,omitempty"` // false when proposing a new id, true for accepting it + NextEkey string `protobuf:"bytes,8,opt,name=NextEkey,proto3" json:"NextEkey,omitempty"` // encrypted key + NextKeyEkeyAck bool `protobuf:"varint,9,opt,name=NextKeyEkeyAck,proto3" json:"NextKeyEkeyAck,omitempty"` // false when proposing a new key, true for accpeting it + NextLkey string `protobuf:"bytes,10,opt,name=NextLkey,proto3" json:"NextLkey,omitempty"` // lookup key + NextLkeyAck bool `protobuf:"varint,11,opt,name=NextLkeyAck,proto3" json:"NextLkeyAck,omitempty"` // false when proposing a new id, true for accepting it } func (x *UserMessage_ConversationStatus) Reset() { @@ -766,30 +768,44 @@ func (x *UserMessage_ConversationStatus) GetProcessed() uint64 { return 0 } -func (x *UserMessage_ConversationStatus) GetNextCcid() string { +func (x *UserMessage_ConversationStatus) GetNextCkey() string { if x != nil { - return x.NextCcid + return x.NextCkey } return "" } -func (x *UserMessage_ConversationStatus) GetNextCcidAck() bool { +func (x *UserMessage_ConversationStatus) GetNextCkeyAck() bool { if x != nil { - return x.NextCcidAck + return x.NextCkeyAck } return false } -func (x *UserMessage_ConversationStatus) GetNextCcpkey() string { +func (x *UserMessage_ConversationStatus) GetNextEkey() string { if x != nil { - return x.NextCcpkey + return x.NextEkey } return "" } -func (x *UserMessage_ConversationStatus) GetNextKeyCcpkeyAck() bool { +func (x *UserMessage_ConversationStatus) GetNextKeyEkeyAck() bool { if x != nil { - return x.NextKeyCcpkeyAck + return x.NextKeyEkeyAck + } + return false +} + +func (x *UserMessage_ConversationStatus) GetNextLkey() string { + if x != nil { + return x.NextLkey + } + return "" +} + +func (x *UserMessage_ConversationStatus) GetNextLkeyAck() bool { + if x != nil { + return x.NextLkeyAck } return false } @@ -939,7 +955,7 @@ var file_messages_proto_rawDesc = []byte{ 0x4b, 0x65, 0x79, 0x12, 0x37, 0x0a, 0x0e, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6d, 0x65, 0x6f, 0x77, 0x6c, 0x69, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0e, 0x54, 0x72, - 0x75, 0x73, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x22, 0x94, 0x05, 0x0a, + 0x75, 0x73, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x22, 0xca, 0x05, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, @@ -957,7 +973,7 @@ var file_messages_proto_rawDesc = []byte{ 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x6f, 0x77, 0x6c, 0x69, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x1a, 0xb0, 0x02, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x1a, 0xe6, 0x02, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x55, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x55, 0x75, 0x69, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x53, 0x65, @@ -968,22 +984,26 @@ var file_messages_proto_rawDesc = []byte{ 0x04, 0x52, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x4e, 0x65, 0x78, - 0x74, 0x43, 0x63, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x4e, 0x65, 0x78, - 0x74, 0x43, 0x63, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x43, 0x63, 0x69, - 0x64, 0x41, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x4e, 0x65, 0x78, 0x74, - 0x43, 0x63, 0x69, 0x64, 0x41, 0x63, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x4e, 0x65, 0x78, 0x74, 0x43, - 0x63, 0x70, 0x6b, 0x65, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4e, 0x65, 0x78, - 0x74, 0x43, 0x63, 0x70, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x4e, 0x65, 0x78, 0x74, 0x4b, - 0x65, 0x79, 0x43, 0x63, 0x70, 0x6b, 0x65, 0x79, 0x41, 0x63, 0x6b, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x10, 0x4e, 0x65, 0x78, 0x74, 0x4b, 0x65, 0x79, 0x43, 0x63, 0x70, 0x6b, 0x65, 0x79, - 0x41, 0x63, 0x6b, 0x1a, 0x4e, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x31, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x6f, 0x77, 0x6c, 0x69, 0x62, 0x2e, 0x4d, 0x69, 0x6e, 0x69, - 0x6d, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x42, 0x21, 0x5a, 0x1f, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x2e, 0x72, 0x65, 0x64, - 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x2f, 0x79, 0x76, 0x65, 0x73, 0x2f, 0x6d, - 0x65, 0x6f, 0x77, 0x6c, 0x69, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x43, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x4e, 0x65, 0x78, + 0x74, 0x43, 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x43, 0x6b, 0x65, + 0x79, 0x41, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x4e, 0x65, 0x78, 0x74, + 0x43, 0x6b, 0x65, 0x79, 0x41, 0x63, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x4e, 0x65, 0x78, 0x74, 0x45, + 0x6b, 0x65, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x4e, 0x65, 0x78, 0x74, 0x45, + 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x4e, 0x65, 0x78, 0x74, 0x4b, 0x65, 0x79, 0x45, 0x6b, + 0x65, 0x79, 0x41, 0x63, 0x6b, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x4e, 0x65, 0x78, + 0x74, 0x4b, 0x65, 0x79, 0x45, 0x6b, 0x65, 0x79, 0x41, 0x63, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x4e, + 0x65, 0x78, 0x74, 0x4c, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x4e, + 0x65, 0x78, 0x74, 0x4c, 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x4c, + 0x6b, 0x65, 0x79, 0x41, 0x63, 0x6b, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x4e, 0x65, + 0x78, 0x74, 0x4c, 0x6b, 0x65, 0x79, 0x41, 0x63, 0x6b, 0x1a, 0x4e, 0x0a, 0x05, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x6f, 0x77, 0x6c, 0x69, + 0x62, 0x2e, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, + 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x42, 0x21, 0x5a, 0x1f, 0x66, 0x6f, 0x72, + 0x67, 0x65, 0x2e, 0x72, 0x65, 0x64, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x2f, + 0x79, 0x76, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x6f, 0x77, 0x6c, 0x69, 0x62, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/network.go b/network.go deleted file mode 100644 index 298fd20..0000000 --- a/network.go +++ /dev/null @@ -1,7 +0,0 @@ -package meowlib - -type Network interface { - Send(request []byte) ([]byte, error) - Start(callback *func() []InternalMessage) - Stop() -} diff --git a/pb/messages.proto b/pb/messages.proto index b27520d..7bda074 100644 --- a/pb/messages.proto +++ b/pb/messages.proto @@ -2,12 +2,14 @@ syntax = "proto3"; package meowlib; option go_package = "forge.redroom.link/yves/meowlib"; +// structure for sending a message intended for server use in protobuf format message PackedServerMessage { string From = 1; bytes Payload = 2; - string Signature = 3; + bytes Signature = 3; } +// structure for sending a message to be forwarded to another user in protobuf format message PackedUserMessage { string From = 1; string Destination=2; @@ -15,6 +17,7 @@ message PackedUserMessage { bytes Signature=4; } +// structure defining a message to be forwarded encrypted, then sent in a "packedmessage" message ServerMessage { string Type = 1; string ServerPubKey = 2 ; @@ -72,10 +75,12 @@ message UserMessage { uint64 Sent = 3 ; uint64 Received = 4; uint64 Processed = 5; - string NextCcid = 6; - bool NextCcidAck = 7; // false when proposing a new id, true for accepting it - string NextCcpkey = 8; - bool NextKeyCcpkeyAck = 9; // false when proposing a new key, true for accpeting it + string NextCkey = 6; // contact key + bool NextCkeyAck = 7; // false when proposing a new id, true for accepting it + string NextEkey = 8; // encryption key + bool NextKeyEkeyAck = 9; // false when proposing a new key, true for accpeting it + string NextLkey = 10; // lookup key + bool NextLkeyAck = 11; // false when proposing a new id, true for accepting it } ConversationStatus Status = 5; diff --git a/pb/protogen.bat b/pb/protogen.bat new file mode 100644 index 0000000..6df5a79 --- /dev/null +++ b/pb/protogen.bat @@ -0,0 +1,4 @@ +#!/bin/bash +protoc -I=. --go_out=.. messages.proto +move ..\forge.redroom.link\yves\meowlib\messages.pb.go ..\ +rmdir ..\forge.redroom.link /S /Q diff --git a/peer.go b/peer.go index 7869b31..99d1974 100644 --- a/peer.go +++ b/peer.go @@ -9,7 +9,6 @@ import ( "log" "math" "os" - "strings" "time" "github.com/makiuchi-d/gozxing" @@ -25,28 +24,29 @@ type ContactCard struct { } type Peer struct { - Name string `json:"name,omitempty"` - Me KeyPair `json:"me,omitempty"` - Contact ContactCard `json:"contact,omitempty"` - Visible bool `json:"visible,omitempty"` - VisiblePassword string `json:"visible_password,omitempty"` - PasswordType string `json:"password_type,omitempty"` - Blocked bool `json:"blocked,omitempty"` - MessageNotification string `json:"message_notification,omitempty"` - OnionMode bool `json:"onion_mode,omitempty"` - Conversation []InternalMessage `json:"convdersation,omitempty"` - LastMessage time.Time `json:"last_message,omitempty"` - EncryptionKp KeyPair `json:"conversation_kp,omitempty"` - LookupKp KeyPair `json:"lookup_kp,omitempty"` - CommUrl string `json:"comm_url,omitempty"` - net Network + Name string `json:"name,omitempty"` + // Conversation []InternalMessage `json:"conversation,omitempty"` + // My own keys for that peer + Me KeyPair `json:"me,omitempty"` + EncryptionKp KeyPair `json:"conversation_kp,omitempty"` + LookupKp KeyPair `json:"lookup_kp,omitempty"` + // Peer keys and infos + Contact ContactCard `json:"contact,omitempty"` + // Internal management attributes + Visible bool `json:"visible,omitempty"` + VisiblePassword string `json:"visible_password,omitempty"` + PasswordType string `json:"password_type,omitempty"` + Blocked bool `json:"blocked,omitempty"` + MessageNotification string `json:"message_notification,omitempty"` + OnionMode bool `json:"onion_mode,omitempty"` + LastMessage time.Time `json:"last_message,omitempty"` } type PeerList []Peer type Group struct { - Name string `json:"name,omitempty"` - Members []ContactCard `json:"members,omitempty"` + Name string `json:"name,omitempty"` + Members []Peer `json:"members,omitempty"` } func (pl *PeerList) GetFromPublicKey(publickey string) *Peer { @@ -67,20 +67,6 @@ func (pl *PeerList) GetFromName(name string) *Peer { return nil } -func (peer *Peer) SetCommInterface(url string) { - if strings.HasPrefix(url, "http://") { - var https *Https - https.url = url - peer.net = Network(https) - } - peer.net = nil -} - -func (peer *Peer) SendText(text string) { - im := CreateText(*peer, text) - fmt.Println(im.MessageData.Destination) -} - func (contact *ContactCard) WritePng(filename string) { jsonContact, _ := json.Marshal(contact) //imgdata := base64.StdEncoding.EncodeToString(jsonContact) @@ -152,3 +138,23 @@ func ReadQr(fielname string) ContactCard { fmt.Println(result) return contact } + +func (p *Peer) CreateMessage(Message []byte) (lookupK string, EncryptedMsg []byte, Signature []byte, Servers []Server, err error) { + // prepares a message to send to a specific peer contact + EncryptedMsg, Signature, err = EncryptAndSign(p.Contact.EncryptionPublicKey, p.Me.Private, Message) + if err != nil { + fmt.Println(err.Error()) + return "", nil, nil, nil, err + } + return p.LookupKp.Public, EncryptedMsg, Signature, p.Contact.PullServers, err +} + +func (p *Peer) ReadMessage(Message []byte, Signature []byte) (DecryptedMsg []byte, err error) { + // reads a message from a specific peer contact + DecryptedMsg, err = DecryptAndCheck(p.Me.Private, p.Contact.ContactPublicKey, Message, Signature) + if err != nil { + fmt.Println(err.Error()) + return nil, err + } + return DecryptedMsg, err +} diff --git a/test b/test new file mode 100644 index 0000000..2f6fc9f --- /dev/null +++ b/test @@ -0,0 +1,2 @@ + +toto mon textemoi \ No newline at end of file diff --git a/test.id b/test.id new file mode 100644 index 0000000..827ac5e --- /dev/null +++ b/test.id @@ -0,0 +1,51 @@ +-----BEGIN PGP MESSAGE----- +Version: GopenPGP 2.2.4 +Comment: https://gopenpgp.org + +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 +=dF9e +-----END PGP MESSAGE----- \ No newline at end of file diff --git a/usermessage.go b/usermessage.go deleted file mode 100644 index f5c18da..0000000 --- a/usermessage.go +++ /dev/null @@ -1,60 +0,0 @@ -package meowlib - -import ( - "encoding/json" - "strings" - "time" - - "github.com/google/uuid" - "github.com/rs/zerolog/log" -) - -type InternalMessage struct { - With Peer `json:"with,omitempty"` - SentByMe bool `json:"sent_by_me,omitempty"` - MessageData UserMessage `json:"message_data,omitempty"` - ServerUuid string `json:"server_uuid,omitempty"` - Received time.Time `json:"received,omitempty"` - Processed time.Time `json:"processed,omitempty"` - TransferPath []Server `json:"transfer_path,omitempty"` -} - -func CreateText(Destination Peer, text string) *InternalMessage { - var msg InternalMessage - msg.With = Destination - msg.SentByMe = true - msg.MessageData.From = Destination.Me.Public - msg.MessageData.Destination = Destination.Contact.ContactPublicKey - msg.MessageData.Status.Sent = uint64(time.Now().Unix()) - msg.MessageData.Status.LocalUuid = strings.Replace(uuid.New().String(), "-", "", -1) - msg.MessageData.Type = "t" - msg.MessageData.Data = []byte(text) - return &msg -} - -func (msg *UserMessage) Pack() *PackedUserMessage { - var pck PackedUserMessage - - jsonMsg, _ := json.Marshal(msg) - armor, err := Encrypt(msg.Destination, jsonMsg) - if err != nil { - log.Error().Msg("Message encryption failed") - } - pck.Destination = msg.Destination - pck.Payload = []byte(armor) - return &pck -} - -func (pck *PackedUserMessage) Unpack(privateKey string) *UserMessage { - var msg *UserMessage - - decrypted, err := Decrypt(privateKey, pck.Payload) - if err != nil { - log.Error().Msg("Message decryption failed") - } - err = json.Unmarshal(decrypted, &msg) - if err != nil { - log.Error().Msg("Message encryption failed") - } - return msg -} diff --git a/usermessage_test.go b/usermessage_test.go deleted file mode 100644 index 74160c4..0000000 --- a/usermessage_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package meowlib - -import ( - "fmt" - "log" - "testing" -) - -func TestCreateText(t *testing.T) { - kp := NewKeyPair() - fmt.Println(kp.Public) - fmt.Println(kp.Private) -} - -func TestPack(t *testing.T) { - kp := NewKeyPair() - // fmt.Println(kp.Public) - // fmt.Println(kp.Private) - key := kp.GetCryptoKeyObject() - // fmt.Println(key.Armor()) - pubkey, _ := key.GetArmoredPublicKey() - if kp.Public != pubkey { - log.Fatal("error in public key") - } -} - -func TestUnPack(t *testing.T) { - kp := NewKeyPair() - // fmt.Println(kp.Public) - // fmt.Println(kp.Private) - key := kp.GetCryptoKeyObject() - // fmt.Println(key.Armor()) - pubkey, _ := key.GetArmoredPublicKey() - if kp.Public != pubkey { - log.Fatal("error in public key") - } -}