use relay public key and password to connect

This commit is contained in:
Zack Scholl 2021-04-23 16:59:28 -07:00
parent 675f896d61
commit fffdf39bea
7 changed files with 169 additions and 66 deletions

2
go.sum
View File

@ -15,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=

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

@ -8,7 +8,10 @@ import (
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
"log"
"math/big"
"strings"
"filippo.io/age"
"golang.org/x/crypto/argon2"
@ -76,6 +79,64 @@ 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 {

View File

@ -2,8 +2,10 @@ package crypt
import (
"fmt"
"os"
"testing"
"github.com/schollz/croc/v9/src/utils"
"github.com/stretchr/testify/assert"
)
@ -115,3 +117,19 @@ func TestEncryptionAge(t *testing.T) {
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

@ -90,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)