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 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI= 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/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 h1:j/cUPa15YvXv7abJlM+kdJIycbBMpmO7WqhPl4YB76I=
github.com/kalafut/imohash v1.0.2/go.mod h1:PjHBF0vpo1q7zMqiTn0qwSTQU2wDn5QIe8S8sFQuZS8= github.com/kalafut/imohash v1.0.2/go.mod h1:PjHBF0vpo1q7zMqiTn0qwSTQU2wDn5QIe8S8sFQuZS8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 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/cli/v2"
"github.com/schollz/croc/v9/src/comm" "github.com/schollz/croc/v9/src/comm"
"github.com/schollz/croc/v9/src/croc" "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/models"
"github.com/schollz/croc/v9/src/tcp" "github.com/schollz/croc/v9/src/tcp"
"github.com/schollz/croc/v9/src/utils" "github.com/schollz/croc/v9/src/utils"
@ -75,6 +76,7 @@ func Run() (err error) {
}, },
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"}, &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: "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"}},
&cli.StringFlag{Name: "out", Value: ".", Usage: "specify an output folder to receive the file"}, &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"}}, &cli.StringFlag{Name: "socks5", Value: "", Usage: "add a socks5 proxy", EnvVars: []string{"SOCKS5_PROXY"}},
} }
app.EnableBashCompletion = true app.EnableBashCompletion = true
@ -167,15 +170,6 @@ func getConfigFile() string {
return path.Join(configFile, "send.json") 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) { func send(c *cli.Context) (err error) {
setDebugLevel(c) setDebugLevel(c)
comm.Socks5Proxy = c.String("socks5") comm.Socks5Proxy = c.String("socks5")
@ -193,7 +187,8 @@ func send(c *cli.Context) (err error) {
RelayPorts: strings.Split(c.String("ports"), ","), RelayPorts: strings.Split(c.String("ports"), ","),
Ask: c.Bool("ask"), Ask: c.Bool("ask"),
NoMultiplexing: c.Bool("no-multi"), NoMultiplexing: c.Bool("no-multi"),
RelayPassword: determinePass(c), RelayPassword: c.String("relay-pass"),
RelayKeyPublic: c.String("relay-key"),
SendingText: c.String("text") != "", SendingText: c.String("text") != "",
NoCompress: c.Bool("no-compress"), NoCompress: c.Bool("no-compress"),
Overwrite: c.Bool("overwrite"), Overwrite: c.Bool("overwrite"),
@ -226,9 +221,12 @@ func send(c *cli.Context) (err error) {
if !c.IsSet("code") { if !c.IsSet("code") {
crocOptions.SharedSecret = rememberedOptions.SharedSecret crocOptions.SharedSecret = rememberedOptions.SharedSecret
} }
if !c.IsSet("pass") { if !c.IsSet("relay-pass") {
crocOptions.RelayPassword = rememberedOptions.RelayPassword crocOptions.RelayPassword = rememberedOptions.RelayPassword
} }
if !c.IsSet("relay-key") {
crocOptions.RelayKeyPublic = rememberedOptions.RelayKeyPublic
}
} }
var fnames []string var fnames []string
@ -383,20 +381,21 @@ func saveConfig(c *cli.Context, crocOptions croc.Options) {
func receive(c *cli.Context) (err error) { func receive(c *cli.Context) (err error) {
comm.Socks5Proxy = c.String("socks5") comm.Socks5Proxy = c.String("socks5")
crocOptions := croc.Options{ crocOptions := croc.Options{
SharedSecret: c.String("code"), SharedSecret: c.String("code"),
IsSender: false, IsSender: false,
Debug: c.Bool("debug"), Debug: c.Bool("debug"),
NoPrompt: c.Bool("yes"), NoPrompt: c.Bool("yes"),
RelayAddress: c.String("relay"), RelayAddress: c.String("relay"),
RelayAddress6: c.String("relay6"), RelayAddress6: c.String("relay6"),
Stdout: c.Bool("stdout"), Stdout: c.Bool("stdout"),
Ask: c.Bool("ask"), Ask: c.Bool("ask"),
RelayPassword: determinePass(c), RelayPassword: c.String("relay-pass"),
OnlyLocal: c.Bool("local"), RelayKeyPublic: c.String("relay-key"),
IP: c.String("ip"), OnlyLocal: c.Bool("local"),
Overwrite: c.Bool("overwrite"), IP: c.String("ip"),
Curve: c.String("curve"), Overwrite: c.Bool("overwrite"),
HashAlgorithm: "xxhash", Curve: c.String("curve"),
HashAlgorithm: "xxhash",
} }
if crocOptions.RelayAddress != models.DEFAULT_RELAY { if crocOptions.RelayAddress != models.DEFAULT_RELAY {
crocOptions.RelayAddress6 = "" crocOptions.RelayAddress6 = ""
@ -481,23 +480,40 @@ func receive(c *cli.Context) (err error) {
} }
func relay(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" debugString := "info"
if c.Bool("debug") { if c.Bool("debug") {
debugString = "debug" debugString = "debug"
} }
ports := strings.Split(c.String("ports"), ",")
tcpPorts := strings.Join(ports[1:], ",")
for i, port := range ports { for i, port := range ports {
if i == 0 { if i == 0 {
continue continue
} }
go func(portStr string) { go func(portStr string) {
err = tcp.Run(debugString, portStr, determinePass(c)) err = tcp.Run(debugString, portStr, password, keyPublic, keyPrivate)
if err != nil { if err != nil {
panic(err) panic(err)
} }
}(port) }(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 // Options specifies user specific options
type Options struct { type Options struct {
IsSender bool IsSender bool
SharedSecret string SharedSecret string
Debug bool Debug bool
RelayAddress string RelayAddress string
RelayAddress6 string RelayAddress6 string
RelayPorts []string RelayPorts []string
RelayPassword string RelayPassword string
Stdout bool RelayKeyPrivate string
NoPrompt bool RelayKeyPublic string
NoMultiplexing bool LocalRelayPassword string
DisableLocal bool LocalRelayKeyPrivate string
OnlyLocal bool LocalRelayKeyPublic string
IgnoreStdin bool Stdout bool
Ask bool NoPrompt bool
SendingText bool NoMultiplexing bool
NoCompress bool DisableLocal bool
IP string OnlyLocal bool
Overwrite bool IgnoreStdin bool
Curve string Ask bool
HashAlgorithm string SendingText bool
NoCompress bool
IP string
Overwrite bool
Curve string
HashAlgorithm string
} }
// Client holds the state of the croc transfer // Client holds the state of the croc transfer
@ -288,7 +293,7 @@ func (c *Client) setupLocalRelay() {
if c.Options.Debug { if c.Options.Debug {
debugString = "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 { if err != nil {
panic(err) panic(err)
} }
@ -320,7 +325,7 @@ func (c *Client) transferOverLocalRelay(options TransferOptions, errchan chan<-
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
log.Debug("establishing connection") log.Debug("establishing connection")
var banner string 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) log.Debugf("banner: %s", banner)
if err != nil { if err != nil {
err = fmt.Errorf("could not connect to localhost:%s: %w", c.Options.RelayPorts[0], err) 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 { if c.Options.RelayAddress != models.DEFAULT_RELAY {
flags.WriteString("--relay " + c.Options.RelayAddress + " ") flags.WriteString("--relay " + c.Options.RelayAddress + " ")
} }
if c.Options.RelayPassword != models.DEFAULT_PASSPHRASE { if c.Options.RelayPassword != models.DEFAULT_RELAY_PASSWORD || c.Options.RelayKeyPublic != models.DEFAULT_RELAY_KEYPUBLIC {
flags.WriteString("--pass " + c.Options.RelayPassword + " ") 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()) 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 { if c.Options.Ask {
@ -376,6 +382,8 @@ func (c *Client) Send(options TransferOptions) (err error) {
errchan := make(chan error, 1) errchan := make(chan error, 1)
if !c.Options.DisableLocal { 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 // add two things to the error channel
errchan = make(chan error, 2) errchan = make(chan error, 2)
c.setupLocalRelay() c.setupLocalRelay()
@ -405,7 +413,7 @@ func (c *Client) Send(options TransferOptions) (err error) {
log.Debugf("got host '%v' and port '%v'", host, port) log.Debugf("got host '%v' and port '%v'", host, port)
address = net.JoinHostPort(host, port) address = net.JoinHostPort(host, port)
log.Debugf("trying connection to %s", address) 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 { if err == nil {
c.Options.RelayAddress = address c.Options.RelayAddress = address
break break
@ -599,7 +607,7 @@ func (c *Client) Receive() (err error) {
log.Debugf("got host '%v' and port '%v'", host, port) log.Debugf("got host '%v' and port '%v'", host, port)
address = net.JoinHostPort(host, port) address = net.JoinHostPort(host, port)
log.Debugf("trying connection to %s", address) 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 { if err == nil {
c.Options.RelayAddress = address c.Options.RelayAddress = address
break break
@ -652,7 +660,7 @@ func (c *Client) Receive() (err error) {
} }
serverTry := fmt.Sprintf("%s:%s", ip, port) 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 { if errConn != nil {
log.Debugf("could not connect to " + serverTry) log.Debugf("could not connect to " + serverTry)
continue continue
@ -909,6 +917,7 @@ func (c *Client) processMessagePake(m message.Message) (err error) {
c.conn[j+1], _, _, err = tcp.ConnectToTCPServer( c.conn[j+1], _, _, err = tcp.ConnectToTCPServer(
server, server,
c.Options.RelayPassword, c.Options.RelayPassword,
c.Options.RelayKeyPublic,
fmt.Sprintf("%s-%d", utils.SHA256(c.Options.SharedSecret[:5])[:6], j), fmt.Sprintf("%s-%d", utils.SHA256(c.Options.SharedSecret[:5])[:6], j),
) )
if err != nil { if err != nil {

View File

@ -8,7 +8,10 @@ import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"math/big"
"strings"
"filippo.io/age" "filippo.io/age"
"golang.org/x/crypto/argon2" "golang.org/x/crypto/argon2"
@ -76,6 +79,64 @@ func Decrypt(encrypted []byte, key []byte) (plaintext []byte, err error) {
return 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) { func NewAge() (pubkey string, privkey string, err error) {
identity, err := age.GenerateX25519Identity() identity, err := age.GenerateX25519Identity()
if err != nil { if err != nil {

View File

@ -2,8 +2,10 @@ package crypt
import ( import (
"fmt" "fmt"
"os"
"testing" "testing"
"github.com/schollz/croc/v9/src/utils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -115,3 +117,19 @@ func TestEncryptionAge(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, msg, dec) 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 // TCP_BUFFER_SIZE is the maximum packet size
const TCP_BUFFER_SIZE = 1024 * 64 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 ( var (
DEFAULT_RELAY = "croc.schollz.com" DEFAULT_RELAY = "croc.schollz.com"
DEFAULT_RELAY6 = "croc6.schollz.com" DEFAULT_RELAY6 = "croc6.schollz.com"
DEFAULT_PORT = "9009" DEFAULT_PORT = "9009"
DEFAULT_PASSPHRASE = "pass123" DEFAULT_RELAY_PASSWORD = "ZiNO9Y"
DEFAULT_RELAY_KEYPUBLIC = "age10yrxthzjrcr0e59nucg0epgnn0qpjv9rhsxqs90rdn335edgnueqrtdnyh"
) )
func init() { func init() {

View File

@ -90,7 +90,7 @@ func (s *server) start() (err error) {
} }
func (s *server) run() (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) server, err := net.Listen("tcp", ":"+s.port)
if err != nil { if err != nil {
return fmt.Errorf("error listening on %s: %w", s.port, err) return fmt.Errorf("error listening on %s: %w", s.port, err)