mirror of
https://github.com/schollz/croc.git
synced 2025-10-11 05:11:06 +02:00
commit
333ece706e
11 changed files with 207 additions and 140 deletions
17
README.md
17
README.md
|
@ -137,6 +137,15 @@ You can send with your own code phrase (must be more than 4 characters).
|
||||||
croc send --code [code-phrase] [file(s)-or-folder]
|
croc send --code [code-phrase] [file(s)-or-folder]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Allow overwriting without prompt
|
||||||
|
|
||||||
|
By default, croc will prompt whether to overwrite a file. You can automatically overwrite files by using the `--overwrite` flag (recipient only). For example, receive a file to automatically overwrite:
|
||||||
|
|
||||||
|
```
|
||||||
|
croc --yes --overwrite <code>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Use pipes - stdin and stdout
|
### Use pipes - stdin and stdout
|
||||||
|
|
||||||
You can pipe to `croc`:
|
You can pipe to `croc`:
|
||||||
|
@ -173,6 +182,14 @@ You can use a proxy as your connection to the relay by adding a proxy address wi
|
||||||
croc --socks5 "127.0.0.1:9050" send SOMEFILE
|
croc --socks5 "127.0.0.1:9050" send SOMEFILE
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Change encryption curve
|
||||||
|
|
||||||
|
You can choose from several different elliptic curves to use for encryption by using the `--curve` flag. Only the recipient can choose the curve. For example, receive a file using the P-521 curve:
|
||||||
|
|
||||||
|
```
|
||||||
|
croc --curve p521 <codephrase>
|
||||||
|
```
|
||||||
|
|
||||||
### Self-host relay
|
### Self-host relay
|
||||||
|
|
||||||
The relay is needed to staple the parallel incoming and outgoing connections. By default, `croc` uses a public relay but you can also run your own relay:
|
The relay is needed to staple the parallel incoming and outgoing connections. By default, `croc` uses a public relay but you can also run your own relay:
|
||||||
|
|
3
go.mod
3
go.mod
|
@ -13,12 +13,11 @@ require (
|
||||||
github.com/schollz/cli/v2 v2.2.1
|
github.com/schollz/cli/v2 v2.2.1
|
||||||
github.com/schollz/logger v1.2.0
|
github.com/schollz/logger v1.2.0
|
||||||
github.com/schollz/mnemonicode v1.0.1
|
github.com/schollz/mnemonicode v1.0.1
|
||||||
github.com/schollz/pake/v2 v2.0.7
|
github.com/schollz/pake/v3 v3.0.0-20210417162517-73e32835a3d0
|
||||||
github.com/schollz/peerdiscovery v1.6.3
|
github.com/schollz/peerdiscovery v1.6.3
|
||||||
github.com/schollz/progressbar/v3 v3.7.6
|
github.com/schollz/progressbar/v3 v3.7.6
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/stretchr/testify v1.6.1
|
||||||
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094
|
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||||
golang.org/x/net v0.0.0-20210326220855-61e056675ecf
|
golang.org/x/net v0.0.0-20210326220855-61e056675ecf
|
||||||
golang.org/x/sys v0.0.0-20210326220804-49726bf1d181 // indirect
|
golang.org/x/sys v0.0.0-20210326220804-49726bf1d181 // indirect
|
||||||
|
|
5
go.sum
5
go.sum
|
@ -39,8 +39,8 @@ github.com/schollz/logger v1.2.0 h1:5WXfINRs3lEUTCZ7YXhj0uN+qukjizvITLm3Ca2m0Ho=
|
||||||
github.com/schollz/logger v1.2.0/go.mod h1:P6F4/dGMGcx8wh+kG1zrNEd4vnNpEBY/mwEMd/vn6AM=
|
github.com/schollz/logger v1.2.0/go.mod h1:P6F4/dGMGcx8wh+kG1zrNEd4vnNpEBY/mwEMd/vn6AM=
|
||||||
github.com/schollz/mnemonicode v1.0.1 h1:LiH5hwADZwjwnfXsaD4xgnMyTAtaKHN+e5AyjRU6WSU=
|
github.com/schollz/mnemonicode v1.0.1 h1:LiH5hwADZwjwnfXsaD4xgnMyTAtaKHN+e5AyjRU6WSU=
|
||||||
github.com/schollz/mnemonicode v1.0.1/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY=
|
github.com/schollz/mnemonicode v1.0.1/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY=
|
||||||
github.com/schollz/pake/v2 v2.0.7 h1:pMd7VRwEytDLFXP3vf5UksO6/zxcEoEhb4jaqb6NRR4=
|
github.com/schollz/pake/v3 v3.0.0-20210417162517-73e32835a3d0 h1:OdIvrbSi1RDKc9Olim4M+Sh0hms7ZU9kXNoXGG17UwA=
|
||||||
github.com/schollz/pake/v2 v2.0.7/go.mod h1:9pvRBp3hcsPYRJre5t3dP6HFOkPG80bUqzDwB7PP3Ec=
|
github.com/schollz/pake/v3 v3.0.0-20210417162517-73e32835a3d0/go.mod h1:U8udmizi/9pb5OqW+ieb4ebU5wGCqnLhXqYXSJRpeD4=
|
||||||
github.com/schollz/peerdiscovery v1.6.3 h1:qoawrl3GBwf8ff3Itb0QkaUGDb2k40NwE24MEgFL4Gc=
|
github.com/schollz/peerdiscovery v1.6.3 h1:qoawrl3GBwf8ff3Itb0QkaUGDb2k40NwE24MEgFL4Gc=
|
||||||
github.com/schollz/peerdiscovery v1.6.3/go.mod h1:xpt6HDA944jqSLzJMLreYtmhWeS2OW2XW4ZLmCezi00=
|
github.com/schollz/peerdiscovery v1.6.3/go.mod h1:xpt6HDA944jqSLzJMLreYtmhWeS2OW2XW4ZLmCezi00=
|
||||||
github.com/schollz/progressbar/v3 v3.7.6 h1:akAvVpTy2IAcePWYndctoBaY9bLE3z4LE1Hn91BJ9g4=
|
github.com/schollz/progressbar/v3 v3.7.6 h1:akAvVpTy2IAcePWYndctoBaY9bLE3z4LE1Hn91BJ9g4=
|
||||||
|
@ -57,7 +57,6 @@ github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094 h1:tZWtuLE+LbUwT4OP1
|
||||||
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
|
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/schollz/croc/v8/src/tcp"
|
"github.com/schollz/croc/v8/src/tcp"
|
||||||
"github.com/schollz/croc/v8/src/utils"
|
"github.com/schollz/croc/v8/src/utils"
|
||||||
log "github.com/schollz/logger"
|
log "github.com/schollz/logger"
|
||||||
|
"github.com/schollz/pake/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version specifies the version
|
// Version specifies the version
|
||||||
|
@ -86,6 +87,8 @@ func Run() (err error) {
|
||||||
&cli.BoolFlag{Name: "ask", Usage: "make sure sender and recipient are prompted"},
|
&cli.BoolFlag{Name: "ask", Usage: "make sure sender and recipient are prompted"},
|
||||||
&cli.BoolFlag{Name: "local", Usage: "force to use only local connections"},
|
&cli.BoolFlag{Name: "local", Usage: "force to use only local connections"},
|
||||||
&cli.BoolFlag{Name: "ignore-stdin", Usage: "ignore piped stdin"},
|
&cli.BoolFlag{Name: "ignore-stdin", Usage: "ignore piped stdin"},
|
||||||
|
&cli.BoolFlag{Name: "overwrite", Usage: "do not prompt to overwrite"},
|
||||||
|
&cli.StringFlag{Name: "curve", Value: "siec", Usage: "choose an encryption curve (" + strings.Join(pake.AvailableCurves(), ", ") + ")"},
|
||||||
&cli.StringFlag{Name: "ip", Value: "", Usage: "set sender ip if known e.g. 10.0.0.1:9009, [::1]:9009"},
|
&cli.StringFlag{Name: "ip", Value: "", Usage: "set sender ip if known e.g. 10.0.0.1:9009, [::1]:9009"},
|
||||||
&cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay", EnvVars: []string{"CROC_RELAY"}},
|
&cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay", EnvVars: []string{"CROC_RELAY"}},
|
||||||
&cli.StringFlag{Name: "relay6", Value: models.DEFAULT_RELAY6, Usage: "ipv6 address of the relay", EnvVars: []string{"CROC_RELAY6"}},
|
&cli.StringFlag{Name: "relay6", Value: models.DEFAULT_RELAY6, Usage: "ipv6 address of the relay", EnvVars: []string{"CROC_RELAY6"}},
|
||||||
|
@ -130,7 +133,7 @@ func getConfigDir() (homedir string, err error) {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if envHomedir, isSet := os.LookupEnv("CROC_CONFIG_DIR"); isSet {
|
if envHomedir, isSet := os.LookupEnv("CROC_CONFIG_DIR"); isSet {
|
||||||
homedir = envHomedir
|
homedir = envHomedir
|
||||||
} else if xdgConfigHome, isSet := os.LookupEnv("XDG_CONFIG_HOME"); isSet {
|
} else if xdgConfigHome, isSet := os.LookupEnv("XDG_CONFIG_HOME"); isSet {
|
||||||
|
@ -138,7 +141,7 @@ func getConfigDir() (homedir string, err error) {
|
||||||
} else {
|
} else {
|
||||||
homedir = path.Join(homedir, ".config", "croc")
|
homedir = path.Join(homedir, ".config", "croc")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = os.Stat(homedir); os.IsNotExist(err) {
|
if _, err = os.Stat(homedir); os.IsNotExist(err) {
|
||||||
log.Debugf("creating home directory %s", homedir)
|
log.Debugf("creating home directory %s", homedir)
|
||||||
err = os.MkdirAll(homedir, 0700)
|
err = os.MkdirAll(homedir, 0700)
|
||||||
|
@ -193,6 +196,8 @@ func send(c *cli.Context) (err error) {
|
||||||
RelayPassword: determinePass(c),
|
RelayPassword: determinePass(c),
|
||||||
SendingText: c.String("text") != "",
|
SendingText: c.String("text") != "",
|
||||||
NoCompress: c.Bool("no-compress"),
|
NoCompress: c.Bool("no-compress"),
|
||||||
|
Overwrite: c.Bool("overwrite"),
|
||||||
|
Curve: c.String("curve"),
|
||||||
}
|
}
|
||||||
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
||||||
crocOptions.RelayAddress6 = ""
|
crocOptions.RelayAddress6 = ""
|
||||||
|
@ -388,6 +393,8 @@ func receive(c *cli.Context) (err error) {
|
||||||
RelayPassword: determinePass(c),
|
RelayPassword: determinePass(c),
|
||||||
OnlyLocal: c.Bool("local"),
|
OnlyLocal: c.Bool("local"),
|
||||||
IP: c.String("ip"),
|
IP: c.String("ip"),
|
||||||
|
Overwrite: c.Bool("overwrite"),
|
||||||
|
Curve: c.String("curve"),
|
||||||
}
|
}
|
||||||
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
||||||
crocOptions.RelayAddress6 = ""
|
crocOptions.RelayAddress6 = ""
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
|
|
||||||
var Socks5Proxy = ""
|
var Socks5Proxy = ""
|
||||||
|
|
||||||
const MAXBYTES = 4000000
|
var MAGIC_BYTES = []byte("croc")
|
||||||
|
|
||||||
// Comm is some basic TCP communication
|
// Comm is some basic TCP communication
|
||||||
type Comm struct {
|
type Comm struct {
|
||||||
|
@ -95,6 +95,7 @@ func (c *Comm) Write(b []byte) (n int, err error) {
|
||||||
fmt.Println("binary.Write failed:", err)
|
fmt.Println("binary.Write failed:", err)
|
||||||
}
|
}
|
||||||
tmpCopy := append(header.Bytes(), b...)
|
tmpCopy := append(header.Bytes(), b...)
|
||||||
|
tmpCopy = append(MAGIC_BYTES, tmpCopy...)
|
||||||
n, err = c.connection.Write(tmpCopy)
|
n, err = c.connection.Write(tmpCopy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("connection.Write failed: %w", err)
|
err = fmt.Errorf("connection.Write failed: %w", err)
|
||||||
|
@ -115,13 +116,25 @@ func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error) {
|
||||||
// must clear the timeout setting
|
// must clear the timeout setting
|
||||||
defer c.connection.SetDeadline(time.Time{})
|
defer c.connection.SetDeadline(time.Time{})
|
||||||
|
|
||||||
// read until we get 4 bytes for the header
|
// read until we get 4 bytes for the magic
|
||||||
header := make([]byte, 4)
|
header := make([]byte, 4)
|
||||||
_, err = io.ReadFull(c.connection, header)
|
_, err = io.ReadFull(c.connection, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("initial read error: %v", err)
|
log.Debugf("initial read error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !bytes.Equal(header, MAGIC_BYTES) {
|
||||||
|
err = fmt.Errorf("initial bytes are not magic: %x", header)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// read until we get 4 bytes for the header
|
||||||
|
header = make([]byte, 4)
|
||||||
|
_, err = io.ReadFull(c.connection, header)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("initial read error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var numBytesUint32 uint32
|
var numBytesUint32 uint32
|
||||||
rbuf := bytes.NewReader(header)
|
rbuf := bytes.NewReader(header)
|
||||||
|
@ -132,11 +145,6 @@ func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
numBytes = int(numBytesUint32)
|
numBytes = int(numBytesUint32)
|
||||||
if numBytes > MAXBYTES {
|
|
||||||
err = fmt.Errorf("too many bytes: %d", numBytes)
|
|
||||||
log.Debug(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// shorten the reading deadline in case getting weird data
|
// shorten the reading deadline in case getting weird data
|
||||||
if err := c.connection.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
if err := c.connection.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestComm(t *testing.T) {
|
func TestComm(t *testing.T) {
|
||||||
token := make([]byte, MAXBYTES)
|
token := make([]byte, 3000)
|
||||||
if _, err := rand.Read(token); err != nil {
|
if _, err := rand.Read(token); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
226
src/croc/croc.go
226
src/croc/croc.go
|
@ -20,10 +20,9 @@ import (
|
||||||
|
|
||||||
"github.com/denisbrodbeck/machineid"
|
"github.com/denisbrodbeck/machineid"
|
||||||
log "github.com/schollz/logger"
|
log "github.com/schollz/logger"
|
||||||
"github.com/schollz/pake/v2"
|
"github.com/schollz/pake/v3"
|
||||||
"github.com/schollz/peerdiscovery"
|
"github.com/schollz/peerdiscovery"
|
||||||
"github.com/schollz/progressbar/v3"
|
"github.com/schollz/progressbar/v3"
|
||||||
"github.com/tscholl2/siec"
|
|
||||||
|
|
||||||
"github.com/schollz/croc/v8/src/comm"
|
"github.com/schollz/croc/v8/src/comm"
|
||||||
"github.com/schollz/croc/v8/src/compress"
|
"github.com/schollz/croc/v8/src/compress"
|
||||||
|
@ -66,6 +65,8 @@ type Options struct {
|
||||||
SendingText bool
|
SendingText bool
|
||||||
NoCompress bool
|
NoCompress bool
|
||||||
IP string
|
IP string
|
||||||
|
Overwrite bool
|
||||||
|
Curve string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client holds the state of the croc transfer
|
// Client holds the state of the croc transfer
|
||||||
|
@ -104,11 +105,12 @@ type Client struct {
|
||||||
longestFilename int
|
longestFilename int
|
||||||
firstSend bool
|
firstSend bool
|
||||||
|
|
||||||
mutex *sync.Mutex
|
mutex *sync.Mutex
|
||||||
fread *os.File
|
fread *os.File
|
||||||
numfinished int
|
numfinished int
|
||||||
quit chan bool
|
quit chan bool
|
||||||
finishedNum int
|
finishedNum int
|
||||||
|
numberOfTransferedFiles int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chunk contains information about the
|
// Chunk contains information about the
|
||||||
|
@ -157,18 +159,16 @@ func New(ops Options) (c *Client, err error) {
|
||||||
Debug(c.Options.Debug)
|
Debug(c.Options.Debug)
|
||||||
log.Debugf("options: %+v", c.Options)
|
log.Debugf("options: %+v", c.Options)
|
||||||
|
|
||||||
if len(c.Options.SharedSecret) < 4 {
|
if len(c.Options.SharedSecret) < 6 {
|
||||||
err = fmt.Errorf("code is too short")
|
err = fmt.Errorf("code is too short")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.conn = make([]*comm.Comm, 16)
|
c.conn = make([]*comm.Comm, 16)
|
||||||
|
|
||||||
// initialize pake
|
// initialize pake for recipient
|
||||||
if c.Options.IsSender {
|
if !c.Options.IsSender {
|
||||||
c.Pake, err = pake.Init([]byte(c.Options.SharedSecret), 1, siec.SIEC255(), 1*time.Microsecond)
|
c.Pake, err = pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 0, c.Options.Curve)
|
||||||
} else {
|
|
||||||
c.Pake, err = pake.Init([]byte(c.Options.SharedSecret), 0, siec.SIEC255(), 1*time.Microsecond)
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -674,7 +674,13 @@ func (c *Client) Receive() (err error) {
|
||||||
}
|
}
|
||||||
log.Debug("exchanged header message")
|
log.Debug("exchanged header message")
|
||||||
fmt.Fprintf(os.Stderr, "\rsecuring channel...")
|
fmt.Fprintf(os.Stderr, "\rsecuring channel...")
|
||||||
return c.transfer(TransferOptions{})
|
err = c.transfer(TransferOptions{})
|
||||||
|
if err == nil {
|
||||||
|
if c.numberOfTransferedFiles == 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "\rNo files transferred.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) transfer(options TransferOptions) (err error) {
|
func (c *Client) transfer(options TransferOptions) (err error) {
|
||||||
|
@ -687,8 +693,9 @@ func (c *Client) transfer(options TransferOptions) (err error) {
|
||||||
log.Debug("ready")
|
log.Debug("ready")
|
||||||
if !c.Options.IsSender && !c.Step1ChannelSecured {
|
if !c.Options.IsSender && !c.Step1ChannelSecured {
|
||||||
err = message.Send(c.conn[0], c.Key, message.Message{
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
||||||
Type: "pake",
|
Type: "pake",
|
||||||
Bytes: c.Pake.Bytes(),
|
Bytes: c.Pake.Bytes(),
|
||||||
|
Bytes2: []byte(c.Options.Curve),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -815,102 +822,92 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) procesMessagePake(m message.Message) (err error) {
|
func (c *Client) processMessagePake(m message.Message) (err error) {
|
||||||
log.Debug("received pake payload")
|
log.Debug("received pake payload")
|
||||||
// if // c.spinner.Suffix != " performing PAKE..." {
|
|
||||||
// // c.spinner.Stop()
|
|
||||||
// // c.spinner.Suffix = " performing PAKE..."
|
|
||||||
// // c.spinner.Start()
|
|
||||||
// }
|
|
||||||
notVerified := !c.Pake.IsVerified()
|
|
||||||
err = c.Pake.Update(m.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (notVerified && c.Pake.IsVerified() && !c.Options.IsSender) || !c.Pake.IsVerified() {
|
|
||||||
err = message.Send(c.conn[0], c.Key, message.Message{
|
|
||||||
Type: "pake",
|
|
||||||
Bytes: c.Pake.Bytes(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if c.Pake.IsVerified() {
|
|
||||||
if c.Options.IsSender {
|
|
||||||
log.Debug("generating salt")
|
|
||||||
salt := make([]byte, 8)
|
|
||||||
if _, rerr := rand.Read(salt); err != nil {
|
|
||||||
log.Errorf("can't generate random numbers: %v", rerr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = message.Send(c.conn[0], c.Key, message.Message{
|
|
||||||
Type: "salt",
|
|
||||||
Bytes: salt,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// connects to the other ports of the server for transfer
|
var salt []byte
|
||||||
var wg sync.WaitGroup
|
if c.Options.IsSender {
|
||||||
wg.Add(len(c.Options.RelayPorts))
|
// initialize curve based on the recipient's choice
|
||||||
for i := 0; i < len(c.Options.RelayPorts); i++ {
|
log.Debugf("using curve %s", string(m.Bytes2))
|
||||||
log.Debugf("port: [%s]", c.Options.RelayPorts[i])
|
c.Pake, err = pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 1, string(m.Bytes2))
|
||||||
go func(j int) {
|
|
||||||
defer wg.Done()
|
|
||||||
var host string
|
|
||||||
if c.Options.RelayAddress == "localhost" {
|
|
||||||
host = c.Options.RelayAddress
|
|
||||||
} else {
|
|
||||||
host, _, err = net.SplitHostPort(c.Options.RelayAddress)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("bad relay address %s", c.Options.RelayAddress)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
server := net.JoinHostPort(host, c.Options.RelayPorts[j])
|
|
||||||
log.Debugf("connecting to %s", server)
|
|
||||||
c.conn[j+1], _, _, err = tcp.ConnectToTCPServer(
|
|
||||||
server,
|
|
||||||
c.Options.RelayPassword,
|
|
||||||
fmt.Sprintf("%s-%d", utils.SHA256(c.Options.SharedSecret)[:7], j),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
log.Debugf("connected to %s", server)
|
|
||||||
if !c.Options.IsSender {
|
|
||||||
go c.receiveData(j)
|
|
||||||
}
|
|
||||||
}(i)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) processMessageSalt(m message.Message) (done bool, err error) {
|
|
||||||
log.Debug("received salt")
|
|
||||||
if !c.Options.IsSender {
|
|
||||||
log.Debug("sending salt back")
|
|
||||||
err = message.Send(c.conn[0], c.Key, message.Message{
|
|
||||||
Type: "salt",
|
|
||||||
Bytes: m.Bytes,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, err
|
log.Error(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update the pake
|
||||||
|
err = c.Pake.Update(m.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate salt and send it back to recipient
|
||||||
|
log.Debug("generating salt")
|
||||||
|
salt = make([]byte, 8)
|
||||||
|
if _, rerr := rand.Read(salt); err != nil {
|
||||||
|
log.Errorf("can't generate random numbers: %v", rerr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debug("sender sending pake+salt")
|
||||||
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
||||||
|
Type: "pake",
|
||||||
|
Bytes: c.Pake.Bytes(),
|
||||||
|
Bytes2: salt,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
err = c.Pake.Update(m.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
salt = m.Bytes2
|
||||||
}
|
}
|
||||||
log.Debugf("session key is verified, generating encryption with salt: %x", m.Bytes)
|
// generate key
|
||||||
key, err := c.Pake.SessionKey()
|
key, err := c.Pake.SessionKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, err
|
return err
|
||||||
}
|
}
|
||||||
c.Key, _, err = crypt.New(key, m.Bytes)
|
c.Key, _, err = crypt.New(key, salt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, err
|
return err
|
||||||
}
|
}
|
||||||
log.Debugf("key = %+x", c.Key)
|
log.Debugf("generated key = %+x with salt %x", c.Key, salt)
|
||||||
if c.Options.IsSender {
|
|
||||||
|
// connects to the other ports of the server for transfer
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(len(c.Options.RelayPorts))
|
||||||
|
for i := 0; i < len(c.Options.RelayPorts); i++ {
|
||||||
|
log.Debugf("port: [%s]", c.Options.RelayPorts[i])
|
||||||
|
go func(j int) {
|
||||||
|
defer wg.Done()
|
||||||
|
var host string
|
||||||
|
if c.Options.RelayAddress == "localhost" {
|
||||||
|
host = c.Options.RelayAddress
|
||||||
|
} else {
|
||||||
|
host, _, err = net.SplitHostPort(c.Options.RelayAddress)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("bad relay address %s", c.Options.RelayAddress)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server := net.JoinHostPort(host, c.Options.RelayPorts[j])
|
||||||
|
log.Debugf("connecting to %s", server)
|
||||||
|
c.conn[j+1], _, _, err = tcp.ConnectToTCPServer(
|
||||||
|
server,
|
||||||
|
c.Options.RelayPassword,
|
||||||
|
fmt.Sprintf("%s-%d", utils.SHA256(c.Options.SharedSecret[:5])[:6], j),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
log.Debugf("connected to %s", server)
|
||||||
|
if !c.Options.IsSender {
|
||||||
|
go c.receiveData(j)
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if !c.Options.IsSender {
|
||||||
log.Debug("sending external IP")
|
log.Debug("sending external IP")
|
||||||
err = message.Send(c.conn[0], c.Key, message.Message{
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
||||||
Type: "externalip",
|
Type: "externalip",
|
||||||
|
@ -923,7 +920,7 @@ func (c *Client) processMessageSalt(m message.Message) (done bool, err error) {
|
||||||
|
|
||||||
func (c *Client) processExternalIP(m message.Message) (done bool, err error) {
|
func (c *Client) processExternalIP(m message.Message) (done bool, err error) {
|
||||||
log.Debugf("received external IP: %+v", m)
|
log.Debugf("received external IP: %+v", m)
|
||||||
if !c.Options.IsSender {
|
if c.Options.IsSender {
|
||||||
err = message.Send(c.conn[0], c.Key, message.Message{
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
||||||
Type: "externalip",
|
Type: "externalip",
|
||||||
Message: c.ExternalIP,
|
Message: c.ExternalIP,
|
||||||
|
@ -949,6 +946,15 @@ func (c *Client) processMessage(payload []byte) (done bool, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only "pake" messages should be unencrypted
|
||||||
|
// if a non-"pake" message is received unencrypted something
|
||||||
|
// is weird
|
||||||
|
if m.Type != "pake" && c.Key == nil {
|
||||||
|
err = fmt.Errorf("unencrypted communication rejected")
|
||||||
|
done = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch m.Type {
|
switch m.Type {
|
||||||
case "finished":
|
case "finished":
|
||||||
err = message.Send(c.conn[0], c.Key, message.Message{
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
||||||
|
@ -958,13 +964,11 @@ func (c *Client) processMessage(payload []byte) (done bool, err error) {
|
||||||
c.SuccessfulTransfer = true
|
c.SuccessfulTransfer = true
|
||||||
return
|
return
|
||||||
case "pake":
|
case "pake":
|
||||||
err = c.procesMessagePake(m)
|
err = c.processMessagePake(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("pake not successful: %w", err)
|
err = fmt.Errorf("pake not successful: %w", err)
|
||||||
log.Debug(err)
|
log.Debug(err)
|
||||||
}
|
}
|
||||||
case "salt":
|
|
||||||
done, err = c.processMessageSalt(m)
|
|
||||||
case "externalip":
|
case "externalip":
|
||||||
done, err = c.processExternalIP(m)
|
done, err = c.processExternalIP(m)
|
||||||
case "error":
|
case "error":
|
||||||
|
@ -1229,12 +1233,21 @@ func (c *Client) updateIfRecipientHasFileInfo() (err error) {
|
||||||
err = c.createEmptyFileAndFinish(fileInfo, i)
|
err = c.createEmptyFileAndFinish(fileInfo, i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
c.numberOfTransferedFiles++
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Debugf("%s %+x %+x %+v", fileInfo.Name, fileHash, fileInfo.Hash, errHash)
|
log.Debugf("%s %+x %+x %+v", fileInfo.Name, fileHash, fileInfo.Hash, errHash)
|
||||||
if !bytes.Equal(fileHash, fileInfo.Hash) {
|
if !bytes.Equal(fileHash, fileInfo.Hash) {
|
||||||
log.Debugf("hashes are not equal %x != %x", fileHash, fileInfo.Hash)
|
log.Debugf("hashes are not equal %x != %x", fileHash, fileInfo.Hash)
|
||||||
|
if errHash == nil && !c.Options.Overwrite {
|
||||||
|
ans := utils.GetInput(fmt.Sprintf("\rOverwrite '%s'? (y/n) ", path.Join(fileInfo.FolderRemote, fileInfo.Name)))
|
||||||
|
if strings.TrimSpace(strings.ToLower(ans)) != "y" {
|
||||||
|
fmt.Fprintf(os.Stderr, "skipping '%s'", path.Join(fileInfo.FolderRemote, fileInfo.Name))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("hashes are equal %x == %x", fileHash, fileInfo.Hash)
|
log.Debugf("hashes are equal %x == %x", fileHash, fileInfo.Hash)
|
||||||
}
|
}
|
||||||
|
@ -1245,6 +1258,7 @@ func (c *Client) updateIfRecipientHasFileInfo() (err error) {
|
||||||
if errHash != nil || !bytes.Equal(fileHash, fileInfo.Hash) {
|
if errHash != nil || !bytes.Equal(fileHash, fileInfo.Hash) {
|
||||||
finished = false
|
finished = false
|
||||||
c.FilesToTransferCurrentNum = i
|
c.FilesToTransferCurrentNum = i
|
||||||
|
c.numberOfTransferedFiles++
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// TODO: print out something about this file already existing
|
// TODO: print out something about this file already existing
|
||||||
|
|
|
@ -14,6 +14,7 @@ type Message struct {
|
||||||
Type string `json:"t,omitempty"`
|
Type string `json:"t,omitempty"`
|
||||||
Message string `json:"m,omitempty"`
|
Message string `json:"m,omitempty"`
|
||||||
Bytes []byte `json:"b,omitempty"`
|
Bytes []byte `json:"b,omitempty"`
|
||||||
|
Bytes2 []byte `json:"b2,omitempty"`
|
||||||
Num int `json:"n,omitempty"`
|
Num int `json:"n,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ func Encode(key []byte, m Message) (b []byte, err error) {
|
||||||
log.Debugf("writing %s message (encrypted)", m.Type)
|
log.Debugf("writing %s message (encrypted)", m.Type)
|
||||||
b, err = crypt.Encrypt(b, key)
|
b, err = crypt.Encrypt(b, key)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("writing %s message", m.Type)
|
log.Debugf("writing %s message (unencrypted)", m.Type)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -58,5 +59,12 @@ func Decode(key []byte, b []byte) (m Message, err error) {
|
||||||
}
|
}
|
||||||
b = compress.Decompress(b)
|
b = compress.Decompress(b)
|
||||||
err = json.Unmarshal(b, &m)
|
err = json.Unmarshal(b, &m)
|
||||||
|
if err == nil {
|
||||||
|
if key != nil {
|
||||||
|
log.Debugf("read %s message (encrypted)", m.Type)
|
||||||
|
} else {
|
||||||
|
log.Debugf("read %s message (unencrypted)", m.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/schollz/logger"
|
log "github.com/schollz/logger"
|
||||||
"github.com/schollz/pake/v2"
|
"github.com/schollz/pake/v3"
|
||||||
|
|
||||||
"github.com/schollz/croc/v8/src/comm"
|
"github.com/schollz/croc/v8/src/comm"
|
||||||
"github.com/schollz/croc/v8/src/crypt"
|
"github.com/schollz/croc/v8/src/crypt"
|
||||||
|
@ -152,7 +152,7 @@ var weakKey = []byte{1, 2, 3}
|
||||||
|
|
||||||
func (s *server) clientCommunication(port string, c *comm.Comm) (room string, err error) {
|
func (s *server) clientCommunication(port string, c *comm.Comm) (room string, err error) {
|
||||||
// establish secure password with PAKE for communication with relay
|
// establish secure password with PAKE for communication with relay
|
||||||
B, err := pake.InitCurve(weakKey, 1, "siec", 1*time.Microsecond)
|
B, err := pake.InitCurve(weakKey, 1, "siec")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -173,14 +173,6 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Abytes, err = c.Receive()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = B.Update(Abytes)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
strongKey, err := B.SessionKey()
|
strongKey, err := B.SessionKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -266,6 +258,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if s.rooms.rooms[room].full {
|
if s.rooms.rooms[room].full {
|
||||||
|
defer s.deleteRoom(room)
|
||||||
s.rooms.Unlock()
|
s.rooms.Unlock()
|
||||||
bSend, err = crypt.Encrypt([]byte("room full"), strongKeyForEncryption)
|
bSend, err = crypt.Encrypt([]byte("room full"), strongKeyForEncryption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -274,7 +267,6 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
||||||
err = c.Send(bSend)
|
err = c.Send(bSend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
s.deleteRoom(room)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -425,7 +417,7 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati
|
||||||
}
|
}
|
||||||
|
|
||||||
// get PAKE connection with server to establish strong key to transfer info
|
// get PAKE connection with server to establish strong key to transfer info
|
||||||
A, err := pake.InitCurve(weakKey, 0, "siec", 1*time.Microsecond)
|
A, err := pake.InitCurve(weakKey, 0, "siec")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -441,10 +433,6 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = c.Send(A.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
strongKey, err := A.SessionKey()
|
strongKey, err := A.SessionKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -140,13 +141,27 @@ func LocalIP() string {
|
||||||
return localAddr.IP.String()
|
return localAddr.IP.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GenerateRandomPin() string {
|
||||||
|
s := ""
|
||||||
|
max := new(big.Int)
|
||||||
|
max.SetInt64(9)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
v, err := rand.Int(rand.Reader, max)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s += fmt.Sprintf("%d", v)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// GetRandomName returns mnemoicoded random name
|
// GetRandomName returns mnemoicoded random name
|
||||||
func GetRandomName() string {
|
func GetRandomName() string {
|
||||||
var result []string
|
var result []string
|
||||||
bs := make([]byte, 4)
|
bs := make([]byte, 4)
|
||||||
rand.Read(bs)
|
rand.Read(bs)
|
||||||
result = mnemonicode.EncodeWordList(result, bs)
|
result = mnemonicode.EncodeWordList(result, bs)
|
||||||
return strings.Join(result, "-")
|
return GenerateRandomPin() + "-" + strings.Join(result, "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByteCountDecimal converts bytes to human readable byte string
|
// ByteCountDecimal converts bytes to human readable byte string
|
||||||
|
|
|
@ -10,11 +10,14 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/schollz/croc/v8/src/models"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var bigFileSize = 75000000
|
||||||
|
|
||||||
func bigFile() {
|
func bigFile() {
|
||||||
ioutil.WriteFile("bigfile.test", bytes.Repeat([]byte("z"), 75000000), 0666)
|
ioutil.WriteFile("bigfile.test", bytes.Repeat([]byte("z"), bigFileSize), 0666)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMD5(b *testing.B) {
|
func BenchmarkMD5(b *testing.B) {
|
||||||
|
@ -47,6 +50,14 @@ func BenchmarkSha256(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkMissingChunks(b *testing.B) {
|
||||||
|
bigFile()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
MissingChunks("bigfile.test", int64(bigFileSize), models.TCP_BUFFER_SIZE/2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestExists(t *testing.T) {
|
func TestExists(t *testing.T) {
|
||||||
bigFile()
|
bigFile()
|
||||||
defer os.Remove("bigfile.test")
|
defer os.Remove("bigfile.test")
|
||||||
|
@ -184,6 +195,7 @@ func TestLocalIP(t *testing.T) {
|
||||||
|
|
||||||
func TestGetRandomName(t *testing.T) {
|
func TestGetRandomName(t *testing.T) {
|
||||||
name := GetRandomName()
|
name := GetRandomName()
|
||||||
|
fmt.Println(name)
|
||||||
assert.NotEmpty(t, name)
|
assert.NotEmpty(t, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue