Browse Source

Adding V3 Onions to Cwtch!

thefuture
Sarah Jamie Lewis 1 year ago
parent
commit
1e04b1161e
20 changed files with 128 additions and 204 deletions
  1. +4
    -0
      .gitignore
  2. +20
    -6
      app/cli/main.go
  3. +0
    -1
      app/peer/alice/alice.go
  4. +1
    -1
      app/peer/bob/bob.go
  5. +9
    -4
      model/group.go
  6. +3
    -3
      model/message.go
  7. +19
    -18
      model/profile.go
  8. +2
    -2
      model/profile_test.go
  9. +1
    -1
      peer/connections/peerpeerconnection.go
  10. +11
    -16
      peer/connections/peerpeerconnection_test.go
  11. +4
    -3
      peer/connections/peerserverconnection.go
  12. +11
    -14
      peer/connections/peerserverconnection_test.go
  13. +9
    -20
      peer/cwtch_peer.go
  14. +1
    -1
      peer/peer/peer_channel.go
  15. +1
    -1
      peer/peer/peer_channel_test.go
  16. +1
    -1
      peer/peer/peer_data_channel.go
  17. +5
    -5
      server/server.go
  18. +16
    -59
      server/serverConfig.go
  19. +1
    -0
      storage/profile_store.go
  20. +9
    -48
      testing/cwtch_peer_server_intergration_test.go

+ 4
- 0
.gitignore View File

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

+ 20
- 6
app/cli/main.go View File

@@ -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]])
}


+ 0
- 1
app/peer/alice/alice.go View File

@@ -13,7 +13,6 @@ func main() {
return data
}


alice.SetPeerDataHandler(processData)
alice.Listen()
}

+ 1
- 1
app/peer/bob/bob.go View File

@@ -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!!!"))


+ 9
- 4
model/group.go View File

@@ -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
}



+ 3
- 3
model/message.go View File

@@ -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
}

+ 19
- 18
model/profile.go View File

@@ -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


+ 2
- 2
model/profile_test.go View File

@@ -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")


+ 1
- 1
peer/connections/peerpeerconnection.go View File

@@ -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() {


+ 11
- 16
peer/connections/peerpeerconnection_test.go View File

@@ -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")


+ 4
- 3
peer/connections/peerserverconnection.go View File

@@ -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



+ 11
- 14
peer/connections/peerserverconnection_test.go View File

@@ -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) {


+ 9
- 20
peer/cwtch_peer.go View File

@@ -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)


+ 1
- 1
peer/peer/peer_channel.go View File

@@ -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.


+ 1
- 1
peer/peer/peer_channel_test.go View File

@@ -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())
}
}


+ 1
- 1
peer/peer/peer_data_channel.go View File

@@ -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.


+ 5
- 5
server/server.go View File

@@ -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)
}


+ 16
- 59
server/serverConfig.go View File

@@ -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()
}

+ 1
- 0
storage/profile_store.go View File

@@ -0,0 +1 @@
package storage

+ 9
- 48
testing/cwtch_peer_server_intergration_test.go View File

@@ -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