croc/src/recipient/recipient.go

174 lines
3.9 KiB
Go

package recipient
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"time"
log "github.com/cihub/seelog"
"github.com/gorilla/websocket"
"github.com/schollz/croc/src/compress"
"github.com/schollz/croc/src/crypt"
"github.com/schollz/croc/src/logger"
"github.com/schollz/croc/src/models"
"github.com/schollz/croc/src/utils"
"github.com/schollz/pake"
"github.com/schollz/progressbar/v2"
"github.com/tscholl2/siec"
)
var DebugLevel string
// Receive is the async operation to receive a file
func Receive(done chan struct{}, c *websocket.Conn, codephrase string) {
logger.SetLogLevel(DebugLevel)
err := receive(c, codephrase)
if err != nil {
if strings.HasPrefix(err.Error(), "websocket: close 1005") {
return
}
log.Error(err)
}
done <- struct{}{}
}
func receive(c *websocket.Conn, codephrase string) (err error) {
var fstats models.FileStats
var sessionKey []byte
// pick an elliptic curve
curve := siec.SIEC255()
// both parties should have a weak key
pw := []byte(codephrase)
// initialize recipient Q ("1" indicates recipient)
Q, err := pake.Init(pw, 1, curve, 100*time.Millisecond)
if err != nil {
return
}
step := 0
for {
messageType, message, err := c.ReadMessage()
if err != nil {
return err
}
if messageType == websocket.PongMessage || messageType == websocket.PingMessage {
continue
}
log.Debugf("got %d: %s", messageType, message)
switch step {
case 0:
// Q receives u
log.Debugf("[%d] Q computes k, sends H(k), v back to P", step)
if err := Q.Update(message); err != nil {
return err
}
c.WriteMessage(websocket.BinaryMessage, Q.Bytes())
case 1:
log.Debugf("[%d] Q recieves H(k) from P", step)
if err := Q.Update(message); err != nil {
return err
}
sessionKey, err = Q.SessionKey()
if err != nil {
return err
}
log.Debugf("%x\n", sessionKey)
c.WriteMessage(websocket.BinaryMessage, []byte("ready"))
case 2:
log.Debugf("[%d] recieve file info", step)
err = json.Unmarshal(message, &fstats)
if err != nil {
return err
}
// await file
f, err := os.Create("out")
if err != nil {
return err
}
bytesWritten := 0
bar := progressbar.NewOptions(
int(fstats.Size),
progressbar.OptionSetRenderBlankState(true),
progressbar.OptionSetBytes(int(fstats.Size)),
)
c.WriteMessage(websocket.BinaryMessage, []byte("ready"))
for {
messageType, message, err := c.ReadMessage()
if err != nil {
return err
}
if messageType == websocket.PongMessage || messageType == websocket.PingMessage {
continue
}
if messageType == websocket.BinaryMessage {
// tell the sender that we recieved this packet
c.WriteMessage(websocket.BinaryMessage, []byte("ok"))
// do decryption
var enc crypt.Encryption
err = json.Unmarshal(message, &enc)
if err != nil {
return err
}
decrypted, err := enc.Decrypt(sessionKey, true)
if err != nil {
return err
}
// do decompression
decompressed := compress.Decompress(decrypted)
// decompressed := decrypted
// write to file
n, err := f.Write(decompressed)
if err != nil {
return err
}
// update the bytes written
bytesWritten += n
// update the progress bar
bar.Add(n)
} else {
// we are finished
// close file
err = f.Close()
if err != nil {
return err
}
// finish bar
bar.Finish()
// check hash
hash256, err := utils.HashFile("out")
if err != nil {
return err
}
// check success hash(myfile) == hash(theirfile)
log.Debugf("got hash: %x", message)
if bytes.Equal(hash256, message) {
c.WriteMessage(websocket.BinaryMessage, []byte("ok"))
return nil
} else {
c.WriteMessage(websocket.BinaryMessage, []byte("not"))
return errors.New("file corrupted")
}
}
}
default:
return fmt.Errorf("unknown step")
}
step++
}
}