From 72e2d4d3d8da53566c09ec3816aeba8a27172a63 Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Fri, 29 Jun 2018 19:34:21 -0700 Subject: [PATCH] pake works between two clients --- main.go | 5 ++- src/api.go | 8 ++-- src/client.go | 49 +++++++++++++++++--- src/models.go | 47 ++++++------------- src/pake/pake.go | 4 +- src/server.go | 115 ++++++++++++++++++++++++----------------------- src/utils.go | 4 +- 7 files changed, 124 insertions(+), 108 deletions(-) diff --git a/main.go b/main.go index cdfe99bf..06bb6af0 100644 --- a/main.go +++ b/main.go @@ -9,15 +9,16 @@ import ( func main() { var err error role := flag.Int("role", 0, "role number") + passphrase := flag.String("code", "chou", "codephrase") flag.Parse() c := croc.Init() if *role == -1 { err = c.Relay() } else if *role == 0 { - err = c.Send("foo") + err = c.Send("foo", *passphrase) } else { - err = c.Receive() + err = c.Receive(*passphrase) } if err != nil { panic(err) diff --git a/src/api.go b/src/api.go index a3e0e9f5..6a0461cc 100644 --- a/src/api.go +++ b/src/api.go @@ -10,13 +10,13 @@ func (c *Croc) Relay() error { } // Send will take an existing file or folder and send it through the croc relay -func (c *Croc) Send(fname string) (err error) { - err = c.client(0) +func (c *Croc) Send(fname string, codephrase string) (err error) { + err = c.client(0, codephrase) return } // Receive will receive something through the croc relay -func (c *Croc) Receive() (err error) { - err = c.client(1) +func (c *Croc) Receive(codephrase string) (err error) { + err = c.client(1, codephrase) return } diff --git a/src/client.go b/src/client.go index 3b9afcd0..a4749ced 100644 --- a/src/client.go +++ b/src/client.go @@ -9,14 +9,15 @@ import ( log "github.com/cihub/seelog" "github.com/gorilla/websocket" + "github.com/schollz/croc/src/pake" ) -func (c *Croc) client(role int) (err error) { +func (c *Croc) client(role int, codePhrase string) (err error) { defer log.Flush() - codePhrase := "chou" // initialize the channel data for this client c.cs.Lock() + c.cs.channel.codePhrase = codePhrase if len(codePhrase) > 0 { if len(codePhrase) < 4 { @@ -25,7 +26,13 @@ func (c *Croc) client(role int) (err error) { } c.cs.channel.Channel = codePhrase[:3] c.cs.channel.passPhrase = codePhrase[3:] + } else { + // TODO + // generate code phrase + c.cs.channel.Channel = "chou" + c.cs.channel.passPhrase = codePhrase[3:] } + channel := c.cs.channel.Channel c.cs.Unlock() interrupt := make(chan os.Signal, 1) @@ -55,7 +62,7 @@ func (c *Croc) client(role int) (err error) { return } log.Debugf("recv: %s", cd) - err = c.processState(cd) + err = c.processState(ws, cd) if err != nil { log.Warn(err) return @@ -66,7 +73,7 @@ func (c *Croc) client(role int) (err error) { // initialize by joining as corresponding role // TODO: // allowing suggesting a channel - p := payload{ + p := channelData{ Open: true, Role: role, Channel: channel, @@ -92,7 +99,7 @@ func (c *Croc) client(role int) (err error) { // Cleanly close the connection by sending a close message and then // waiting (with timeout) for the server to close the connection. log.Debug("sending close signal") - errWrite := ws.WriteJSON(payload{ + errWrite := ws.WriteJSON(channelData{ Channel: channel, UUID: uuid, Close: true, @@ -111,7 +118,7 @@ func (c *Croc) client(role int) (err error) { return } -func (c *Croc) processState(cd channelData) (err error) { +func (c *Croc) processState(ws *websocket.Conn, cd channelData) (err error) { c.cs.Lock() defer c.cs.Unlock() @@ -130,6 +137,15 @@ func (c *Croc) processState(cd channelData) (err error) { c.cs.channel.UUID = cd.UUID c.cs.channel.Channel = cd.Channel c.cs.channel.Role = cd.Role + c.cs.channel.Curve = cd.Curve + c.cs.channel.Pake, err = pake.Init([]byte(c.cs.channel.passPhrase), cd.Role, getCurve(cd.Curve)) + c.cs.channel.Update = true + log.Debugf("updating channel") + errWrite := ws.WriteJSON(c.cs.channel) + if errWrite != nil { + log.Error(errWrite) + } + c.cs.channel.Update = false log.Debugf("initialized client state") return } @@ -138,6 +154,27 @@ func (c *Croc) processState(cd channelData) (err error) { c.cs.channel.TransferReady = true } c.cs.channel.Ports = cd.Ports + if cd.Pake != nil && cd.Pake.Role != c.cs.channel.Role { + log.Debugf("updating pake from %d", cd.Pake.Role) + if c.cs.channel.Pake.HkA == nil { + err = c.cs.channel.Pake.Update(cd.Pake.Bytes()) + if err != nil { + log.Error(err) + log.Debug("sending close signal") + c.cs.channel.Close = true + c.cs.channel.Error = err.Error() + ws.WriteJSON(c.cs.channel) + return + } + c.cs.channel.Update = true + log.Debugf("updating channel") + errWrite := ws.WriteJSON(c.cs.channel) + if errWrite != nil { + log.Error(errWrite) + } + c.cs.channel.Update = false + } + } // TODO: // process the client state diff --git a/src/models.go b/src/models.go index 10ac2727..d766fb82 100644 --- a/src/models.go +++ b/src/models.go @@ -1,7 +1,6 @@ package croc import ( - "bytes" "encoding/json" "net" "sync" @@ -45,6 +44,7 @@ func Init() (c *Croc) { c.CurveType = "p521" c.rs.Lock() c.rs.channel = make(map[string]*channelData) + c.cs.channel = new(channelData) c.rs.Unlock() return } @@ -60,17 +60,27 @@ type clientState struct { } type channelData struct { + // Relay actions + // Open set to true when trying to open + Open bool `json:"open"` + // Update set to true when updating + Update bool `json:"update"` + // Close set to true when closing: + Close bool `json:"close"` + // Public // Channel is the name of the channel Channel string `json:"channel,omitempty"` // Pake contains the information for // generating the session key over an insecure channel - Pake pake.Pake + Pake *pake.Pake // TransferReady is set by the relaying when both parties have connected // with their credentials TransferReady bool `json:"transfer_ready"` // Ports returns which TCP ports to connect to Ports []string `json:"ports"` + // Curve is the type of elliptic curve to use + Curve string `json:"curve"` // Error is sent if there is an error Error string `json:"error"` @@ -90,6 +100,7 @@ type channelData struct { passPhrase string // sessionKey sessionKey []byte + pakeDone bool // relay parameters // isopen determine whether or not the channel has been opened @@ -105,38 +116,6 @@ type channelData struct { } 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) - return string(cdb) } - -type payload struct { - // Open set to true when trying to open - Open bool `json:"open"` - // Channel is used to designate the channel of interest - Channel string `json:"channel"` - // Role designates which role the person will take; - // 0 for sender and 1 for recipient. - Role int `json:"role"` - // Curve is the curve to be used. - Curve string `json:"curve"` - - // Update set to true when updating - Update bool `json:"update"` - UUID string `json:"uuid"` - // State is the state information to be updated - State map[string][]byte `json:"state"` - - // Close set to true when closing: - Close bool `json:"close"` -} diff --git a/src/pake/pake.go b/src/pake/pake.go index 8d042477..0cb62b20 100644 --- a/src/pake/pake.go +++ b/src/pake/pake.go @@ -177,9 +177,9 @@ func (p *Pake) Update(qBytes []byte) (err error) { return } -// hashK generates a bcrypt hash of the password using work factor 14. +// hashK generates a bcrypt hash of the password using work factor 12. func hashK(k []byte) ([]byte, error) { - return bcrypt.GenerateFromPassword(k, 14) + return bcrypt.GenerateFromPassword(k, 12) } // checkKHash securely compares a bcrypt hashed password with its possible diff --git a/src/server.go b/src/server.go index 07a9501f..06c80f12 100644 --- a/src/server.go +++ b/src/server.go @@ -8,6 +8,7 @@ import ( "github.com/frankenbeanies/uuid4" "github.com/gorilla/websocket" "github.com/pkg/errors" + "github.com/schollz/croc/src/pake" ) // startServer initiates the server which listens for websocket connections @@ -29,8 +30,8 @@ func (c *Croc) startServer(tcpPorts []string, port string) (err error) { var channel string for { log.Debug("waiting for next message") - var p payload - err := ws.ReadJSON(&p) + var cd channelData + err := ws.ReadJSON(&cd) if err != nil { if _, ok := err.(*websocket.CloseError); ok { // on forced close, delete the channel @@ -41,10 +42,10 @@ func (c *Croc) startServer(tcpPorts []string, port string) (err error) { } break } - channel, err = c.processPayload(ws, p) + channel, err = c.processPayload(ws, cd) if err != nil { // 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", cd, err.Error()) ws.WriteJSON(channelData{Error: err.Error()}) c.closeChannel(channel) return @@ -56,95 +57,95 @@ func (c *Croc) startServer(tcpPorts []string, port string) (err error) { return } -func (c *Croc) updateChannel(p payload) (err error) { +func (c *Croc) updateChannel(cd channelData) (err error) { c.rs.Lock() defer c.rs.Unlock() // determine if channel is invalid - if _, ok := c.rs.channel[p.Channel]; !ok { - err = errors.Errorf("channel '%s' does not exist", p.Channel) + if _, ok := c.rs.channel[cd.Channel]; !ok { + err = errors.Errorf("channel '%s' does not exist", cd.Channel) return } // determine if UUID is invalid for channel - if p.UUID != c.rs.channel[p.Channel].uuids[0] && - p.UUID != c.rs.channel[p.Channel].uuids[1] { - err = errors.Errorf("uuid '%s' is invalid", p.UUID) + if cd.UUID != c.rs.channel[cd.Channel].uuids[0] && + cd.UUID != c.rs.channel[cd.Channel].uuids[1] { + err = errors.Errorf("uuid '%s' is invalid", cd.UUID) return } - // assign each key provided - assignedKeys := []string{} - for key := range p.State { - // TODO: - // add a check that the value of key is not enormous - - // add only if it is a valid key - if _, ok := c.rs.channel[p.Channel].State[key]; ok { - assignedKeys = append(assignedKeys, key) - c.rs.channel[p.Channel].State[key] = p.State[key] - } + // update each + if c.rs.channel[cd.Channel].Pake == nil { + c.rs.channel[cd.Channel].Pake = new(pake.Pake) } - - log.Debugf("assigned %d keys: %v", len(assignedKeys), assignedKeys) + c.rs.channel[cd.Channel].Pake.HkA = cd.Pake.HkA + c.rs.channel[cd.Channel].Pake.HkB = cd.Pake.HkB + c.rs.channel[cd.Channel].Pake.Role = cd.Pake.Role + c.rs.channel[cd.Channel].Pake.Uᵤ = cd.Pake.Uᵤ + c.rs.channel[cd.Channel].Pake.Uᵥ = cd.Pake.Uᵥ + c.rs.channel[cd.Channel].Pake.Vᵤ = cd.Pake.Vᵤ + c.rs.channel[cd.Channel].Pake.Vᵥ = cd.Pake.Vᵥ + c.rs.channel[cd.Channel].Pake.Xᵤ = cd.Pake.Xᵤ + c.rs.channel[cd.Channel].Pake.Xᵥ = cd.Pake.Xᵥ + c.rs.channel[cd.Channel].Pake.Yᵤ = cd.Pake.Yᵤ + c.rs.channel[cd.Channel].Pake.Yᵥ = cd.Pake.Yᵥ + // TODO return } -func (c *Croc) joinChannel(ws *websocket.Conn, p payload) (channel string, err error) { +func (c *Croc) joinChannel(ws *websocket.Conn, cd channelData) (channel string, err error) { log.Debugf("joining channel %s", ws.RemoteAddr().String()) c.rs.Lock() defer c.rs.Unlock() // determine if sender or recipient - if p.Role != 0 && p.Role != 1 { - err = errors.Errorf("no such role of %d", p.Role) + if cd.Role != 0 && cd.Role != 1 { + err = errors.Errorf("no such role of %d", cd.Role) return } // determine channel - if p.Channel == "" { + if cd.Channel == "" { // TODO: // find an empty channel - p.Channel = "chou" + cd.Channel = "chou" } - if _, ok := c.rs.channel[p.Channel]; ok { + if _, ok := c.rs.channel[cd.Channel]; ok { // channel is not empty - if c.rs.channel[p.Channel].uuids[p.Role] != "" { - err = errors.Errorf("channel '%s' already occupied by role %d", p.Channel, p.Role) + if c.rs.channel[cd.Channel].uuids[cd.Role] != "" { + err = errors.Errorf("channel '%s' already occupied by role %d", cd.Channel, cd.Role) return } } log.Debug("creating new channel") - if _, ok := c.rs.channel[p.Channel]; !ok { - c.rs.channel[p.Channel] = newChannelData(p.Channel) + if _, ok := c.rs.channel[cd.Channel]; !ok { + c.rs.channel[cd.Channel] = new(channelData) } - channel = p.Channel + channel = cd.Channel // assign UUID for the role in the channel - c.rs.channel[p.Channel].uuids[p.Role] = uuid4.New().String() - log.Debugf("(%s) %s has joined as role %d", p.Channel, c.rs.channel[p.Channel].uuids[p.Role], p.Role) + c.rs.channel[cd.Channel].uuids[cd.Role] = uuid4.New().String() + log.Debugf("(%s) %s has joined as role %d", cd.Channel, c.rs.channel[cd.Channel].uuids[cd.Role], cd.Role) // send Channel+UUID back to the current person err = ws.WriteJSON(channelData{ - Channel: p.Channel, - UUID: c.rs.channel[p.Channel].uuids[p.Role], - Role: p.Role, + Channel: cd.Channel, + UUID: c.rs.channel[cd.Channel].uuids[cd.Role], + Role: cd.Role, }) if err != nil { return } // if channel is not open, set initial parameters - if !c.rs.channel[p.Channel].isopen { - 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, _ = getCurve(p.Curve) - log.Debugf("(%s) using curve '%s'", p.Channel, p.Curve) - c.rs.channel[p.Channel].State["curve"] = []byte(p.Curve) + if !c.rs.channel[cd.Channel].isopen { + c.rs.channel[cd.Channel].isopen = true + c.rs.channel[cd.Channel].Ports = c.TcpPorts + c.rs.channel[cd.Channel].startTime = time.Now() + c.rs.channel[cd.Channel].Curve = "p256" } - c.rs.channel[p.Channel].websocketConn[p.Role] = ws + c.rs.channel[cd.Channel].websocketConn[cd.Role] = ws - log.Debugf("assigned role %d in channel '%s'", p.Role, p.Channel) + log.Debugf("assigned role %d in channel '%s'", cd.Role, cd.Channel) return } @@ -166,20 +167,20 @@ func (c *Croc) closeChannel(channel string) { 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, cd channelData) (channel string, err error) { log.Debugf("processing payload from %s", ws.RemoteAddr().String()) - channel = p.Channel + channel = cd.Channel // if the request is to close, delete the channel - if p.Close { - log.Debugf("closing channel %s", p.Channel) - c.closeChannel(p.Channel) + if cd.Close { + log.Debugf("closing channel %s", cd.Channel) + c.closeChannel(cd.Channel) return } // if request is to Open, try to open - if p.Open { - channel, err = c.joinChannel(ws, p) + if cd.Open { + channel, err = c.joinChannel(ws, cd) if err != nil { return } @@ -197,9 +198,9 @@ func (c *Croc) processPayload(ws *websocket.Conn, p payload) (channel string, er c.rs.Unlock() // if the request is to Update, then update the state - if p.Update { + if cd.Update { // update - err = c.updateChannel(p) + err = c.updateChannel(cd) if err != nil { return } diff --git a/src/utils.go b/src/utils.go index 7641b7bf..4fc8a319 100644 --- a/src/utils.go +++ b/src/utils.go @@ -70,8 +70,7 @@ func splitFile(fileName string, numPieces int) (err error) { return nil } -func getCurve(s string) (curveString string, curve elliptic.Curve) { - curveString = s +func getCurve(s string) (curve elliptic.Curve) { switch s { case "p224": curve = elliptic.P224() @@ -84,7 +83,6 @@ func getCurve(s string) (curveString string, curve elliptic.Curve) { default: // TODO: // add SIEC - curveString = "p256" curve = elliptic.P256() } return