1
1
Fork 0
mirror of https://github.com/schollz/croc.git synced 2025-10-11 05:11:06 +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.
- 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.

View file

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

View file

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

View file

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

View file

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

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