Compare commits

...

6 Commits
main ... fix3

Author SHA1 Message Date
Zack Scholl fffdf39bea use relay public key and password to connect 2021-04-23 16:59:28 -07:00
Zack Scholl 675f896d61 beter tests 2021-04-23 14:40:31 -07:00
Zack Scholl 496a6d3a05 works 2021-04-23 14:10:54 -07:00
Zack Scholl f68e194a4a bug fixes 2021-04-23 13:50:12 -07:00
Zack Scholl c3c892f95d use age for tcp 2021-04-23 13:48:19 -07:00
Zack Scholl 9db87434a7 add age encryption 2021-04-23 12:39:27 -07:00
9 changed files with 312 additions and 172 deletions

1
go.mod
View File

@ -3,6 +3,7 @@ module github.com/schollz/croc/v9
go 1.13
require (
filippo.io/age v1.0.0-rc.1
github.com/OneOfOne/xxhash v1.2.5 // indirect
github.com/cespare/xxhash v1.1.0
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect

5
go.sum
View File

@ -1,3 +1,5 @@
filippo.io/age v1.0.0-rc.1 h1:jQ+dz16Xxx3W/WY+YS0J96nVAAidLHO3kfQe0eOmKgI=
filippo.io/age v1.0.0-rc.1/go.mod h1:Vvd9IlwNo4Au31iqNZeZVnYtGcOf/wT4mtvZQ2ODlSk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
@ -13,8 +15,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kalafut/imohash v1.0.0 h1:LgCJ+p/BwM2HKpOxFopkeddpzVCfm15EtXMroXD1SYE=
github.com/kalafut/imohash v1.0.0/go.mod h1:c3RHT80ZAp5C/aYgQI92ZlrOymqkZnRDprU87kg75HI=
github.com/kalafut/imohash v1.0.2 h1:j/cUPa15YvXv7abJlM+kdJIycbBMpmO7WqhPl4YB76I=
github.com/kalafut/imohash v1.0.2/go.mod h1:PjHBF0vpo1q7zMqiTn0qwSTQU2wDn5QIe8S8sFQuZS8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@ -61,6 +61,7 @@ github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094/go.mod h1:KL9+ubr1JZ
github.com/twmb/murmur3 v1.1.5 h1:i9OLS9fkuLzBXjt6dptlAEyk58fJsSTXbRg3SgVyqgk=
github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/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-20210415154028-4f45737414dc h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA=
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=

View File

@ -16,6 +16,7 @@ import (
"github.com/schollz/cli/v2"
"github.com/schollz/croc/v9/src/comm"
"github.com/schollz/croc/v9/src/croc"
"github.com/schollz/croc/v9/src/crypt"
"github.com/schollz/croc/v9/src/models"
"github.com/schollz/croc/v9/src/tcp"
"github.com/schollz/croc/v9/src/utils"
@ -75,6 +76,7 @@ func Run() (err error) {
},
Flags: []cli.Flag{
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"},
&cli.StringFlag{Name: "identity", Value: "", Usage: "identity file with credentials"},
},
},
}
@ -93,7 +95,8 @@ func Run() (err error) {
&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: "out", Value: ".", Usage: "specify an output folder to receive the file"},
&cli.StringFlag{Name: "pass", Value: models.DEFAULT_PASSPHRASE, Usage: "password for the relay", EnvVars: []string{"CROC_PASS"}},
&cli.StringFlag{Name: "relay-pass", Value: models.DEFAULT_RELAY_PASSWORD, Usage: "password for the relay (defaults to public relay)", EnvVars: []string{"CROC_PASS"}},
&cli.StringFlag{Name: "relay-key", Value: models.DEFAULT_RELAY_KEYPUBLIC, Usage: "public key for the relay (defaults to public relay)", EnvVars: []string{"CROC_KEY"}},
&cli.StringFlag{Name: "socks5", Value: "", Usage: "add a socks5 proxy", EnvVars: []string{"SOCKS5_PROXY"}},
}
app.EnableBashCompletion = true
@ -167,15 +170,6 @@ func getConfigFile() string {
return path.Join(configFile, "send.json")
}
func determinePass(c *cli.Context) (pass string) {
pass = c.String("pass")
b, err := ioutil.ReadFile(pass)
if err == nil {
pass = strings.TrimSpace(string(b))
}
return
}
func send(c *cli.Context) (err error) {
setDebugLevel(c)
comm.Socks5Proxy = c.String("socks5")
@ -193,7 +187,8 @@ func send(c *cli.Context) (err error) {
RelayPorts: strings.Split(c.String("ports"), ","),
Ask: c.Bool("ask"),
NoMultiplexing: c.Bool("no-multi"),
RelayPassword: determinePass(c),
RelayPassword: c.String("relay-pass"),
RelayKeyPublic: c.String("relay-key"),
SendingText: c.String("text") != "",
NoCompress: c.Bool("no-compress"),
Overwrite: c.Bool("overwrite"),
@ -226,9 +221,12 @@ func send(c *cli.Context) (err error) {
if !c.IsSet("code") {
crocOptions.SharedSecret = rememberedOptions.SharedSecret
}
if !c.IsSet("pass") {
if !c.IsSet("relay-pass") {
crocOptions.RelayPassword = rememberedOptions.RelayPassword
}
if !c.IsSet("relay-key") {
crocOptions.RelayKeyPublic = rememberedOptions.RelayKeyPublic
}
}
var fnames []string
@ -383,20 +381,21 @@ func saveConfig(c *cli.Context, crocOptions croc.Options) {
func receive(c *cli.Context) (err error) {
comm.Socks5Proxy = c.String("socks5")
crocOptions := croc.Options{
SharedSecret: c.String("code"),
IsSender: false,
Debug: c.Bool("debug"),
NoPrompt: c.Bool("yes"),
RelayAddress: c.String("relay"),
RelayAddress6: c.String("relay6"),
Stdout: c.Bool("stdout"),
Ask: c.Bool("ask"),
RelayPassword: determinePass(c),
OnlyLocal: c.Bool("local"),
IP: c.String("ip"),
Overwrite: c.Bool("overwrite"),
Curve: c.String("curve"),
HashAlgorithm: "xxhash",
SharedSecret: c.String("code"),
IsSender: false,
Debug: c.Bool("debug"),
NoPrompt: c.Bool("yes"),
RelayAddress: c.String("relay"),
RelayAddress6: c.String("relay6"),
Stdout: c.Bool("stdout"),
Ask: c.Bool("ask"),
RelayPassword: c.String("relay-pass"),
RelayKeyPublic: c.String("relay-key"),
OnlyLocal: c.Bool("local"),
IP: c.String("ip"),
Overwrite: c.Bool("overwrite"),
Curve: c.String("curve"),
HashAlgorithm: "xxhash",
}
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
crocOptions.RelayAddress6 = ""
@ -481,23 +480,40 @@ func receive(c *cli.Context) (err error) {
}
func relay(c *cli.Context) (err error) {
log.Infof("starting croc relay version %v", Version)
identityFile := "identity.croc"
if c.String("identity") != "" {
identityFile = c.String("identity")
}
if !utils.Exists(identityFile) {
err = crypt.GenerateIdentityAndPassword(identityFile)
if err != nil {
return
}
}
keyPrivate, keyPublic, password, err := crypt.LoadIdentityAndPassword(identityFile)
if err != nil {
return
}
ports := strings.Split(c.String("ports"), ",")
tcpPorts := strings.Join(ports[1:], ",")
fmt.Printf("starting croc relay version %v\n", Version)
identityFileAbs, _ := filepath.Abs(identityFile)
fmt.Printf("identity file = %s\n", identityFileAbs)
fmt.Printf("\n\nexample to send files:\n\n croc --relay localhost:%s --relay-pass %s --relay-key %s send file.txt", ports[0], password, keyPublic)
debugString := "info"
if c.Bool("debug") {
debugString = "debug"
}
ports := strings.Split(c.String("ports"), ",")
tcpPorts := strings.Join(ports[1:], ",")
for i, port := range ports {
if i == 0 {
continue
}
go func(portStr string) {
err = tcp.Run(debugString, portStr, determinePass(c))
err = tcp.Run(debugString, portStr, password, keyPublic, keyPrivate)
if err != nil {
panic(err)
}
}(port)
}
return tcp.Run(debugString, ports[0], determinePass(c), tcpPorts)
return tcp.Run(debugString, ports[0], password, keyPublic, keyPrivate, tcpPorts)
}

View File

@ -48,26 +48,31 @@ func Debug(debug bool) {
// Options specifies user specific options
type Options struct {
IsSender bool
SharedSecret string
Debug bool
RelayAddress string
RelayAddress6 string
RelayPorts []string
RelayPassword string
Stdout bool
NoPrompt bool
NoMultiplexing bool
DisableLocal bool
OnlyLocal bool
IgnoreStdin bool
Ask bool
SendingText bool
NoCompress bool
IP string
Overwrite bool
Curve string
HashAlgorithm string
IsSender bool
SharedSecret string
Debug bool
RelayAddress string
RelayAddress6 string
RelayPorts []string
RelayPassword string
RelayKeyPrivate string
RelayKeyPublic string
LocalRelayPassword string
LocalRelayKeyPrivate string
LocalRelayKeyPublic string
Stdout bool
NoPrompt bool
NoMultiplexing bool
DisableLocal bool
OnlyLocal bool
IgnoreStdin bool
Ask bool
SendingText bool
NoCompress bool
IP string
Overwrite bool
Curve string
HashAlgorithm string
}
// Client holds the state of the croc transfer
@ -288,7 +293,7 @@ func (c *Client) setupLocalRelay() {
if c.Options.Debug {
debugString = "debug"
}
err := tcp.Run(debugString, portStr, c.Options.RelayPassword, strings.Join(c.Options.RelayPorts[1:], ","))
err := tcp.Run(debugString, portStr, c.Options.LocalRelayPassword, c.Options.LocalRelayKeyPublic, c.Options.LocalRelayKeyPrivate, strings.Join(c.Options.RelayPorts[1:], ","))
if err != nil {
panic(err)
}
@ -320,7 +325,7 @@ func (c *Client) transferOverLocalRelay(options TransferOptions, errchan chan<-
time.Sleep(500 * time.Millisecond)
log.Debug("establishing connection")
var banner string
conn, banner, ipaddr, err := tcp.ConnectToTCPServer("localhost:"+c.Options.RelayPorts[0], c.Options.RelayPassword, c.Options.SharedSecret[:3])
conn, banner, ipaddr, err := tcp.ConnectToTCPServer("localhost:"+c.Options.RelayPorts[0], c.Options.LocalRelayPassword, c.Options.LocalRelayKeyPublic, c.Options.SharedSecret[:3])
log.Debugf("banner: %s", banner)
if err != nil {
err = fmt.Errorf("could not connect to localhost:%s: %w", c.Options.RelayPorts[0], err)
@ -361,8 +366,9 @@ func (c *Client) Send(options TransferOptions) (err error) {
if c.Options.RelayAddress != models.DEFAULT_RELAY {
flags.WriteString("--relay " + c.Options.RelayAddress + " ")
}
if c.Options.RelayPassword != models.DEFAULT_PASSPHRASE {
flags.WriteString("--pass " + c.Options.RelayPassword + " ")
if c.Options.RelayPassword != models.DEFAULT_RELAY_PASSWORD || c.Options.RelayKeyPublic != models.DEFAULT_RELAY_KEYPUBLIC {
flags.WriteString("--relay-pass " + c.Options.RelayPassword + " ")
flags.WriteString("--relay-key " + c.Options.RelayKeyPublic + " ")
}
fmt.Fprintf(os.Stderr, "Code is: %[1]s\nOn the other computer run\n\ncroc %[2]s%[1]s\n", c.Options.SharedSecret, flags.String())
if c.Options.Ask {
@ -376,6 +382,8 @@ func (c *Client) Send(options TransferOptions) (err error) {
errchan := make(chan error, 1)
if !c.Options.DisableLocal {
c.Options.LocalRelayKeyPublic, c.Options.LocalRelayKeyPrivate, _ = crypt.NewAge()
c.Options.LocalRelayPassword, _ = crypt.GenerateRandomString(6)
// add two things to the error channel
errchan = make(chan error, 2)
c.setupLocalRelay()
@ -405,7 +413,7 @@ func (c *Client) Send(options TransferOptions) (err error) {
log.Debugf("got host '%v' and port '%v'", host, port)
address = net.JoinHostPort(host, port)
log.Debugf("trying connection to %s", address)
conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], durations[i])
conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.RelayKeyPublic, c.Options.SharedSecret[:3], durations[i])
if err == nil {
c.Options.RelayAddress = address
break
@ -599,7 +607,7 @@ func (c *Client) Receive() (err error) {
log.Debugf("got host '%v' and port '%v'", host, port)
address = net.JoinHostPort(host, port)
log.Debugf("trying connection to %s", address)
c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], durations[i])
c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.RelayKeyPublic, c.Options.SharedSecret[:3], durations[i])
if err == nil {
c.Options.RelayAddress = address
break
@ -652,7 +660,7 @@ func (c *Client) Receive() (err error) {
}
serverTry := fmt.Sprintf("%s:%s", ip, port)
conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], 50*time.Millisecond)
conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.RelayKeyPublic, c.Options.SharedSecret[:3], 50*time.Millisecond)
if errConn != nil {
log.Debugf("could not connect to " + serverTry)
continue
@ -909,6 +917,7 @@ func (c *Client) processMessagePake(m message.Message) (err error) {
c.conn[j+1], _, _, err = tcp.ConnectToTCPServer(
server,
c.Options.RelayPassword,
c.Options.RelayKeyPublic,
fmt.Sprintf("%s-%d", utils.SHA256(c.Options.SharedSecret[:5])[:6], j),
)
if err != nil {

View File

@ -1,13 +1,19 @@
package crypt
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
"log"
"math/big"
"strings"
"filippo.io/age"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/pbkdf2"
@ -73,6 +79,118 @@ func Decrypt(encrypted []byte, key []byte) (plaintext []byte, err error) {
return
}
// GenerateIdentityAndPassword will generate a file with a public age identity and password
func GenerateIdentityAndPassword(fname string) (err error) {
keyPublic, keyPrivate, err := NewAge()
if err != nil {
return
}
password, err := GenerateRandomString(6)
if err != nil {
return
}
err = ioutil.WriteFile(fname, []byte(fmt.Sprintf("%s\n%s\n%s", keyPrivate, keyPublic, password)), 0644)
return
}
// LoadIdentityAndPassword will load a file with a public age identity and password
func LoadIdentityAndPassword(fname string) (keyPrivate string, keyPublic string, password string, err error) {
b, err := ioutil.ReadFile(fname)
if err != nil {
return
}
foo := strings.Fields(string(b))
if len(foo) < 3 {
err = fmt.Errorf("malformed file")
return
}
keyPrivate = foo[0]
keyPublic = foo[1]
password = foo[2]
_, err = age.ParseX25519Identity(keyPrivate)
if err != nil {
return
}
_, err = age.ParseX25519Recipient(keyPublic)
if err != nil {
return
}
return
}
// GenerateRandomString returns a securely generated random string.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomString(n int) (string, error) {
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
ret := make([]byte, n)
for i := 0; i < n; i++ {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
if err != nil {
return "", err
}
ret[i] = letters[num.Int64()]
}
return string(ret), nil
}
func NewAge() (pubkey string, privkey string, err error) {
identity, err := age.GenerateX25519Identity()
if err != nil {
return
}
pubkey = identity.Recipient().String()
privkey = identity.String()
return
}
func EncryptAge(data []byte, pubkey string) (encrypted []byte, err error) {
recipient, err := age.ParseX25519Recipient(pubkey)
if err != nil {
return
}
out := &bytes.Buffer{}
w, err := age.Encrypt(out, recipient)
if err != nil {
return
}
_, err = w.Write(data)
if err != nil {
return
}
err = w.Close()
if err != nil {
return
}
encrypted = out.Bytes()
return
}
func DecryptAge(encrypted []byte, privkey string) (data []byte, err error) {
identity, err := age.ParseX25519Identity(privkey)
if err != nil {
return
}
r, err := age.Decrypt(bytes.NewReader(encrypted), identity)
if err != nil {
return
}
out := &bytes.Buffer{}
_, err = io.Copy(out, r)
if err != nil {
return
}
data = out.Bytes()
return
}
// NewArgon2 generates a new key based on a passphrase and salt
// using argon2
// https://pkg.go.dev/golang.org/x/crypto/argon2

View File

@ -2,8 +2,10 @@ package crypt
import (
"fmt"
"os"
"testing"
"github.com/schollz/croc/v9/src/utils"
"github.com/stretchr/testify/assert"
)
@ -103,3 +105,31 @@ func TestEncryptionChaCha(t *testing.T) {
_, _, err = NewArgon2([]byte(""), nil)
assert.NotNil(t, err)
}
func TestEncryptionAge(t *testing.T) {
pub, priv, err := NewAge()
fmt.Printf("key: %s\n", pub)
assert.Nil(t, err)
msg := []byte("hello, world")
enc, err := EncryptAge(msg, pub)
assert.Nil(t, err)
dec, err := DecryptAge(enc, priv)
assert.Nil(t, err)
assert.Equal(t, msg, dec)
}
func TestGenerate(t *testing.T) {
err := GenerateIdentityAndPassword("test")
assert.Nil(t, err)
assert.True(t, utils.Exists("test"))
keyPrivate, keyPublic, password, err := LoadIdentityAndPassword("test")
assert.Nil(t, err)
fmt.Println(keyPrivate)
fmt.Println(keyPublic)
fmt.Println(password)
_, _, _, err = LoadIdentityAndPassword("crypt.go")
assert.NotNil(t, err)
_, _, _, err = LoadIdentityAndPassword("doesntexist")
assert.NotNil(t, err)
os.Remove("test")
}

View File

@ -9,12 +9,13 @@ import (
// TCP_BUFFER_SIZE is the maximum packet size
const TCP_BUFFER_SIZE = 1024 * 64
// DEFAULT_RELAY is the default relay used (can be set using --relay)
// DEFAULT_RELAY is the default relay used (can be set using --relay-pass)
var (
DEFAULT_RELAY = "croc.schollz.com"
DEFAULT_RELAY6 = "croc6.schollz.com"
DEFAULT_PORT = "9009"
DEFAULT_PASSPHRASE = "pass123"
DEFAULT_RELAY = "croc.schollz.com"
DEFAULT_RELAY6 = "croc6.schollz.com"
DEFAULT_PORT = "9009"
DEFAULT_RELAY_PASSWORD = "ZiNO9Y"
DEFAULT_RELAY_KEYPUBLIC = "age10yrxthzjrcr0e59nucg0epgnn0qpjv9rhsxqs90rdn335edgnueqrtdnyh"
)
func init() {

View File

@ -8,8 +8,9 @@ import (
"sync"
"time"
"filippo.io/age"
log "github.com/schollz/logger"
"github.com/schollz/pake/v3"
"golang.org/x/crypto/bcrypt"
"github.com/schollz/croc/v9/src/comm"
"github.com/schollz/croc/v9/src/crypt"
@ -18,6 +19,8 @@ import (
type server struct {
port string
keyPrivate string
keyPublic string
debugLevel string
banner string
password string
@ -40,9 +43,11 @@ var timeToRoomDeletion = 10 * time.Minute
var pingRoom = "pinglkasjdlfjsaldjf"
// Run starts a tcp listener, run async
func Run(debugLevel, port, password string, banner ...string) (err error) {
func Run(debugLevel, port, password string, keyPublic string, keyPrivate string, banner ...string) (err error) {
s := new(server)
s.port = port
s.keyPrivate = keyPrivate
s.keyPublic = keyPublic
s.password = password
s.debugLevel = debugLevel
if len(banner) > 0 {
@ -85,7 +90,7 @@ func (s *server) start() (err error) {
}
func (s *server) run() (err error) {
log.Infof("starting TCP server on " + s.port)
log.Debugf("starting TCP server on " + s.port)
server, err := net.Listen("tcp", ":"+s.port)
if err != nil {
return fmt.Errorf("error listening on %s: %w", s.port, err)
@ -151,59 +156,34 @@ func (s *server) run() (err error) {
var weakKey = []byte{1, 2, 3}
func (s *server) clientCommunication(port string, c *comm.Comm) (room string, err error) {
// establish secure password with PAKE for communication with relay
B, err := pake.InitCurve(weakKey, 1, "siec")
// get public key of the connecting client
retBytesEnc, err := c.Receive()
if err != nil {
return
}
Abytes, err := c.Receive()
if err != nil {
return
}
if bytes.Equal(Abytes, []byte("ping")) {
if bytes.Equal(retBytesEnc, []byte("ping")) {
room = pingRoom
c.Send([]byte("pong"))
return
}
err = B.Update(Abytes)
retBytes, err := crypt.DecryptAge(retBytesEnc, s.keyPrivate)
if err != nil {
return
}
err = c.Send(B.Bytes())
if err != nil {
return
}
strongKey, err := B.SessionKey()
if err != nil {
return
}
log.Debugf("strongkey: %x", strongKey)
// check whether we have a valid public key from client
foo := bytes.Split(retBytes, []byte("--"))
keyPublic := string(foo[0])
hashedPassword := foo[1]
// receive salt
salt, err := c.Receive()
err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(s.password))
if err != nil {
return
}
strongKeyForEncryption, _, err := crypt.New(strongKey, salt)
if err != nil {
return
}
log.Debugf("waiting for password")
passwordBytesEnc, err := c.Receive()
if err != nil {
return
}
passwordBytes, err := crypt.Decrypt(passwordBytesEnc, strongKeyForEncryption)
if err != nil {
return
}
if strings.TrimSpace(string(passwordBytes)) != s.password {
err = fmt.Errorf("bad password")
enc, _ := crypt.Decrypt([]byte(err.Error()), strongKeyForEncryption)
if err := c.Send(enc); err != nil {
return "", fmt.Errorf("send error: %w", err)
}
return
}
_, err = age.ParseX25519Recipient(keyPublic)
if err != nil {
err = fmt.Errorf("bad public key: %s", keyPublic)
return
}
@ -213,7 +193,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
banner = "ok"
}
log.Debugf("sending '%s'", banner)
bSend, err := crypt.Encrypt([]byte(banner+"|||"+c.Connection().RemoteAddr().String()), strongKeyForEncryption)
bSend, err := crypt.EncryptAge([]byte(banner+"|||"+c.Connection().RemoteAddr().String()), keyPublic)
if err != nil {
return
}
@ -228,7 +208,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
if err != nil {
return
}
roomBytes, err := crypt.Decrypt(enc, strongKeyForEncryption)
roomBytes, err := crypt.DecryptAge(enc, s.keyPrivate)
if err != nil {
return
}
@ -244,7 +224,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
s.rooms.Unlock()
// tell the client that they got the room
bSend, err = crypt.Encrypt([]byte("ok"), strongKeyForEncryption)
bSend, err = crypt.EncryptAge([]byte("ok"), keyPublic)
if err != nil {
return
}
@ -259,7 +239,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
}
if s.rooms.rooms[room].full {
s.rooms.Unlock()
bSend, err = crypt.Encrypt([]byte("room full"), strongKeyForEncryption)
bSend, err = crypt.EncryptAge([]byte("room full"), keyPublic)
if err != nil {
return
}
@ -293,7 +273,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
}(otherConnection, c, &wg)
// tell the sender everything is ready
bSend, err = crypt.Encrypt([]byte("ok"), strongKeyForEncryption)
bSend, err = crypt.EncryptAge([]byte("ok"), keyPublic)
if err != nil {
return
}
@ -405,7 +385,7 @@ func PingServer(address string) (err error) {
// ConnectToTCPServer will initiate a new connection
// to the specified address, room with optional time limit
func ConnectToTCPServer(address, password, room string, timelimit ...time.Duration) (c *comm.Comm, banner string, ipaddr string, err error) {
func ConnectToTCPServer(address, password, keyPublicRelay, room string, timelimit ...time.Duration) (c *comm.Comm, banner string, ipaddr string, err error) {
if len(timelimit) > 0 {
c, err = comm.NewConnection(address, timelimit[0])
} else {
@ -415,65 +395,41 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati
return
}
// get PAKE connection with server to establish strong key to transfer info
A, err := pake.InitCurve(weakKey, 0, "siec")
if err != nil {
return
}
err = c.Send(A.Bytes())
if err != nil {
return
}
Bbytes, err := c.Receive()
if err != nil {
return
}
err = A.Update(Bbytes)
if err != nil {
return
}
strongKey, err := A.SessionKey()
if err != nil {
return
}
log.Debugf("strong key: %x", strongKey)
// generate ephermeral key
keyPublic, keyPrivate, err := crypt.NewAge()
strongKeyForEncryption, salt, err := crypt.New(strongKey, nil)
if err != nil {
return
}
// send salt
err = c.Send(salt)
// send epheremal public key + bcrypted password, encrypted using the server's public key
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 10)
if err != nil {
return
}
log.Debug("sending password")
bSend, err := crypt.Encrypt([]byte(password), strongKeyForEncryption)
sendBytesEnc, err := crypt.EncryptAge(append([]byte(keyPublic+"--"), hashedPassword...), keyPublicRelay)
if err != nil {
return
}
err = c.Send(bSend)
err = c.Send(sendBytesEnc)
if err != nil {
return
}
log.Debug("waiting for first ok")
enc, err := c.Receive()
retBytesEnc, err := c.Receive()
if err != nil {
return
}
data, err := crypt.Decrypt(enc, strongKeyForEncryption)
retBytes, err := crypt.DecryptAge(retBytesEnc, keyPrivate)
if err != nil {
return
}
if !strings.Contains(string(data), "|||") {
err = fmt.Errorf("bad response: %s", string(data))
if !strings.Contains(string(retBytes), "|||") {
err = fmt.Errorf("bad response: %s", string(retBytes))
return
}
banner = strings.Split(string(data), "|||")[0]
ipaddr = strings.Split(string(data), "|||")[1]
banner = strings.Split(string(retBytes), "|||")[0]
ipaddr = strings.Split(string(retBytes), "|||")[1]
log.Debug("sending room")
bSend, err = crypt.Encrypt([]byte(room), strongKeyForEncryption)
bSend, err := crypt.EncryptAge([]byte(room), keyPublicRelay)
if err != nil {
return
}
@ -482,11 +438,11 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati
return
}
log.Debug("waiting for room confirmation")
enc, err = c.Receive()
enc, err := c.Receive()
if err != nil {
return
}
data, err = crypt.Decrypt(enc, strongKeyForEncryption)
data, err := crypt.DecryptAge(enc, keyPrivate)
if err != nil {
return
}

View File

@ -2,44 +2,52 @@ package tcp
import (
"bytes"
"fmt"
"testing"
"time"
"github.com/schollz/croc/v9/src/crypt"
log "github.com/schollz/logger"
"github.com/stretchr/testify/assert"
)
func BenchmarkConnection(b *testing.B) {
log.SetLevel("trace")
go Run("debug", "8283", "pass123", "8284")
time.Sleep(100 * time.Millisecond)
b.ResetTimer()
for i := 0; i < b.N; i++ {
c, _, _, _ := ConnectToTCPServer("localhost:8283", "pass123", fmt.Sprintf("testroom%d", i), 1*time.Minute)
c.Close()
}
}
// func BenchmarkConnection(b *testing.B) {
// log.SetLevel("trace")
// go Run("debug", "8283", "pass123", "8284")
// time.Sleep(100 * time.Millisecond)
// b.ResetTimer()
// for i := 0; i < b.N; i++ {
// c, _, _, _ := ConnectToTCPServer("localhost:8283", "pass123", fmt.Sprintf("testroom%d", i), 1*time.Minute)
// c.Close()
// }
// }
func TestTCP(t *testing.T) {
log.SetLevel("error")
timeToRoomDeletion = 100 * time.Millisecond
go Run("debug", "8281", "pass123", "8282")
keyPublic, keyPrivate, err := crypt.NewAge()
if err != nil {
panic(err)
}
go Run("debug", "8281", "pass123", keyPublic, keyPrivate, "8282")
time.Sleep(100 * time.Millisecond)
err := PingServer("localhost:8281")
err = PingServer("localhost:8281")
assert.Nil(t, err)
err = PingServer("localhost:8333")
assert.NotNil(t, err)
time.Sleep(100 * time.Millisecond)
c1, banner, _, err := ConnectToTCPServer("localhost:8281", "pass123", "testRoom", 1*time.Minute)
c1, banner, _, err := ConnectToTCPServer("localhost:8281", "pass123", keyPublic, "testRoom", 1*time.Minute)
assert.Equal(t, banner, "8282")
assert.Nil(t, err)
c2, _, _, err := ConnectToTCPServer("localhost:8281", "pass123", "testRoom")
c2, _, _, err := ConnectToTCPServer("localhost:8281", "pass123", keyPublic, "testRoom")
assert.Nil(t, err)
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", "testRoom")
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", keyPublic, "testRoom")
assert.NotNil(t, err)
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", "testRoom", 1*time.Nanosecond)
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", keyPublic, "testRoom", 1*time.Nanosecond)
assert.NotNil(t, err)
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", keyPublic+"askldjfklsajdf", "testRoom2")
assert.NotNil(t, err)
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass125", keyPublic, "testRoom3")
assert.NotNil(t, err)
// try sending data