0
0
Fork 0
mirror of https://github.com/schollz/croc.git synced 2025-10-11 13:21:00 +02:00

simplify setup

This commit is contained in:
Zack Scholl 2018-06-29 07:35:25 -07:00
parent 7d07ccfe40
commit f64427f70d
6 changed files with 78 additions and 84 deletions

View file

@ -100,12 +100,13 @@ croc.Receive()
- Generates X from pw. - Generates X from pw.
- Sender sends X to relay. - 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*. - Encrypts data using *k_A*.
- Connect to TCP ports of Relay. - Connect to TCP ports of Relay.
- Send the Relay authentication *H(k_A)*. - Send the Relay authentication *Bcrypt(k_A)*.
*Are ports stapled?* *Are ports stapled?*
@ -121,11 +122,11 @@ croc.Receive()
*Is X available?* *Is X available?*
- Generate *Y*, session key *k_B*, and hashed session key *H(k_B)* using PAKE from secret *pw*. - 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. - Connect to TCP ports of Relay and listen.
- Once file is received, Send close signal to Relay. - Once file is received, Send close signal to Relay.

View file

@ -13,10 +13,15 @@ import (
func (c *Croc) client(role int) (err error) { func (c *Croc) client(role int) (err error) {
defer log.Flush() defer log.Flush()
codePhrase := "chou"
// initialize the channel data for this client // initialize the channel data for this client
c.cs.Lock() c.cs.Lock()
c.cs.channel = newChannelData("") 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() c.cs.Unlock()
interrupt := make(chan os.Signal, 1) interrupt := make(chan os.Signal, 1)
@ -45,7 +50,7 @@ func (c *Croc) client(role int) (err error) {
log.Debugf("sender read error:", err) log.Debugf("sender read error:", err)
return return
} }
log.Debugf("recv: %+v", cd) log.Debugf("recv: %s", cd)
err = c.processState(cd) err = c.processState(cd)
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)
@ -57,10 +62,13 @@ func (c *Croc) client(role int) (err error) {
// initialize by joining as corresponding role // initialize by joining as corresponding role
// TODO: // TODO:
// allowing suggesting a channel // allowing suggesting a channel
err = ws.WriteJSON(payload{ p := payload{
Open: true, Open: true,
Role: role, Role: role,
}) Channel: channel,
}
log.Debugf("sending opening payload: %+v", p)
err = ws.WriteJSON(p)
if err != nil { if err != nil {
log.Errorf("problem opening: %s", err.Error()) log.Errorf("problem opening: %s", err.Error())
return return
@ -116,16 +124,16 @@ func (c *Croc) processState(cd channelData) (err error) {
// initialize if has UUID // initialize if has UUID
if cd.UUID != "" { if cd.UUID != "" {
c.cs.channel.UUID = cd.UUID c.cs.channel.UUID = cd.UUID
c.cs.channel.Ports = cd.Ports
c.cs.channel.Channel = cd.Channel c.cs.channel.Channel = cd.Channel
c.cs.channel.Role = cd.Role c.cs.channel.Role = cd.Role
c.cs.channel.Ports = cd.Ports
log.Debugf("initialized client state") log.Debugf("initialized client state")
return
} }
// copy over the rest of the state // copy over the rest of the state
if cd.TransferReady { if cd.TransferReady {
c.cs.channel.TransferReady = true c.cs.channel.TransferReady = true
} }
c.cs.channel.Ports = cd.Ports
for key := range cd.State { for key := range cd.State {
c.cs.channel.State[key] = cd.State[key] c.cs.channel.State[key] = cd.State[key]
} }
@ -134,6 +142,6 @@ func (c *Croc) processState(cd channelData) (err error) {
// TODO: // TODO:
// process the client state // process the client state
log.Debugf("processing client state: %+v", c.cs.channel) log.Debugf("processing client state: %+v", c.cs.channel.String2())
return return
} }

View file

@ -6,7 +6,6 @@ import (
"crypto/rand" "crypto/rand"
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"encoding/binary"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
mathrand "math/rand" mathrand "math/rand"
@ -27,7 +26,7 @@ func init() {
func getRandomName() string { func getRandomName() string {
result := []string{} result := []string{}
bs := make([]byte, 4) bs := make([]byte, 4)
binary.LittleEndian.PutUint32(bs, mathrand.Uint32()) rand.Read(bs)
result = mnemonicode.EncodeWordList(result, bs) result = mnemonicode.EncodeWordList(result, bs)
return strings.Join(result, "-") return strings.Join(result, "-")
} }

View file

@ -1,7 +1,9 @@
package croc package croc
import ( import (
"bytes"
"crypto/elliptic" "crypto/elliptic"
"encoding/json"
"net" "net"
"sync" "sync"
"time" "time"
@ -15,9 +17,9 @@ const (
) )
var ( 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 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 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"} 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 // Private
// client parameters // 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 // secret are the computed secretes
// contains "curve", "h_k", "hh_k", "x", "y" // contains "curve", "h_k", "hh_k", "x", "y"
secret map[string][]byte `json:"secret"` secret map[string][]byte
// relay + client parameters
// curve is the type of elliptic curve used for PAKE // curve is the type of elliptic curve used for PAKE
curve elliptic.Curve curve elliptic.Curve
@ -111,15 +113,20 @@ type channelData struct {
startTime time.Time startTime time.Time
} }
type response struct { func (cd channelData) String2() string {
// various responses for key := range cd.State {
Channel string `json:"channel,omitempty"` if bytes.Equal(cd.State[key], []byte{}) {
UUID string `json:"uuid,omitempty"` delete(cd.State, key)
Data *channelData `json:"data,omitempty"` }
}
for key := range cd.secret {
if !bytes.Equal(cd.secret[key], []byte{}) {
cd.State[key] = cd.secret[key]
}
}
cdb, _ := json.Marshal(cd)
// constant responses return string(cdb)
Success bool `json:"success"`
Message string `json:"message"`
} }
type payload struct { type payload struct {

View file

@ -32,15 +32,10 @@ func (c *Croc) startServer(tcpPorts []string, port string) (err error) {
if _, ok := err.(*websocket.CloseError); ok { if _, ok := err.(*websocket.CloseError); ok {
// on forced close, delete the channel // on forced close, delete the channel
log.Debug("closed channel") log.Debug("closed channel")
c.rs.Lock() c.closeChannel(channel)
if _, ok := c.rs.channel[channel]; ok {
delete(c.rs.channel, channel)
}
c.rs.Unlock()
} else { } else {
log.Debugf("read:", err) log.Debugf("read:", err)
} }
break break
} }
channel, err = c.processPayload(ws, p) 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 // if error, send the error back and then delete the channel
log.Warn("problem processing payload %+v: %s", p, err.Error()) log.Warn("problem processing payload %+v: %s", p, err.Error())
ws.WriteJSON(channelData{Error: err.Error()}) ws.WriteJSON(channelData{Error: err.Error()})
c.rs.Lock() c.closeChannel(channel)
delete(c.rs.channel, p.Channel)
c.rs.Unlock()
return 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) { func (c *Croc) joinChannel(ws *websocket.Conn, p payload) (channel string, err error) {
log.Debugf("joining channel %s", ws.RemoteAddr().String())
c.rs.Lock() c.rs.Lock()
defer c.rs.Unlock() defer c.rs.Unlock()
@ -117,6 +111,7 @@ func (c *Croc) joinChannel(ws *websocket.Conn, p payload) (channel string, err e
return return
} }
} }
log.Debug("creating new channel")
if _, ok := c.rs.channel[p.Channel]; !ok { if _, ok := c.rs.channel[p.Channel]; !ok {
c.rs.channel[p.Channel] = newChannelData(p.Channel) 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].isopen = true
c.rs.channel[p.Channel].Ports = c.TcpPorts c.rs.channel[p.Channel].Ports = c.TcpPorts
c.rs.channel[p.Channel].startTime = time.Now() 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) log.Debugf("(%s) using curve '%s'", p.Channel, p.Curve)
c.rs.channel[p.Channel].State["curve"] = []byte(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 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) { 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 channel = p.Channel
// if the request is to close, delete the channel // if the request is to close, delete the channel
if p.Close { if p.Close {
log.Debugf("closing channel %s", p.Channel) log.Debugf("closing channel %s", p.Channel)
c.rs.Lock() c.closeChannel(p.Channel)
delete(c.rs.channel, p.Channel)
c.rs.Unlock()
return return
} }
@ -200,7 +212,7 @@ func (c *Croc) processPayload(ws *websocket.Conn, p payload) (channel string, er
if wsConn == nil { if wsConn == nil {
continue 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]) err = wsConn.WriteJSON(c.rs.channel[channel])
if err != nil { if err != nil {
log.Debugf("problem writing to role %d: %s", role, err.Error()) log.Debugf("problem writing to role %d: %s", role, err.Error())
@ -221,13 +233,18 @@ func (c *Croc) channelCleanup() {
keys[i] = key keys[i] = key
i++ i++
} }
channelsToDelete := []string{}
for _, key := range keys { for _, key := range keys {
if time.Since(c.rs.channel[key].startTime) > maximumWait { if time.Since(c.rs.channel[key].startTime) > maximumWait {
log.Debugf("channel %s has exceeded time, deleting", key) channelsToDelete = append(channelsToDelete, key)
delete(c.rs.channel, key)
} }
} }
c.rs.Unlock() c.rs.Unlock()
for _, channel := range channelsToDelete {
log.Debugf("channel %s has exceeded time, deleting", channel)
c.closeChannel(channel)
}
time.Sleep(1 * time.Minute) time.Sleep(1 * time.Minute)
} }
} }

View file

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