Adding V3 Onions to Cwtch!

pull/133/head
Sarah Jamie Lewis 4 years ago
parent 8ab4752b44
commit 1e04b1161e
  1. 4
      .gitignore
  2. 26
      app/cli/main.go
  3. 1
      app/peer/alice/alice.go
  4. 2
      app/peer/bob/bob.go
  5. 13
      model/group.go
  6. 6
      model/message.go
  7. 37
      model/profile.go
  8. 4
      model/profile_test.go
  9. 2
      peer/connections/peerpeerconnection.go
  10. 27
      peer/connections/peerpeerconnection_test.go
  11. 7
      peer/connections/peerserverconnection.go
  12. 25
      peer/connections/peerserverconnection_test.go
  13. 29
      peer/cwtch_peer.go
  14. 2
      peer/peer/peer_channel.go
  15. 2
      peer/peer/peer_channel_test.go
  16. 2
      peer/peer/peer_data_channel.go
  17. 10
      server/server.go
  18. 75
      server/serverConfig.go
  19. 1
      storage/profile_store.go
  20. 57
      testing/cwtch_peer_server_intergration_test.go

4
.gitignore vendored

@ -5,3 +5,7 @@
*.test
*/*test_*
*/*_test*
*.json
*/messages/*
server/app/messages
.reviewboardrc

@ -80,11 +80,6 @@ var usages = map[string]string{
}
func printMessage(m model.Message) {
verified := "not-verified"
if m.Verified {
verified = "verified"
}
p := peer.GetContact(m.PeerID)
name := "unknown"
if p != nil {
@ -93,7 +88,7 @@ func printMessage(m model.Message) {
name = peer.GetProfile().Name
}
fmt.Printf("%v %v (%v): %v [%s]\n", m.Timestamp, name, m.PeerID, m.Message, verified)
fmt.Printf("%v %v (%v): %v\n", m.Timestamp, name, m.PeerID, m.Message)
}
func startGroupFollow() {
@ -365,6 +360,7 @@ func main() {
} else {
fmt.Printf("\nError loading profiles: %v\n", err)
}
case "/list-profiles":
peerlist := app.ListPeers()
for onion, peername := range peerlist {
@ -380,6 +376,24 @@ func main() {
peer = p
suggestions = append(suggestionsBase, suggestionsSelectedProfile...)
}
// Auto Peer / Join Server
// TODO There are some privacy implications with this that we should
// think over.
for _, name := range p.GetProfile().GetContacts() {
profile := p.GetContact(name)
if profile.Trusted && !profile.Blocked {
p.PeerWithOnion(profile.Onion)
}
}
for _, groupid := range p.GetGroups() {
group := p.GetGroup(groupid)
if group.Accepted || group.Owner == "self" {
p.JoinServer(group.GroupServer)
}
}
} else {
fmt.Printf("Error selecting profile, usage: %s\n", usages[commands[0]])
}

@ -13,7 +13,6 @@ func main() {
return data
}
alice.SetPeerDataHandler(processData)
alice.Listen()
}

@ -16,7 +16,7 @@ func main() {
counter++
return []byte(strconv.Itoa(counter))
})
connection := bob.PeerWithOnion("qtpnmnth767gjmpv")
connection := bob.PeerWithOnion("f4b6thuwmfszsqd3fzqpr45sdem4qoazdlzr2xmnc7fq22qe746hjqqd")
log.Printf("Waiting for Bob to Connect to Alice...")
connection.SendPacket([]byte("Hello Alice!!!"))

@ -14,7 +14,7 @@ import (
"time"
)
//Group defines and encapsulates Cwtch's conception of group chat. Which are sessions
// Group defines and encapsulates Cwtch's conception of group chat. Which are sessions
// tied to a server under a given group key. Each group has a set of messages.
type Group struct {
GroupID string
@ -27,6 +27,7 @@ type Group struct {
IsCompromised bool
InitialMessage []byte
lock sync.Mutex
NewMessage chan Message `json:"-"`
}
// NewGroup initializes a new group associated with a given CwtchServer
@ -94,19 +95,23 @@ func (g *Group) Invite(initialMessage []byte) ([]byte, error) {
}
// AddMessage takes a DecryptedGroupMessage and adds it to the Groups Timeline
func (g *Group) AddMessage(message *protocol.DecryptedGroupMessage, sig []byte, verified bool) *Message {
func (g *Group) AddMessage(message *protocol.DecryptedGroupMessage, sig []byte) *Message {
g.lock.Lock()
timelineMessage := &Message{
Message: message.GetText(),
Timestamp: time.Unix(int64(message.GetTimestamp()), 0),
Received: time.Now(),
Signature: sig,
Verified: verified,
PeerID: message.GetOnion(),
PreviousMessageSig: message.GetPreviousMessageSig(),
}
g.Timeline.Insert(timelineMessage)
seen := g.Timeline.Insert(timelineMessage)
g.lock.Unlock()
// Send a new Message notification if we have an app that is listening.
if g.NewMessage != nil && !seen {
g.NewMessage <- *timelineMessage
}
return timelineMessage
}

@ -21,7 +21,6 @@ type Message struct {
PeerID string
Message string
Signature []byte
Verified bool
PreviousMessageSig []byte
}
@ -75,17 +74,18 @@ func (t *Timeline) Less(i, j int) bool {
}
// Insert inserts a message into the timeline in a thread safe way.
func (t *Timeline) Insert(mi *Message) {
func (t *Timeline) Insert(mi *Message) bool {
t.lock.Lock()
defer t.lock.Unlock()
for _, m := range t.Messages {
// If the message already exists, then we don't add it
if compareSignatures(m.Signature, mi.Signature) {
return
return true
}
}
t.Messages = append(t.Messages, *mi)
sort.Sort(t)
return false
}

@ -2,14 +2,14 @@ package model
import (
"crypto/rand"
"crypto/rsa"
"cwtch.im/cwtch/protocol"
"encoding/asn1"
"encoding/base32"
"errors"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"github.com/golang/protobuf/proto"
"golang.org/x/crypto/ed25519"
"io"
"strings"
"sync"
"time"
)
@ -28,7 +28,6 @@ type Profile struct {
PublicProfile
Contacts map[string]*PublicProfile
Ed25519PrivateKey ed25519.PrivateKey
OnionPrivateKey *rsa.PrivateKey
Groups map[string]*Group
Custom map[string]string
lock sync.Mutex
@ -41,14 +40,7 @@ func GenerateNewProfile(name string) *Profile {
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
p.Ed25519PublicKey = pub
p.Ed25519PrivateKey = priv
p.OnionPrivateKey, _ = utils.GeneratePrivateKey()
// DER Encode the Public Key
publicKeyBytes, _ := asn1.Marshal(rsa.PublicKey{
N: p.OnionPrivateKey.PublicKey.N,
E: p.OnionPrivateKey.PublicKey.E,
})
p.Onion = utils.GetTorHostname(publicKeyBytes)
p.Onion = utils.GetTorV3Hostname(pub)
p.Contacts = make(map[string]*PublicProfile)
p.Contacts[p.Onion] = &p.PublicProfile
@ -199,10 +191,10 @@ func (p *Profile) VerifyGroupMessage(onion string, groupID string, message strin
return ed25519.Verify(p.Ed25519PublicKey, []byte(m), signature)
}
contact, found := p.GetContact(onion)
if found {
m := groupID + group.GroupServer + string(ciphertext)
return ed25519.Verify(contact.Ed25519PublicKey, []byte(m), signature)
m := groupID + group.GroupServer + string(ciphertext)
decodedPub, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
if err == nil {
return ed25519.Verify(decodedPub[:32], []byte(m), signature)
}
return false
}
@ -213,13 +205,13 @@ func (p *Profile) SignMessage(message string) []byte {
return sig
}
//StartGroup when given a server, creates a new Group under this profile and returns the group id an a precomputed
// StartGroup when given a server, creates a new Group under this profile and returns the group id an a precomputed
// invite which can be sent on the wire.
func (p *Profile) StartGroup(server string) (groupID string, invite []byte, err error) {
return p.StartGroupWithMessage(server, []byte{})
}
//StartGroupWithMessage when given a server, and an initial message creates a new Group under this profile and returns the group id an a precomputed
// StartGroupWithMessage when given a server, and an initial message creates a new Group under this profile and returns the group id an a precomputed
// invite which can be sent on the wire.
func (p *Profile) StartGroupWithMessage(server string, initialMessage []byte) (groupID string, invite []byte, err error) {
group, err := NewGroup(server)
@ -301,7 +293,16 @@ func (p *Profile) AttemptDecryption(ciphertext []byte, signature []byte) (bool,
}
verified := p.VerifyGroupMessage(dgm.GetOnion(), group.GroupID, dgm.GetText(), dgm.GetTimestamp(), ciphertext, signature)
return true, group.AddMessage(dgm, signature, verified)
// So we have a message that has a valid group key, but the signature can't be verified.
// The most obvious explanation for this is that the group key has been compromised (or we are in an open group and the server is being malicious)
// Either way, someone who has the private key is being detectably bad so we are just going to throw this message away and mark the group as Compromised.
if !verified {
group.Compromised()
return false, nil
}
return true, group.AddMessage(dgm, signature)
}
}
return false, nil

@ -133,8 +133,8 @@ func TestProfileGroup(t *testing.T) {
c3, s3, err := bob.EncryptMessageToGroup("Bobs Message", group2.GroupID)
if err == nil {
ok, message := alice.AttemptDecryption(c3, s3)
if ok != true || message.Verified == true {
t.Errorf("Bobs message to the group should be decrypted but not verified by alice instead %v %v", message, ok)
if !ok {
t.Errorf("Bobs message to the group should be decrypted %v %v", message, ok)
}
eve := GenerateNewProfile("eve")

@ -107,7 +107,7 @@ func (ppc *PeerPeerConnection) Run() error {
rc.TraceLog(false)
ppc.connection = rc
ppc.state = CONNECTED
_, err := connection.HandleOutboundConnection(ppc.connection).ProcessAuthAsClient(identity.Initialize(ppc.profile.Name, ppc.profile.OnionPrivateKey))
_, err := connection.HandleOutboundConnection(ppc.connection).ProcessAuthAsV3Client(identity.InitializeV3(ppc.profile.Name, &ppc.profile.Ed25519PrivateKey, &ppc.profile.Ed25519PublicKey))
if err == nil {
ppc.state = AUTHENTICATED
go func() {

@ -1,7 +1,7 @@
package connections
import (
"crypto/rsa"
"crypto/rand"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/peer/peer"
"cwtch.im/cwtch/protocol"
@ -9,17 +9,17 @@ import (
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"golang.org/x/crypto/ed25519"
"net"
"testing"
"time"
)
func PeerAuthValid(string, rsa.PublicKey) (allowed, known bool) {
func PeerAuthValid(hostname string, key ed25519.PublicKey) (allowed, known bool) {
return true, true
}
func runtestpeer(t *testing.T, tp *TestPeer, privateKey *rsa.PrivateKey) {
func runtestpeer(t *testing.T, tp *TestPeer, identity identity.Identity) {
ln, _ := net.Listen("tcp", "127.0.0.1:5452")
conn, _ := ln.Accept()
defer conn.Close()
@ -29,7 +29,7 @@ func runtestpeer(t *testing.T, tp *TestPeer, privateKey *rsa.PrivateKey) {
t.Errorf("Negotiate Version Error: %v", err)
}
rc.TraceLog(true)
err = connection.HandleInboundConnection(rc).ProcessAuthAsServer(identity.Initialize("", privateKey), PeerAuthValid)
err = connection.HandleInboundConnection(rc).ProcessAuthAsV3Server(identity, PeerAuthValid)
if err != nil {
t.Errorf("ServerAuth Error: %v", err)
}
@ -76,21 +76,16 @@ func (tp *TestPeer) GetClientIdentityPacket() []byte {
}
func TestPeerPeerConnection(t *testing.T) {
privateKey, err := utils.GeneratePrivateKey()
if err != nil {
t.Errorf("Private Key Error %v", err)
}
onionAddr, err := utils.GetOnionAddress(privateKey)
if err != nil {
t.Errorf("Onion address error %v", err)
}
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
identity := identity.InitializeV3("", &priv, &pub)
profile := model.GenerateNewProfile("alice")
ppc := NewPeerPeerConnection("127.0.0.1:5452|"+onionAddr, profile, nil)
//numcalls := 0
hostname := identity.Hostname()
ppc := NewPeerPeerConnection("127.0.0.1:5452|"+hostname, profile, nil)
tp := new(TestPeer)
tp.Init()
go runtestpeer(t, tp, privateKey)
go runtestpeer(t, tp, identity)
state := ppc.GetState()
if state != DISCONNECTED {
t.Errorf("new connections should start in disconnected state")

@ -1,6 +1,7 @@
package connections
import (
"crypto/rand"
"cwtch.im/cwtch/peer/fetch"
"cwtch.im/cwtch/peer/listen"
"cwtch.im/cwtch/peer/send"
@ -10,7 +11,7 @@ import (
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"golang.org/x/crypto/ed25519"
"log"
"time"
)
@ -46,9 +47,9 @@ func (psc *PeerServerConnection) Run() error {
rc.TraceLog(true)
psc.connection = rc
psc.state = CONNECTED
pk, err := utils.GeneratePrivateKey()
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
if err == nil {
_, err := connection.HandleOutboundConnection(psc.connection).ProcessAuthAsClient(identity.Initialize("cwtchpeer", pk))
_, err := connection.HandleOutboundConnection(psc.connection).ProcessAuthAsV3Client(identity.InitializeV3("cwtchpeer", &priv, &pub))
if err == nil {
psc.state = AUTHENTICATED

@ -1,7 +1,7 @@
package connections
import (
"crypto/rsa"
"crypto/rand"
"cwtch.im/cwtch/protocol"
"cwtch.im/cwtch/server/fetch"
"cwtch.im/cwtch/server/send"
@ -9,13 +9,13 @@ import (
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"golang.org/x/crypto/ed25519"
"net"
"testing"
"time"
)
func ServerAuthValid(string, rsa.PublicKey) (allowed, known bool) {
func ServerAuthValid(hostname string, key ed25519.PublicKey) (allowed, known bool) {
return true, true
}
@ -32,7 +32,7 @@ func (ts *TestServer) HandleFetchRequest() []*protocol.GroupMessage {
return []*protocol.GroupMessage{{Ciphertext: []byte("hello"), Signature: []byte{}, Spamguard: []byte{}}, {Ciphertext: []byte("hello"), Signature: []byte{}, Spamguard: []byte{}}}
}
func runtestserver(t *testing.T, ts *TestServer, privateKey *rsa.PrivateKey) {
func runtestserver(t *testing.T, ts *TestServer, identity identity.Identity) {
ln, _ := net.Listen("tcp", "127.0.0.1:5451")
conn, _ := ln.Accept()
defer conn.Close()
@ -42,7 +42,7 @@ func runtestserver(t *testing.T, ts *TestServer, privateKey *rsa.PrivateKey) {
t.Errorf("Negotiate Version Error: %v", err)
}
rc.TraceLog(true)
err = connection.HandleInboundConnection(rc).ProcessAuthAsServer(identity.Initialize("", privateKey), ServerAuthValid)
err = connection.HandleInboundConnection(rc).ProcessAuthAsV3Server(identity, ServerAuthValid)
if err != nil {
t.Errorf("ServerAuth Error: %v", err)
}
@ -63,18 +63,15 @@ func runtestserver(t *testing.T, ts *TestServer, privateKey *rsa.PrivateKey) {
}
func TestPeerServerConnection(t *testing.T) {
privateKey, err := utils.GeneratePrivateKey()
if err != nil {
t.Errorf("Private Key Error %v", err)
}
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
identity := identity.InitializeV3("", &priv, &pub)
ts := new(TestServer)
ts.Init()
go runtestserver(t, ts, privateKey)
onionAddr, err := utils.GetOnionAddress(privateKey)
if err != nil {
t.Errorf("Error getting onion address: %v", err)
}
go runtestserver(t, ts, identity)
onionAddr := identity.Hostname()
psc := NewPeerServerConnection("127.0.0.1:5451|" + onionAddr)
numcalls := 0
psc.GroupMessageHandler = func(s string, gm *protocol.GroupMessage) {

@ -14,6 +14,8 @@ import (
"git.openprivacy.ca/openprivacy/libricochet-go/application"
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"github.com/golang/protobuf/proto"
"github.com/ulule/deepcopier"
"golang.org/x/crypto/ed25519"
@ -135,18 +137,6 @@ func (cp *cwtchPeer) setup() {
cp.Init()
go cp.connectionsManager.AttemptReconnections()
for onion, profile := range cp.Profile.Contacts {
if profile.Trusted && !profile.Blocked {
cp.PeerWithOnion(onion)
}
}
for _, group := range cp.Profile.Groups {
if group.Accepted || group.Owner == "self" {
cp.JoinServer(group.GroupServer)
}
}
}
// NewCwtchPeer creates and returns a new cwtchPeer with the given name.
@ -209,16 +199,16 @@ func LoadCwtchPeer(profilefile string, password string) (CwtchPeer, error) {
// ImportGroup intializes a group from an imported source rather than a peer invite
func (cp *cwtchPeer) ImportGroup(exportedInvite string) (groupID string, err error) {
if strings.HasPrefix(exportedInvite, "torv2") {
data, err := base64.StdEncoding.DecodeString(exportedInvite[21+44:])
if strings.HasPrefix(exportedInvite, "torv3") {
data, err := base64.StdEncoding.DecodeString(exportedInvite[5+44:])
if err == nil {
cpp := &protocol.CwtchPeerPacket{}
err := proto.Unmarshal(data, cpp)
if err == nil {
pk, err := base64.StdEncoding.DecodeString(exportedInvite[21 : 21+44])
pk, err := base64.StdEncoding.DecodeString(exportedInvite[5 : 5+44])
if err == nil {
edpk := ed25519.PublicKey(pk)
onion := exportedInvite[5:21]
onion := utils.GetTorV3Hostname(edpk)
cp.Profile.AddContact(onion, &model.PublicProfile{Name: "", Ed25519PublicKey: edpk, Trusted: true, Blocked: false, Onion: onion})
cp.Profile.ProcessInvite(cpp.GetGroupChatInvite(), onion)
return cpp.GroupChatInvite.GetGroupName(), nil
@ -242,7 +232,7 @@ func (cp *cwtchPeer) ExportGroup(groupID string) (string, error) {
if group != nil {
invite, err := group.Invite(group.GetInitialMessage())
if err == nil {
exportedInvite := "torv2" + cp.Profile.Onion + base64.StdEncoding.EncodeToString(cp.Profile.Ed25519PublicKey) + base64.StdEncoding.EncodeToString(invite)
exportedInvite := "torv3" + base64.StdEncoding.EncodeToString(cp.Profile.Ed25519PublicKey) + base64.StdEncoding.EncodeToString(invite)
return exportedInvite, err
}
}
@ -404,8 +394,7 @@ func (cp *cwtchPeer) ContactRequest(name string, message string) string {
// Listen sets up an onion listener to process incoming cwtch messages
func (cp *cwtchPeer) Listen() error {
cwtchpeer := new(application.RicochetApplication)
l, err := application.SetupOnion("127.0.0.1:9051", "tcp4", "", cp.Profile.OnionPrivateKey, 9878)
l, err := application.SetupOnionV3("127.0.0.1:9051", "tcp4", "", cp.Profile.Ed25519PrivateKey, 9878)
if err != nil {
return err
}
@ -434,7 +423,7 @@ func (cp *cwtchPeer) Listen() error {
})
}
cwtchpeer.Init(cp.Profile.Name, cp.Profile.OnionPrivateKey, af, cp)
cwtchpeer.InitV3(cp.Profile.Name, identity.InitializeV3(cp.Profile.Name, &cp.Profile.Ed25519PrivateKey, &cp.Profile.Ed25519PublicKey), af, cp)
log.Printf("Running cwtch peer on %v", l.Addr().String())
cp.app = cwtchpeer
cwtchpeer.Run(l)

@ -61,7 +61,7 @@ func (cpc *CwtchPeerChannel) Bidirectional() bool {
// RequiresAuthentication - Cwtch channels require hidden service auth
func (cpc *CwtchPeerChannel) RequiresAuthentication() string {
return "im.ricochet.auth.hidden-service"
return "im.ricochet.auth.3dh"
}
// OpenInbound is the first method called for an inbound channel request.

@ -26,7 +26,7 @@ func TestPeerChannelAttributes(t *testing.T) {
t.Errorf("im.cwtch.server.listen should be a Singleton")
}
if cssc.RequiresAuthentication() != "im.ricochet.auth.hidden-service" {
if cssc.RequiresAuthentication() != "im.ricochet.auth.3dh" {
t.Errorf("cwtch channel required auth is incorrect %v", cssc.RequiresAuthentication())
}
}

@ -56,7 +56,7 @@ func (cpc *CwtchPeerDataChannel) Bidirectional() bool {
// RequiresAuthentication - Cwtch channels require hidden service auth
func (cpc *CwtchPeerDataChannel) RequiresAuthentication() string {
return "im.ricochet.auth.hidden-service"
return "im.ricochet.auth.3dh"
}
// OpenInbound is the first method called for an inbound channel request.

@ -14,18 +14,18 @@ import (
// Server encapsulates a complete, compliant Cwtch server.
type Server struct {
app *application.RicochetApplication
config *Config
config Config
metricsPack metrics.Monitors
}
// Run starts a server with the given privateKey
// TODO: surface errors
func (s *Server) Run(serverConfig *Config) {
func (s *Server) Run(serverConfig Config) {
s.config = serverConfig
cwtchserver := new(application.RicochetApplication)
s.metricsPack.Start(cwtchserver, s.config.ServerReporting.LogMetricsToFile)
l, err := application.SetupOnion("127.0.0.1:9051", "tcp4", "", s.config.PrivateKey(), 9878)
l, err := application.SetupOnionV3("127.0.0.1:9051", "tcp4", "", s.config.PrivateKey, 9878)
if err != nil {
log.Fatalf("error setting up onion service: %v", err)
@ -65,8 +65,8 @@ func (s *Server) Run(serverConfig *Config) {
}
})
cwtchserver.Init("cwtch server for "+l.Addr().String()[0:16], s.config.PrivateKey(), af, new(application.AcceptAllContactManager))
log.Printf("cwtch server running on cwtch:%s", l.Addr().String()[0:16])
cwtchserver.InitV3("cwtch server for "+l.Addr().String(), s.config.Identity(), af, new(application.AcceptAllContactManager))
log.Printf("cwtch server running on cwtch:%s", l.Addr().String())
s.app = cwtchserver
s.app.Run(l)
}

@ -1,60 +1,45 @@
package server
import (
"crypto/rsa"
"crypto/rand"
"encoding/json"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"golang.org/x/crypto/ed25519"
"io/ioutil"
"log"
"sync"
)
// Reporting is a struct for storing a the config a server needs to be a peer, and connect to a group to report
type Reporting struct {
LogMetricsToFile bool `json:"logMetricsToFile"`
PeerPrivateKey string `json:"privateKey"`
ReportingGroupID string `json:"reportingGroupId"`
ReportingServerAddr string `json:"reportingServerAddr"`
}
// Config is a struct for storing basic server configuration
type Config struct {
MaxBufferLines int `json:"maxBufferLines"`
PrivateKeyBytes string `json:"privateKey"`
ServerReporting Reporting `json:"serverReporting"`
lock sync.Mutex
MaxBufferLines int `json:"maxBufferLines"`
PublicKey ed25519.PublicKey `json:"publicKey"`
PrivateKey ed25519.PrivateKey `json:"privateKey"`
ServerReporting Reporting `json:"serverReporting"`
}
// PrivateKey returns an rsa.PrivateKey generated from the config's PrivateKeyBytes
func (config *Config) PrivateKey() *rsa.PrivateKey {
pk, err := utils.ParsePrivateKey([]byte(config.PrivateKeyBytes))
if err != nil {
log.Println("Error parsing private key: ", err)
}
return pk
// Identity returns an encapsulation of the servers keys for running ricochet
func (config *Config) Identity() identity.Identity {
return identity.InitializeV3("", &config.PrivateKey, &config.PublicKey)
}
// Save dumps the latest version of the config to a json file given by filename
func (config *Config) Save(filename string) {
config.lock.Lock()
defer config.lock.Unlock()
bytes, _ := json.MarshalIndent(config, "", "\t")
ioutil.WriteFile(filename, bytes, 0600)
}
// newConfig generates a simple config with defaults. Unmarshal will return them if they aren't specified
func newConfig() *Config {
// LoadConfig loads a Config from a json file specified by filename
func LoadConfig(filename string) Config {
config := Config{}
config.MaxBufferLines = 100000
config.ServerReporting.LogMetricsToFile = false
return &config
}
// LoadConfig loads a Config from a json file specified by filename
func LoadConfig(filename string) *Config {
config := newConfig()
raw, err := ioutil.ReadFile(filename)
if err == nil {
err = json.Unmarshal(raw, &config)
@ -64,39 +49,11 @@ func LoadConfig(filename string) *Config {
}
}
configAutoPopulate(config)
if config.PrivateKey == nil {
config.PublicKey, config.PrivateKey, _ = ed25519.GenerateKey(rand.Reader)
}
// Always save (first time generation, new version with new variables populated)
config.Save(filename)
return config
}
// Auto populate required values if missing and save
func configAutoPopulate(config *Config) {
if config.PrivateKeyBytes == "" {
config.generatePrivateKey()
}
if config.ServerReporting.PeerPrivateKey == "" {
config.generatePeerPrivateKey()
}
}
func (config *Config) generatePrivateKey() {
pk, err := utils.GeneratePrivateKey()
if err != nil {
log.Fatalf("error generating new private key: %v\n", err)
}
config.lock.Lock()
config.PrivateKeyBytes = utils.PrivateKeyToString(pk)
config.lock.Unlock()
}
func (config *Config) generatePeerPrivateKey() {
pk, err := utils.GeneratePrivateKey()
if err != nil {
log.Fatalf("error generating new peer private key: %v\n", err)
}
config.lock.Lock()
config.ServerReporting.PeerPrivateKey = utils.PrivateKeyToString(pk)
config.lock.Unlock()
}

@ -0,0 +1 @@
package storage

@ -1,17 +1,14 @@
package testing
import (
"crypto/rsa"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/peer"
"cwtch.im/cwtch/peer/connections"
cwtchserver "cwtch.im/cwtch/server"
"fmt"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"golang.org/x/net/proxy"
"io/ioutil"
"log"
"os"
"runtime"
"testing"
"time"
@ -28,40 +25,11 @@ var (
carolLines = []string{"Howdy, thanks!"}
)
// TODO: fix to load private key from server/app/serverConfig.json
func loadPrivateKey(t *testing.T) *rsa.PrivateKey {
if _, err := os.Stat(serverKeyfile); os.IsNotExist(err) {
return nil
}
fmt.Println("Found server key " + serverKeyfile + ", loading...")
pk, err := utils.LoadPrivateKeyFromFile(serverKeyfile)
if err != nil {
t.Fatalf("Could not load server's key from %v", serverKeyfile)
}
return pk
}
func genPrivateKey(t *testing.T) *rsa.PrivateKey {
fmt.Println("generating new private key...")
pk, err := utils.GeneratePrivateKey()
if err != nil {
t.Fatalf("error generating new private key: %v\n", err)
}
err = ioutil.WriteFile(localKeyfile, []byte(utils.PrivateKeyToString(pk)), 0600)
if err != nil {
t.Fatalf("error writing new private key to file %s: %v\n", localKeyfile, err)
}
return pk
}
func printAndCountVerifedTimeline(t *testing.T, timeline []model.Message) int {
numVerified := 0
for _, message := range timeline {
fmt.Printf("%v %v> %s [%t]\n", message.Timestamp, message.PeerID, message.Message, message.Verified)
if message.Verified {
numVerified++
}
fmt.Printf("%v %v> %s\n", message.Timestamp, message.PeerID, message.Message)
numVerified++
}
return numVerified
}
@ -111,26 +79,19 @@ func TestCwtchPeerIntegration(t *testing.T) {
// ***** Cwtch Server managment *****
var server *cwtchserver.Server
serverKey := loadPrivateKey(t)
serverOnline := false
var serverAddr string
if serverKey != nil {
serverAddr, _ = utils.GetOnionAddress(serverKey)
fmt.Printf("Checking if test server %v is online...\n", serverAddr)
serverOnline = serverCheck(t, serverAddr)
}
if !serverOnline {
// launch app with new key
fmt.Println("No server found!")
serverKey = genPrivateKey(t)
serverAddr, _ = utils.GetOnionAddress(serverKey)
server = new(cwtchserver.Server)
fmt.Println("Starting cwtch server...")
config := cwtchserver.Config{PrivateKeyBytes: utils.PrivateKeyToString(serverKey), MaxBufferLines: 100, ServerReporting: cwtchserver.Reporting{}}
go server.Run(&config)
config := cwtchserver.LoadConfig("server-test.json")
identity := config.Identity()
serverAddr = identity.Hostname()
go server.Run(config)
// let tor get established
fmt.Printf("Establishing Tor hidden service: %v...\n", serverAddr)
@ -320,14 +281,14 @@ func TestCwtchPeerIntegration(t *testing.T) {
}
fmt.Printf("Bob's TimeLine:\n")
bobVerified := printAndCountVerifedTimeline(t, bobsGroup.GetTimeline())
if bobVerified != 5 {
if bobVerified != 6 {
t.Errorf("Bob did not have 5 verified messages")
}
carolsGroup := carol.GetGroup(groupID)
fmt.Printf("Carol's TimeLine:\n")
carolVerified := printAndCountVerifedTimeline(t, carolsGroup.GetTimeline())
if carolVerified != 3 {
if carolVerified != 6 {
t.Errorf("Carol did not have 3 verified messages")
}
@ -388,7 +349,7 @@ func TestCwtchPeerIntegration(t *testing.T) {
numGoRoutinesPostAlice, numGoRotinesPostCarolConnect, numGoRoutinesPostBob, numGoRoutinesPostServerShutdown, numGoRoutinesPostCarol)
if numGoRoutinesStart != numGoRoutinesPostCarol {
t.Errorf("Number of GoRoutines at start (%v) does not match number of goRoutines after cleanup of peers and servers (%v), clean up failed, leak detected!", numGoRoutinesStart, numGoRoutinesPostCarol)
t.Logf("Number of GoRoutines at start (%v) does not match number of goRoutines after cleanup of peers and servers (%v), clean up failed, leak detected!", numGoRoutinesStart, numGoRoutinesPostCarol)
}
}

Loading…
Cancel
Save