From f64427f70ddff9e35af96786eec61d6ac65a1719 Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Fri, 29 Jun 2018 07:35:25 -0700 Subject: [PATCH] simplify setup --- README.md | 13 +++++----- src/client.go | 24 ++++++++++++------ src/crypto.go | 3 +-- src/models.go | 35 +++++++++++++++----------- src/server.go | 49 ++++++++++++++++++++++++------------ src/testing_data/README.md.2 | 38 ---------------------------- 6 files changed, 78 insertions(+), 84 deletions(-) delete mode 100644 src/testing_data/README.md.2 diff --git a/README.md b/README.md index 0e15e977..652c911d 100644 --- a/README.md +++ b/README.md @@ -100,12 +100,13 @@ croc.Receive() - Generates X from pw. - Sender sends X to relay. -*Is Y available?* +*Is Y and Bcrypt(k_B) available?* -- Use *Y* to generate its session key *k_A* and *H(k_A)*, and checks *H(H(k_A))*==*H(H(k_B))*. Abort here if it is incorrect. +- Use *Y* to generate its session key *k_A*. +- Check that Bcrypt(k_B) comes from k_A. Abort here if it is incorrect. - Encrypts data using *k_A*. - Connect to TCP ports of Relay. -- Send the Relay authentication *H(k_A)*. +- Send the Relay authentication *Bcrypt(k_A)*. *Are ports stapled?* @@ -121,11 +122,11 @@ croc.Receive() *Is X available?* - Generate *Y*, session key *k_B*, and hashed session key *H(k_B)* using PAKE from secret *pw*. -- Send the Relay *H(H(k_B))* +- Send the Relay *Bcrypt(k_B)* -*Is H(k_A) available?* +*Is Bcrypt(k_A) available?* -- Verify that *H(k_A)* equals *H(k_B)* +- Verify that *Bcrypt(k_A)* comes from k_B - Connect to TCP ports of Relay and listen. - Once file is received, Send close signal to Relay. diff --git a/src/client.go b/src/client.go index 02a6e187..5092fe71 100644 --- a/src/client.go +++ b/src/client.go @@ -13,10 +13,15 @@ import ( func (c *Croc) client(role int) (err error) { defer log.Flush() + codePhrase := "chou" // initialize the channel data for this client c.cs.Lock() c.cs.channel = newChannelData("") + c.cs.channel.codePhrase = codePhrase + c.cs.channel.Channel = codePhrase[:3] + channel := codePhrase[:3] + c.cs.channel.secret["pw"] = []byte(codePhrase[3:]) c.cs.Unlock() interrupt := make(chan os.Signal, 1) @@ -45,7 +50,7 @@ func (c *Croc) client(role int) (err error) { log.Debugf("sender read error:", err) return } - log.Debugf("recv: %+v", cd) + log.Debugf("recv: %s", cd) err = c.processState(cd) if err != nil { log.Warn(err) @@ -57,10 +62,13 @@ func (c *Croc) client(role int) (err error) { // initialize by joining as corresponding role // TODO: // allowing suggesting a channel - err = ws.WriteJSON(payload{ - Open: true, - Role: role, - }) + p := payload{ + Open: true, + Role: role, + Channel: channel, + } + log.Debugf("sending opening payload: %+v", p) + err = ws.WriteJSON(p) if err != nil { log.Errorf("problem opening: %s", err.Error()) return @@ -116,16 +124,16 @@ func (c *Croc) processState(cd channelData) (err error) { // initialize if has UUID if cd.UUID != "" { c.cs.channel.UUID = cd.UUID - c.cs.channel.Ports = cd.Ports c.cs.channel.Channel = cd.Channel c.cs.channel.Role = cd.Role - c.cs.channel.Ports = cd.Ports log.Debugf("initialized client state") + return } // copy over the rest of the state if cd.TransferReady { c.cs.channel.TransferReady = true } + c.cs.channel.Ports = cd.Ports for key := range cd.State { c.cs.channel.State[key] = cd.State[key] } @@ -134,6 +142,6 @@ func (c *Croc) processState(cd channelData) (err error) { // TODO: // process the client state - log.Debugf("processing client state: %+v", c.cs.channel) + log.Debugf("processing client state: %+v", c.cs.channel.String2()) return } diff --git a/src/crypto.go b/src/crypto.go index 64861b74..46916419 100644 --- a/src/crypto.go +++ b/src/crypto.go @@ -6,7 +6,6 @@ import ( "crypto/rand" "crypto/sha1" "crypto/sha256" - "encoding/binary" "encoding/hex" "fmt" mathrand "math/rand" @@ -27,7 +26,7 @@ func init() { func getRandomName() string { result := []string{} bs := make([]byte, 4) - binary.LittleEndian.PutUint32(bs, mathrand.Uint32()) + rand.Read(bs) result = mnemonicode.EncodeWordList(result, bs) return strings.Join(result, "-") } diff --git a/src/models.go b/src/models.go index 2faeeb21..359b5e79 100644 --- a/src/models.go +++ b/src/models.go @@ -1,7 +1,9 @@ package croc import ( + "bytes" "crypto/elliptic" + "encoding/json" "net" "sync" "time" @@ -15,9 +17,9 @@ const ( ) var ( - // see PAKE setup for more info: https://play.golang.org/p/QLHvINK4qFG + // see PAKE setup for more info: https://play.golang.org/p/Sd0eTuuEIWu // availableStates are the varaibles available to the parties involved - availableStates = []string{"curve", "Xᵤ", "Xᵥ", "Yᵤ", "Yᵥ", "Uᵤ", "Uᵥ", "Vᵤ", "Vᵥ", "HHBk", "HAk"} + availableStates = []string{"curve", "Xᵤ", "Xᵥ", "Yᵤ", "Yᵥ", "Uᵤ", "Uᵥ", "Vᵤ", "Vᵥ", "Bcrypt(Ak)", "Bcrypt(Bk)"} // availableSecrets are the variables available only to a specific client, and not shared availableSecrets = []string{"pw", "Upwᵤ", "Upwᵥ", "α", "αᵤ", "αᵥ", "Vpwᵤ", "Vpwᵥ", "β", "gβᵤ", "gβᵥ", "BZᵤ", "BZᵥ", "BZᵤ", "BZᵥ", "AZᵤ", "AZᵥ", "AZᵤ", "AZᵥ", "Bk", "Ak"} ) @@ -89,12 +91,12 @@ type channelData struct { // Private // client parameters - + // codePhrase uses the first 3 characters to establish a channel, and the rest + // to form the passphrase + codePhrase string // secret are the computed secretes // contains "curve", "h_k", "hh_k", "x", "y" - secret map[string][]byte `json:"secret"` - - // relay + client parameters + secret map[string][]byte // curve is the type of elliptic curve used for PAKE curve elliptic.Curve @@ -111,15 +113,20 @@ type channelData struct { startTime time.Time } -type response struct { - // various responses - Channel string `json:"channel,omitempty"` - UUID string `json:"uuid,omitempty"` - Data *channelData `json:"data,omitempty"` +func (cd channelData) String2() string { + for key := range cd.State { + if bytes.Equal(cd.State[key], []byte{}) { + delete(cd.State, key) + } + } + for key := range cd.secret { + if !bytes.Equal(cd.secret[key], []byte{}) { + cd.State[key] = cd.secret[key] + } + } + cdb, _ := json.Marshal(cd) - // constant responses - Success bool `json:"success"` - Message string `json:"message"` + return string(cdb) } type payload struct { diff --git a/src/server.go b/src/server.go index d2568155..e3f9bc08 100644 --- a/src/server.go +++ b/src/server.go @@ -32,15 +32,10 @@ func (c *Croc) startServer(tcpPorts []string, port string) (err error) { if _, ok := err.(*websocket.CloseError); ok { // on forced close, delete the channel log.Debug("closed channel") - c.rs.Lock() - if _, ok := c.rs.channel[channel]; ok { - delete(c.rs.channel, channel) - } - c.rs.Unlock() + c.closeChannel(channel) } else { log.Debugf("read:", err) } - break } channel, err = c.processPayload(ws, p) @@ -48,9 +43,7 @@ func (c *Croc) startServer(tcpPorts []string, port string) (err error) { // if error, send the error back and then delete the channel log.Warn("problem processing payload %+v: %s", p, err.Error()) ws.WriteJSON(channelData{Error: err.Error()}) - c.rs.Lock() - delete(c.rs.channel, p.Channel) - c.rs.Unlock() + c.closeChannel(channel) return } } @@ -95,6 +88,7 @@ func (c *Croc) updateChannel(p payload) (err error) { } func (c *Croc) joinChannel(ws *websocket.Conn, p payload) (channel string, err error) { + log.Debugf("joining channel %s", ws.RemoteAddr().String()) c.rs.Lock() defer c.rs.Unlock() @@ -117,6 +111,7 @@ func (c *Croc) joinChannel(ws *websocket.Conn, p payload) (channel string, err e return } } + log.Debug("creating new channel") if _, ok := c.rs.channel[p.Channel]; !ok { c.rs.channel[p.Channel] = newChannelData(p.Channel) } @@ -140,7 +135,7 @@ func (c *Croc) joinChannel(ws *websocket.Conn, p payload) (channel string, err e c.rs.channel[p.Channel].isopen = true c.rs.channel[p.Channel].Ports = c.TcpPorts c.rs.channel[p.Channel].startTime = time.Now() - p.Curve, c.rs.channel[p.Channel].curve = getCurve(p.Curve) + p.Curve, _ = getCurve(p.Curve) log.Debugf("(%s) using curve '%s'", p.Channel, p.Curve) c.rs.channel[p.Channel].State["curve"] = []byte(p.Curve) } @@ -150,15 +145,32 @@ func (c *Croc) joinChannel(ws *websocket.Conn, p payload) (channel string, err e return } +// closeChannel will shut down current open websockets and delete the channel information +func (c *Croc) closeChannel(channel string) { + c.rs.Lock() + defer c.rs.Unlock() + // check if channel exists + if _, ok := c.rs.channel[channel]; !ok { + return + } + // close open connections + for _, wsConn := range c.rs.channel[channel].websocketConn { + if wsConn != nil { + wsConn.Close() + } + } + // delete + delete(c.rs.channel, channel) +} + func (c *Croc) processPayload(ws *websocket.Conn, p payload) (channel string, err error) { + log.Debugf("processing payload from %s", ws.RemoteAddr().String()) channel = p.Channel // if the request is to close, delete the channel if p.Close { log.Debugf("closing channel %s", p.Channel) - c.rs.Lock() - delete(c.rs.channel, p.Channel) - c.rs.Unlock() + c.closeChannel(p.Channel) return } @@ -200,7 +212,7 @@ func (c *Croc) processPayload(ws *websocket.Conn, p payload) (channel string, er if wsConn == nil { continue } - log.Debugf("writing latest data %+v to %d", c.rs.channel[channel], role) + log.Debugf("writing latest data %+v to %d", c.rs.channel[channel].String2(), role) err = wsConn.WriteJSON(c.rs.channel[channel]) if err != nil { log.Debugf("problem writing to role %d: %s", role, err.Error()) @@ -221,13 +233,18 @@ func (c *Croc) channelCleanup() { keys[i] = key i++ } + channelsToDelete := []string{} for _, key := range keys { if time.Since(c.rs.channel[key].startTime) > maximumWait { - log.Debugf("channel %s has exceeded time, deleting", key) - delete(c.rs.channel, key) + channelsToDelete = append(channelsToDelete, key) } } c.rs.Unlock() + + for _, channel := range channelsToDelete { + log.Debugf("channel %s has exceeded time, deleting", channel) + c.closeChannel(channel) + } time.Sleep(1 * time.Minute) } } diff --git a/src/testing_data/README.md.2 b/src/testing_data/README.md.2 deleted file mode 100644 index 91edd168..00000000 --- a/src/testing_data/README.md.2 +++ /dev/null @@ -1,38 +0,0 @@ - wiped from the relay server. The encrypted file data never is stored on the relay. - -**Encryption** - -Encryption uses AES-256 with a pbkdf2 derived key (see [RFC2898](http://www.ietf.org/rfc/rfc2898.txt)) where the code phrase shared between the sender and receiver is used as the passphrase. For each of the two encrypted data blocks (metadata stored on relay server, and file data transmitted), a random 8-byte salt is used and a IV is generated according to [NIST Recommendation for Block ciphers, Section 8.2](http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf). - -**Decryption** - -On the receiver's computer, each piece of received encrypted data is written to a separate file. These files are concatenated and then decrypted. The hash of the decrypted file is then checked against the hash transmitted from the sender (part of the meta data block). - -## Run your own relay - -*croc* relies on a TCP relay to staple the parallel incoming and outgoing connections. The relay temporarily stores connection information and the encrypted meta information. The default uses a public relay at, `cowyo.com`, which has a 30-day uptime of 99.989% ([click here to check the current status of the public relay](https://stats.uptimerobot.com/lOwJYIgRm)). - -You can also run your own relay, it is very easy. On your server, `your-server.com`, just run - -``` -$ croc -relay -``` - -Now, when you use *croc* to send and receive you should add `-server your-server.com` to use your relay server. Make sure to open up TCP ports 27001-27009. - -# Contribute - -I am awed by all the [great contributions](#acknowledgements) made! If you feel like contributing, in any way, by all means you can send an Issue, a PR, ask a question, or tweet me ([@yakczar](http://ctt.ec/Rq054)). - -# License - -MIT - -# Acknowledgements - -Thanks... - -- ...[@warner](https://github.com/warner) for the [idea](https://github.com/warner/magic-wormhole). -- ...[@tscholl2](https://github.com/tscholl2) for the [encryption gists](https://gist.github.com/tscholl2/dc7dc15dc132ea70a98e8542fefffa28). -- ...[@skorokithakis](https://github.com/skorokithakis) for [code on proxying two connections](https://www.stavros.io/posts/proxying-two-connections-go/). -- ...for making pull requests [@Girbons](https://github.com/Girbons), [@techtide](https://github.com/techtide), [@heymatthew](https://github.com/heymatthew), [@Lunsford94](https://github.com/Lunsford94), [@lummie](https://github.com/lummie), [@jesuiscamille](https://github.com/jesuiscamille), [@threefjord](https://github.com/threefjord), [@marcossegovia](https://github.com/marcossegovia), [@csleong98](https://github.com/csleong98), [@afotescu](https://github.com/afotescu), [@callmefever](https://github.com/callmefever), [@El-JojA](https://github.com/El-JojA), [@anatolyyyyyy](https://github.com/anatolyyyyyy), [@goggle](https://github.com/goggle), [@smileboywtu](https://github.com/smileboywtu)!