Complete refactor using protobuff
This commit is contained in:
parent
60b14db80c
commit
c07cdff3de
106
asymcrypt.go
Normal file
106
asymcrypt.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package meowlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||||
|
"github.com/ProtonMail/gopenpgp/v2/helper"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyPair struct {
|
||||||
|
Public string `json:"public,omitempty"`
|
||||||
|
Private string `json:"private,omitempty"`
|
||||||
|
Generated time.Time `json:"generated,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeysArray []KeyPair
|
||||||
|
|
||||||
|
func NewKeyPair() KeyPair {
|
||||||
|
var kp KeyPair
|
||||||
|
keys, err := crypto.GenerateKey("name", "mail", "rsa", 4096)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Key generation failed")
|
||||||
|
}
|
||||||
|
kp.Generated = time.Now()
|
||||||
|
pub, err := keys.GetArmoredPublicKey()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Public key extraction failed")
|
||||||
|
}
|
||||||
|
kp.Public = base64.StdEncoding.EncodeToString([]byte(pub))
|
||||||
|
priv, err := keys.Armor()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Private key extraction failed")
|
||||||
|
}
|
||||||
|
kp.Private = base64.StdEncoding.EncodeToString([]byte(priv))
|
||||||
|
return kp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (keyPair *KeyPair) GetCryptoKeyObject() *crypto.Key {
|
||||||
|
priv, err := base64.StdEncoding.DecodeString(keyPair.Private)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Create key from armoured b64 failed")
|
||||||
|
}
|
||||||
|
key, err := crypto.NewKeyFromArmored(string(priv))
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Create key from armoured failed")
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
func Encrypt(publicKey string, data []byte) ([]byte, error) {
|
||||||
|
pub, err := base64.StdEncoding.DecodeString(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message encryption b64 failed")
|
||||||
|
}
|
||||||
|
armor, err := helper.EncryptBinaryMessageArmored(string(pub), data)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message encryption failed")
|
||||||
|
}
|
||||||
|
return []byte(armor), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Decrypt(privateKey string, data []byte) ([]byte, error) {
|
||||||
|
priv, err := base64.StdEncoding.DecodeString(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message decryption b64 failed")
|
||||||
|
}
|
||||||
|
decrypted, err := helper.DecryptBinaryMessageArmored(string(priv), []byte(""), 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)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message encryption and sign b64 failed")
|
||||||
|
}
|
||||||
|
priv, err := base64.StdEncoding.DecodeString(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message encryption and sign b64 failed")
|
||||||
|
}
|
||||||
|
armor, signature, err := helper.EncryptSignBinaryDetached(string(pub), string(priv), []byte(""), data)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message encryption and sign failed")
|
||||||
|
}
|
||||||
|
return []byte(armor), []byte(signature), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecryptAndSign(publicKey string, privateKey string, data []byte, signature []byte) ([]byte, error) {
|
||||||
|
pub, err := base64.StdEncoding.DecodeString(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message decryption and sign b64 failed")
|
||||||
|
}
|
||||||
|
priv, err := base64.StdEncoding.DecodeString(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message decryption and sign b64 failed")
|
||||||
|
}
|
||||||
|
decrypted, err := helper.DecryptVerifyBinaryDetached(string(pub), string(priv), []byte(""), data, string(signature))
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message decryption and sign failed")
|
||||||
|
}
|
||||||
|
return decrypted, err
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package meow
|
package meowlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
@ -18,7 +19,8 @@ func TestGetKey(t *testing.T) {
|
|||||||
// fmt.Println(kp.Private)
|
// fmt.Println(kp.Private)
|
||||||
key := kp.GetCryptoKeyObject()
|
key := kp.GetCryptoKeyObject()
|
||||||
// fmt.Println(key.Armor())
|
// fmt.Println(key.Armor())
|
||||||
pubkey, _ := key.GetArmoredPublicKey()
|
Armpubkey, _ := key.GetArmoredPublicKey()
|
||||||
|
pubkey := base64.StdEncoding.EncodeToString([]byte(Armpubkey))
|
||||||
if kp.Public != pubkey {
|
if kp.Public != pubkey {
|
||||||
log.Fatal("error in public key")
|
log.Fatal("error in public key")
|
||||||
}
|
}
|
10
clean.sh
Executable file
10
clean.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#/usr/bin/bash
|
||||||
|
rm doc/*.aux
|
||||||
|
rm doc/*.fdb_latexmk
|
||||||
|
rm doc/*.fls
|
||||||
|
rm doc/*.log
|
||||||
|
rm doc/*.pdf
|
||||||
|
rm doc/*.synctex.gz
|
||||||
|
rm test.id
|
||||||
|
rm id.json
|
||||||
|
rm messages.pb.go
|
15
doc/archi01.puml
Normal file
15
doc/archi01.puml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
@startuml Simple server based communication
|
||||||
|
|
||||||
|
actor Client1
|
||||||
|
|
||||||
|
node Server [
|
||||||
|
Server
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
actor Client2
|
||||||
|
Client1 -u-> Server:Send Message
|
||||||
|
Client2 -u-> Server: Retrieve Message
|
||||||
|
|
||||||
|
|
||||||
|
@enduml
|
15
doc/archi02.puml
Normal file
15
doc/archi02.puml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
@startuml
|
||||||
|
|
||||||
|
actor Client1
|
||||||
|
|
||||||
|
node Server [
|
||||||
|
Server
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
actor Client2
|
||||||
|
Client1 -u-> Server:Send Presence (IP@)
|
||||||
|
Client2 -u-> Server: Retrieve Presence (IP@)
|
||||||
|
Client1 <-> Client2: Direct Connect
|
||||||
|
|
||||||
|
@enduml
|
39
doc/archi03.puml
Normal file
39
doc/archi03.puml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
@startuml Mtrk protocol communication
|
||||||
|
|
||||||
|
actor Client1
|
||||||
|
|
||||||
|
node Server [
|
||||||
|
Server
|
||||||
|
]
|
||||||
|
node GW1 [
|
||||||
|
GW1
|
||||||
|
]
|
||||||
|
node GW2 [
|
||||||
|
GW2
|
||||||
|
]
|
||||||
|
node GW3 [
|
||||||
|
GW3
|
||||||
|
]
|
||||||
|
node GW4 [
|
||||||
|
GW4
|
||||||
|
]
|
||||||
|
node GW5 [
|
||||||
|
GW5
|
||||||
|
]
|
||||||
|
node GW6 [
|
||||||
|
GW6
|
||||||
|
]
|
||||||
|
actor Client2
|
||||||
|
|
||||||
|
Client1 -> GW1:(GK1(GK2(GK3((CK2)Payload))))
|
||||||
|
GW1 -> GW2:(GK2(GK3((CK2)Payload)))
|
||||||
|
GW2 -> GW3:(GK3((CK2)Payload))
|
||||||
|
GW3 -d-> Server:((CK2)Payload)
|
||||||
|
|
||||||
|
Client2 -> GW4:(GK4(GK5(GK6(CK2)Request))))
|
||||||
|
GW4 -> GW5:(GK5(GK6(CK2)Request)))
|
||||||
|
GW5 -> GW6:(GK6(CK2)Request))
|
||||||
|
GW6 -u-> Server:((CK2)Request)
|
||||||
|
|
||||||
|
|
||||||
|
@enduml
|
0
doc/architecture.tex
Normal file
0
doc/architecture.tex
Normal file
83
doc/class_messages01.puml
Normal file
83
doc/class_messages01.puml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
@startuml Messages sturcture and hierarchy
|
||||||
|
|
||||||
|
class PackedServerMessage {
|
||||||
|
From string
|
||||||
|
Payload string
|
||||||
|
Signature string
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerMessage {
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PackedForwardMessage {
|
||||||
|
NextServerKey string
|
||||||
|
Url string
|
||||||
|
Payload string
|
||||||
|
}
|
||||||
|
|
||||||
|
class PackedUserMessage {
|
||||||
|
DestinationKey
|
||||||
|
Payload
|
||||||
|
Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserMessage
|
||||||
|
{
|
||||||
|
LocalUuid
|
||||||
|
Destination
|
||||||
|
From
|
||||||
|
Type
|
||||||
|
Data
|
||||||
|
Sent
|
||||||
|
NextKey
|
||||||
|
}
|
||||||
|
|
||||||
|
class InternalMessage {
|
||||||
|
With
|
||||||
|
SentByMe
|
||||||
|
UserMessageData
|
||||||
|
ServerUuid
|
||||||
|
Received
|
||||||
|
Processed
|
||||||
|
TransferPath
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConversationRequest {
|
||||||
|
Destination string
|
||||||
|
LastUuidOK string
|
||||||
|
PublishIp bool
|
||||||
|
}
|
||||||
|
class PollRequest {
|
||||||
|
Destinations []ConversationRequest
|
||||||
|
Time time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConversationResponse {
|
||||||
|
MessageUuids []string
|
||||||
|
SourceIpAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
class PollResponse {
|
||||||
|
Conversations map[string]ConversationResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageBodies {
|
||||||
|
Messages []Message
|
||||||
|
}
|
||||||
|
|
||||||
|
PackedServerMessage *-- ServerMessage
|
||||||
|
|
||||||
|
ServerMessage <|--PackedForwardMessage
|
||||||
|
ServerMessage <|--PollRequest
|
||||||
|
PollRequest *-- ConversationRequest
|
||||||
|
PollResponse *--ConversationResponse
|
||||||
|
ServerMessage <|--PollResponse
|
||||||
|
ServerMessage <|-- MessageBodies
|
||||||
|
PackedUserMessage *-- UserMessage
|
||||||
|
InternalMessage *-- UserMessage
|
||||||
|
PackedForwardMessage *-- ServerMessage
|
||||||
|
PackedForwardMessage *-- PackedForwardMessage
|
||||||
|
MessageBodies *-- PackedUserMessage
|
||||||
|
@enduml
|
193
doc/protocol.tex
Normal file
193
doc/protocol.tex
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
\documentclass{article}
|
||||||
|
\usepackage{fetamont}
|
||||||
|
\begin{document}
|
||||||
|
\title{
|
||||||
|
\textffm{Meow} messaging protocol}
|
||||||
|
\author{Author
|
||||||
|
\texttt{author@address.net}}
|
||||||
|
\date{\today}
|
||||||
|
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
\begin{abstract}
|
||||||
|
The \textffm{Meow} protocol is a privacy driven instant messaging protocol.
|
||||||
|
That protocol might be used for creating secure and distributed chat services or allowing machine to machine communication.
|
||||||
|
This document describes the services provided by the protocol, the messaging structures and the transport protocols that might be used.
|
||||||
|
|
||||||
|
\end{abstract}
|
||||||
|
|
||||||
|
|
||||||
|
\section{Services}
|
||||||
|
\subsection{Unregulated identities}
|
||||||
|
The only requirement to get a valid \textffm{Meow} identity is to generate a user key pair.
|
||||||
|
No phone number or email check will be performed, unlike main instant messaging protocols, there is no central administration.
|
||||||
|
|
||||||
|
\subsection{Fine grained privacy control}
|
||||||
|
|
||||||
|
\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 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.
|
||||||
|
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).
|
||||||
|
|
||||||
|
\subsubsection{Presence protocol for direct messaging}
|
||||||
|
A presence service associating your conversation keys to your IP address for direct peer to peer connection is also provided.
|
||||||
|
The presence protocol is simply activated by setting a flag in the message poll requests.
|
||||||
|
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 :
|
||||||
|
\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 ...
|
||||||
|
\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.
|
||||||
|
|
||||||
|
\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.
|
||||||
|
That rendez-vous card might be sent by any trustable communication means, or preferably from hand to hand, as a file on a flash disk or a QR code.\\
|
||||||
|
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 would be discarded.
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Group conversation}
|
||||||
|
A very basic group messaging service is available. It allows to exchange group information between users. After that, a message to a group will send a copy of the message to each member.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Emergency broadcast}
|
||||||
|
The
|
||||||
|
|
||||||
|
|
||||||
|
\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,...)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\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.
|
||||||
|
Why providing that service while the internet is suffocating due to the abundance of social networks ?\\
|
||||||
|
Well, that option offered 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.
|
||||||
|
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}
|
||||||
|
|
||||||
|
\section{Identities and keys}
|
||||||
|
|
||||||
|
\subsection{User identity}
|
||||||
|
Each \textffm{Meow} user has a unique identity. That identity is strictly private, only used to manage your own data (local encryption, devices, ...)
|
||||||
|
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)
|
||||||
|
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.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsection{Conversation encryption}
|
||||||
|
Each conversation with one of your contacts will be encrypted using an encryption keypair (Ekp)
|
||||||
|
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 tequest 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.
|
||||||
|
|
||||||
|
\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.
|
||||||
|
|
||||||
|
\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
|
||||||
|
\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 :
|
||||||
|
\begin{enumerate}
|
||||||
|
\item I'm receiving a contact name, without any key
|
||||||
|
\item
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\section{Messaging}
|
||||||
|
\subsection{User messages}
|
||||||
|
TODO
|
||||||
|
|
||||||
|
\subsection{Server stored message}
|
||||||
|
TODO
|
||||||
|
|
||||||
|
\subsection{Matriochka message packing}
|
||||||
|
TODO
|
||||||
|
|
||||||
|
\subsection{Synchronization messages}
|
||||||
|
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.\\
|
||||||
|
Examples of a valid url:
|
||||||
|
\begin{verbatim}
|
||||||
|
http://myserver.com
|
||||||
|
https://user:pass@myauthenticatedserver.net:8443
|
||||||
|
mqtt://mymqttserver:6203
|
||||||
|
udp://myudpserver.org:41325
|
||||||
|
serial://dev/ttyS0
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
\subsection{HTTP/S}
|
||||||
|
TODO
|
||||||
|
|
||||||
|
\subsection{UDP}
|
||||||
|
TODO
|
||||||
|
|
||||||
|
\subsection{Internetless alternative routing}
|
||||||
|
TODO
|
||||||
|
|
||||||
|
\section{Server Features}
|
||||||
|
\subsection{Server catalog}
|
||||||
|
\subsection{Antispam}
|
||||||
|
\subsection{Self defense}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\section{Backup}
|
||||||
|
\section{Recovery}
|
||||||
|
|
||||||
|
\section{Very secure devices}
|
||||||
|
You don't trust your phone ?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\end{document}
|
8
doc/sq_msg01.puml
Normal file
8
doc/sq_msg01.puml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
@startuml
|
||||||
|
Client1 -> Server: Send Message (PubKeyClient2 + Payload)
|
||||||
|
Server --> Client1: Ack Message (Server UUID + DateReceived)
|
||||||
|
Client2 -> Server: Get Messages (PubKeyClient2 + IpPublish)
|
||||||
|
Server --> Client2: Available Messages list (UUID list cyphered with PubKeyClient2)
|
||||||
|
Client2 <- Server: Get Messages (decoded UUID signed with PK)
|
||||||
|
Server --> Client2: Messages [](PubKeyClient2 + Payload)
|
||||||
|
@enduml
|
70
endtoend_test.go
Normal file
70
endtoend_test.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package meowlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEndToEnd(t *testing.T) {
|
||||||
|
//
|
||||||
|
// Create my own identity
|
||||||
|
//
|
||||||
|
fmt.Println("Trying to load identity from file.")
|
||||||
|
me, err := LoadIdentity("test.id")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed : creating New identity...")
|
||||||
|
me = CreateIdentity("myname")
|
||||||
|
|
||||||
|
//
|
||||||
|
// define my preferences (servers)
|
||||||
|
//
|
||||||
|
me.MessageServers.Name = "Message Servers"
|
||||||
|
me.MessageServers.AddUrls([]string{"http://127.0.0.1/meow/", "mqtt://127.0.0.1", "meow://127.0.0.1"})
|
||||||
|
//
|
||||||
|
// create an invitation for a friend, I want him/her to know me as Bender
|
||||||
|
//
|
||||||
|
fmt.Println("Creating an invitation for the first friend...")
|
||||||
|
myFirstFriend, invitation := me.InvitePeer("Bender", "myfirstfriend", []int{1, 2})
|
||||||
|
// print my invitation
|
||||||
|
a, _ := json.Marshal(invitation)
|
||||||
|
fmt.Println(string(a))
|
||||||
|
// TODO : Convert invitation to QR Code
|
||||||
|
|
||||||
|
//
|
||||||
|
// Simulate peer invitation response : generate the friend's keypair
|
||||||
|
fmt.Println("Simulating first friend answer...")
|
||||||
|
var receivedContact Contact
|
||||||
|
firstFriendContactKp := NewKeyPair()
|
||||||
|
firstFriendEncryptionKp := NewKeyPair()
|
||||||
|
firstFriendLookupKp := NewKeyPair()
|
||||||
|
receivedContact.Name = "I'm the friend"
|
||||||
|
receivedContact.ContactPublicKey = firstFriendContactKp.Public
|
||||||
|
receivedContact.EncryptionPublicKey = firstFriendEncryptionKp.Public
|
||||||
|
receivedContact.LookupPublicKey = firstFriendLookupKp.Public
|
||||||
|
var friendsMessageServers ServerList
|
||||||
|
friendsMessageServers.AddUrls([]string{"http://myfriend.org/meow/"})
|
||||||
|
for _, srv := range friendsMessageServers.Servers {
|
||||||
|
receivedContact.PullServers = append(receivedContact.PullServers, srv.ServerData)
|
||||||
|
}
|
||||||
|
// End simulating concact invitation response
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// Finalize the contact with the invitation response
|
||||||
|
//
|
||||||
|
me.FinalizeInvitation(myFirstFriend, &receivedContact)
|
||||||
|
err = me.Save("test.id")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
a, _ = json.Marshal(me)
|
||||||
|
ioutil.WriteFile("id.json", a, 0644)
|
||||||
|
fmt.Println(string(a))
|
||||||
|
}
|
||||||
|
//myFirstFriend.SetComMethod()
|
||||||
|
//msg := myFirstFriend.SendText()
|
||||||
|
|
||||||
|
}
|
5
go.mod
5
go.mod
@ -4,5 +4,10 @@ go 1.16
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.2.4
|
github.com/ProtonMail/gopenpgp/v2 v2.2.4
|
||||||
|
github.com/go-resty/resty/v2 v2.6.0
|
||||||
|
github.com/google/uuid v1.3.0
|
||||||
github.com/rs/zerolog v1.25.0
|
github.com/rs/zerolog v1.25.0
|
||||||
|
github.com/stretchr/testify v1.4.0
|
||||||
|
github.com/tidwall/gjson v1.10.2
|
||||||
|
google.golang.org/protobuf v1.27.1
|
||||||
)
|
)
|
||||||
|
20
go.sum
20
go.sum
@ -9,7 +9,14 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-resty/resty/v2 v2.6.0 h1:joIR5PNLM2EFqqESUjCMGXrWmXNHEU9CEiK813oKYS4=
|
||||||
|
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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
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=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@ -26,6 +33,12 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo=
|
||||||
|
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@ -45,6 +58,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -67,7 +81,13 @@ golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapK
|
|||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
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=
|
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.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||||
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
50
https.go
Normal file
50
https.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package meowlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Address string
|
||||||
|
|
||||||
|
func Configure(url string) {
|
||||||
|
Address = url
|
||||||
|
}
|
||||||
|
|
||||||
|
func Send(msg []byte) (string, error) {
|
||||||
|
client := resty.New().SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
|
|
||||||
|
resp, err := client.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(msg).
|
||||||
|
Post(Address + "/message/add/")
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg(err.Error())
|
||||||
|
}
|
||||||
|
serverUuid := gjson.Get(resp.String(), "serveruuid").String()
|
||||||
|
return serverUuid, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Receive(key string) []byte {
|
||||||
|
client := resty.New().SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
|
|
||||||
|
resp, err := client.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
Get(Address + "/message/" + key)
|
||||||
|
fmt.Println(" StatusCode :", resp.StatusCode())
|
||||||
|
fmt.Println(" Cookies :", resp.Cookies())
|
||||||
|
fmt.Println(" Error :", err)
|
||||||
|
msg := resp.Body()
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start(callback *func() []InternalMessage) {
|
||||||
|
for {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
87
identity.go
87
identity.go
@ -1,41 +1,86 @@
|
|||||||
package meow
|
package meowlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/gopenpgp/v2/helper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const key = "3pw0c8#6ZG8{75b5;3?fe80$2"
|
||||||
|
|
||||||
type Identity struct {
|
type Identity struct {
|
||||||
nickname string
|
Nickname string `json:"nickname,omitempty"`
|
||||||
PublicKey string
|
PublicKey string `json:"public_key,omitempty"`
|
||||||
PrivateKey string
|
PrivateKey string `json:"private_key,omitempty"`
|
||||||
Status string
|
Status string `json:"status,omitempty"`
|
||||||
Peers []Peer
|
Peers PeerList `json:"peers,omitempty"`
|
||||||
|
KnownServers ServerList `json:"known_servers,omitempty"`
|
||||||
|
MessageServers ServerList `json:"message_servers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Create(nickname string) Identity {
|
func CreateIdentity(nickname string) *Identity {
|
||||||
var id Identity
|
var id Identity
|
||||||
id.nickname = nickname
|
id.Nickname = nickname
|
||||||
kp := NewKeyPair()
|
kp := NewKeyPair()
|
||||||
id.PublicKey = kp.Public
|
id.PublicKey = kp.Public
|
||||||
id.PrivateKey = kp.Private
|
id.PrivateKey = kp.Private
|
||||||
return id
|
return &id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Identity) InvitePeer(name string) *Peer {
|
func (id *Identity) InvitePeer(myName string, contactName string, messageServerIdxs []int) (*Peer, *Contact) {
|
||||||
var peer Peer
|
var peer Peer
|
||||||
peer.Me = append(peer.Me, NewKeyPair())
|
var myContactCard Contact
|
||||||
peer.Name = name
|
peer.Me = NewKeyPair()
|
||||||
return &peer
|
peer.EncryptionKp = NewKeyPair()
|
||||||
|
peer.LookupKp = NewKeyPair()
|
||||||
|
peer.Name = contactName
|
||||||
|
for _, i := range messageServerIdxs {
|
||||||
|
myContactCard.PullServers = append(myContactCard.PullServers, id.MessageServers.Servers[i].ServerData)
|
||||||
|
}
|
||||||
|
myContactCard.Name = myName
|
||||||
|
myContactCard.ContactPublicKey = peer.Me.Public
|
||||||
|
myContactCard.EncryptionPublicKey = peer.EncryptionKp.Public
|
||||||
|
myContactCard.LookupPublicKey = peer.LookupKp.Public
|
||||||
|
|
||||||
|
id.Peers = append(id.Peers, peer)
|
||||||
|
|
||||||
|
return &id.Peers[len(id.Peers)-1], &myContactCard
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Identity) FinalizeInvitation(peer *Peer, peerPublicKey string) {
|
func (*Identity) FinalizeInvitation(peer *Peer, receivedContact *Contact) {
|
||||||
peer.PublicKey = peerPublicKey
|
peer.Contact = *receivedContact
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Identity) AddPeer(name string, peerPublicKey string) string {
|
func (*Identity) AddPeer(name string, peerPublicKey string) string {
|
||||||
var peer Peer
|
var peer Peer
|
||||||
peer.Me = append(peer.Me, NewKeyPair())
|
peer.Me = NewKeyPair()
|
||||||
peer.Name = name
|
peer.Contact.Name = name
|
||||||
peer.PublicKey = peerPublicKey
|
peer.Contact.ContactPublicKey = peerPublicKey
|
||||||
return peer.Me[len(peer.Me)-1].Public
|
return peer.Me.Public
|
||||||
}
|
}
|
||||||
|
|
||||||
func Save() {
|
func LoadIdentity(file string) (*Identity, error) {
|
||||||
|
var id Identity
|
||||||
|
indata, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pass, err := helper.DecryptMessageWithPassword([]byte(key), string(indata))
|
||||||
|
if err == nil {
|
||||||
|
err = json.Unmarshal([]byte(pass), &id)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (id *Identity) Save(file string) error {
|
||||||
|
b, _ := json.Marshal(id)
|
||||||
|
armor, err := helper.EncryptMessageWithPassword([]byte(key), string(b))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(file, []byte(armor), 0644)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
30
identity_test.go
Normal file
30
identity_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package meowlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreate(t *testing.T) {
|
||||||
|
id := CreateIdentity("myname")
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
42
keys.go
42
keys.go
@ -1,42 +0,0 @@
|
|||||||
package meow
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type KeyPair struct {
|
|
||||||
Public string
|
|
||||||
Private string
|
|
||||||
Generated time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type KeysArray []KeyPair
|
|
||||||
|
|
||||||
func NewKeyPair() KeyPair {
|
|
||||||
var kp KeyPair
|
|
||||||
keys, err := crypto.GenerateKey("name", "mail", "rsa", 4096)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug().Msg("Key generation failed")
|
|
||||||
}
|
|
||||||
kp.Generated = time.Now()
|
|
||||||
kp.Public, err = keys.GetArmoredPublicKey()
|
|
||||||
if err != nil {
|
|
||||||
log.Debug().Msg("Public key extraction failed")
|
|
||||||
}
|
|
||||||
kp.Private, err = keys.Armor()
|
|
||||||
if err != nil {
|
|
||||||
log.Debug().Msg("Private key extraction failed")
|
|
||||||
}
|
|
||||||
return kp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (keyPair *KeyPair) GetCryptoKeyObject() *crypto.Key {
|
|
||||||
key, err := crypto.NewKeyFromArmored(keyPair.Private)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug().Msg("Create key from armoured failed")
|
|
||||||
}
|
|
||||||
return key
|
|
||||||
}
|
|
45
message.go
45
message.go
@ -1,45 +0,0 @@
|
|||||||
package meow
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/gopenpgp/v2/helper"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
To string
|
|
||||||
From string
|
|
||||||
Data []byte
|
|
||||||
Sent time.Time
|
|
||||||
Received time.Time
|
|
||||||
Read time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateFromText(peer Peer, text string) *Message {
|
|
||||||
var msg Message
|
|
||||||
var err error
|
|
||||||
msg.To = peer.PublicKey
|
|
||||||
msg.From = peer.Me[len(peer.Me)-1].Public
|
|
||||||
msg.Data = []byte{byte('t')}
|
|
||||||
armor, err := helper.EncryptMessageArmored(msg.To, text)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug().Msg("Message encryption failed")
|
|
||||||
}
|
|
||||||
msg.Data = append(msg.Data, []byte(armor)...)
|
|
||||||
msg.Sent = time.Now()
|
|
||||||
return &msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Message) GetType() string {
|
|
||||||
|
|
||||||
return "msg"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *Message) GetText(peer Peer) string {
|
|
||||||
decrypted, err := helper.DecryptMessageArmored(peer.Me[len(peer.Me)-1].Private, []byte(""), string(msg.Data))
|
|
||||||
if err != nil {
|
|
||||||
log.Debug().Msg("Message decryption failed")
|
|
||||||
}
|
|
||||||
return decrypted
|
|
||||||
}
|
|
9
network.go
Normal file
9
network.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package meowlib
|
||||||
|
|
||||||
|
type Network interface {
|
||||||
|
Configure(url string)
|
||||||
|
Send(msg []byte)
|
||||||
|
Receive(request []byte) []byte
|
||||||
|
Start()
|
||||||
|
Stop()
|
||||||
|
}
|
90
pb/messages.proto
Normal file
90
pb/messages.proto
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package meowlib;
|
||||||
|
option go_package = "forge.redroom.link/yves/meowlib";
|
||||||
|
|
||||||
|
message PackedServerMessage {
|
||||||
|
string From = 1;
|
||||||
|
bytes Payload = 2;
|
||||||
|
string Signature = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PackedUserMessage {
|
||||||
|
string From = 1;
|
||||||
|
string Destination=2;
|
||||||
|
bytes Payload=3;
|
||||||
|
bytes Signature=4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ServerMessage {
|
||||||
|
string Type = 1;
|
||||||
|
string ServerPubKey = 2 ;
|
||||||
|
bytes Payload = 3 ;
|
||||||
|
uint64 ServerReceived = 4 ;
|
||||||
|
string ServerUuid = 5 ;
|
||||||
|
|
||||||
|
message ConversationRequest {
|
||||||
|
string ccid = 1;
|
||||||
|
string LastUuidOK = 2;
|
||||||
|
bool PublishOnline = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConversationResponse {
|
||||||
|
repeated string MessageUuids = 1;
|
||||||
|
string SourceIpAddress = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
repeated ConversationRequest PollRequest = 7;
|
||||||
|
map<string,ConversationResponse> PollResponse = 8;
|
||||||
|
|
||||||
|
message PostedMessage{
|
||||||
|
string ccid= 1;
|
||||||
|
repeated PackedUserMessage Messages = 2;
|
||||||
|
}
|
||||||
|
repeated PostedMessage Messages = 9;
|
||||||
|
string NextServerKey = 10;
|
||||||
|
string Url = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Server {
|
||||||
|
string Name = 1;
|
||||||
|
string Description=2;
|
||||||
|
string PublicKey = 3;
|
||||||
|
string Url = 4;
|
||||||
|
int32 ConfidenceLevel = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MinimalContact {
|
||||||
|
string name=1;
|
||||||
|
string publicKey=2;
|
||||||
|
repeated Server TrustedServers = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message UserMessage {
|
||||||
|
|
||||||
|
string Destination = 1;
|
||||||
|
string From = 2;
|
||||||
|
string Type = 3;
|
||||||
|
bytes Data = 4;
|
||||||
|
message ConversationStatus {
|
||||||
|
string LocalUuid = 1;
|
||||||
|
uint64 LocalSequence = 2 ;
|
||||||
|
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
|
||||||
|
}
|
||||||
|
ConversationStatus Status = 5;
|
||||||
|
|
||||||
|
|
||||||
|
MinimalContact contact = 6;
|
||||||
|
|
||||||
|
message Group{
|
||||||
|
string name=1;
|
||||||
|
repeated MinimalContact members = 2;
|
||||||
|
}
|
||||||
|
Group group = 7;
|
||||||
|
}
|
4
pb/protogen.sh
Executable file
4
pb/protogen.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
protoc -I=. --go_out=.. messages.proto
|
||||||
|
mv ../forge.redroom.link/yves/meowlib/messages.pb.go ../
|
||||||
|
rm -rf ../forge.redroom.link
|
59
peer.go
59
peer.go
@ -1,27 +1,60 @@
|
|||||||
package meow
|
package meowlib
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Contact struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
ContactPublicKey string `json:"contact_public_key,omitempty"`
|
||||||
|
EncryptionPublicKey string `json:"encryption_public_key,omitempty"`
|
||||||
|
LookupPublicKey string `json:"lookup_public_key,omitempty"`
|
||||||
|
PullServers []Server `json:"pull_servers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
Me KeysArray
|
Name string `json:"name,omitempty"`
|
||||||
Name string
|
Me KeyPair `json:"me,omitempty"`
|
||||||
Visible bool
|
Contact Contact `json:"contact,omitempty"`
|
||||||
Password string
|
Visible bool `json:"visible,omitempty"`
|
||||||
MessageNotification string
|
VisiblePassword string `json:"visible_password,omitempty"`
|
||||||
PublicKey string
|
PasswordType string `json:"password_type,omitempty"`
|
||||||
PushServers []Server
|
Blocked bool `json:"blocked,omitempty"`
|
||||||
OnionMode bool
|
MessageNotification string `json:"message_notification,omitempty"`
|
||||||
Convdersation []Message
|
OnionMode bool `json:"onion_mode,omitempty"`
|
||||||
LastMessage time.Time
|
Conversation []InternalMessage `json:"convdersation,omitempty"`
|
||||||
|
LastMessage time.Time `json:"last_message,omitempty"`
|
||||||
|
EncryptionKp KeyPair `json:"conversation_kp,omitempty"`
|
||||||
|
LookupKp KeyPair `json:"lookup_kp,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeerList []Peer
|
type PeerList []Peer
|
||||||
|
|
||||||
|
type Group struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Members []Contact `json:"members,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func (pl *PeerList) GetFromPublicKey(publickey string) *Peer {
|
func (pl *PeerList) GetFromPublicKey(publickey string) *Peer {
|
||||||
for _, peer := range *pl {
|
for _, peer := range *pl {
|
||||||
if peer.PublicKey == publickey {
|
if peer.Contact.ContactPublicKey == publickey {
|
||||||
return &peer
|
return &peer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pl *PeerList) GetFromName(name string) *Peer {
|
||||||
|
for _, peer := range *pl {
|
||||||
|
if peer.Contact.Name == name {
|
||||||
|
return &peer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (peer *Peer) SendText(text string) {
|
||||||
|
im := CreateText(*peer, text)
|
||||||
|
fmt.Println(im.MessageData.Destination)
|
||||||
|
}
|
||||||
|
10
peer_test.go
Normal file
10
peer_test.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package meowlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetFromPublicKey(t *testing.T) {
|
||||||
|
id := CreateIdentity("test")
|
||||||
|
id.Save("test.id")
|
||||||
|
}
|
36
proto_test.go
Normal file
36
proto_test.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package meowlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServerMessageSerialization(t *testing.T) {
|
||||||
|
var msg PackedServerMessage
|
||||||
|
msg.From = "toto"
|
||||||
|
msg.Payload = []byte("mon texte")
|
||||||
|
msg.Signature = "moi"
|
||||||
|
out, err := proto.Marshal(&msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to encode address book:", err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile("test", out, 0644); err != nil {
|
||||||
|
log.Fatalln("Failed to write address book:", err)
|
||||||
|
}
|
||||||
|
in, err := ioutil.ReadFile("test")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Error reading file:", err)
|
||||||
|
}
|
||||||
|
rmsg := &PackedServerMessage{}
|
||||||
|
if err := proto.Unmarshal(in, rmsg); err != nil {
|
||||||
|
log.Fatalln("Failed to parse address book:", err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, msg.From, rmsg.From, "The two words should be the same.")
|
||||||
|
assert.Equal(t, msg.Payload, rmsg.Payload, "The two words should be the same.")
|
||||||
|
assert.Equal(t, msg.Signature, rmsg.Signature, "The two words should be the same.")
|
||||||
|
|
||||||
|
}
|
34
server.go
34
server.go
@ -1,8 +1,30 @@
|
|||||||
package meow
|
package meowlib
|
||||||
|
|
||||||
type Server struct {
|
import "time"
|
||||||
Name string
|
|
||||||
PublicKey string
|
type InternalServer struct {
|
||||||
Address string
|
ServerData Server `json:"server_data,omitempty"`
|
||||||
Port string
|
Presence bool `json:"presence,omitempty"`
|
||||||
|
LastCheck time.Time `json:"last_check,omitempty"`
|
||||||
|
Uptime time.Duration `json:"uptime,omitempty"`
|
||||||
|
Login string `json:"login,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Me KeyPair `json:"me,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerList struct {
|
||||||
|
Name string
|
||||||
|
Servers []InternalServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerFromUrl(url string) *InternalServer {
|
||||||
|
var is InternalServer
|
||||||
|
is.ServerData.Url = url
|
||||||
|
return &is
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *ServerList) AddUrls(urls []string) {
|
||||||
|
for _, url := range urls {
|
||||||
|
sl.Servers = append(sl.Servers, *ServerFromUrl(url))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
54
servermessage.go
Normal file
54
servermessage.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package meowlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MessagesType = 1
|
||||||
|
const PollRequestType = 1
|
||||||
|
const PollResponseType = 1
|
||||||
|
const MtrkType = 1
|
||||||
|
|
||||||
|
func (msg *ServerMessage) Pack() *PackedServerMessage {
|
||||||
|
var pck PackedServerMessage
|
||||||
|
|
||||||
|
jsonMsg, _ := json.Marshal(msg)
|
||||||
|
armor, err := Encrypt(msg.ServerPubKey, jsonMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message encryption failed")
|
||||||
|
}
|
||||||
|
pck.Payload = []byte(armor)
|
||||||
|
return &pck
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pck *PackedServerMessage) Unpack(privateKey string) *ServerMessage {
|
||||||
|
var msg *ServerMessage
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateMtrkChainServerMessage([]Server, []PackedUserMessage) *PackedServerMessage {
|
||||||
|
var msg PackedServerMessage
|
||||||
|
return &msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ServerMessage) Parse() {
|
||||||
|
var pck PackedServerMessage
|
||||||
|
|
||||||
|
jsonMsg, _ := json.Marshal(msg)
|
||||||
|
armor, err := Encrypt(msg.ServerPubKey, jsonMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Message encryption failed")
|
||||||
|
}
|
||||||
|
pck.Payload = []byte(armor)
|
||||||
|
}
|
60
usermessage.go
Normal file
60
usermessage.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
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
|
||||||
|
}
|
37
usermessage_test.go
Normal file
37
usermessage_test.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user