Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions client/client_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package chclient

import (
"crypto/ecdsa"
"crypto/elliptic"
"log"
"net/http"
Expand Down Expand Up @@ -54,7 +53,7 @@ func TestFallbackLegacyFingerprint(t *testing.T) {
t.Fatal(err)
}
r := ccrypto.NewDetermRand([]byte("test123"))
priv, err := ecdsa.GenerateKey(elliptic.P256(), r)
priv, err := ccrypto.GenerateKeyGo119(elliptic.P256(), r)
if err != nil {
t.Fatal(err)
}
Expand All @@ -77,7 +76,7 @@ func TestVerifyLegacyFingerprint(t *testing.T) {
t.Fatal(err)
}
r := ccrypto.NewDetermRand([]byte("test123"))
priv, err := ecdsa.GenerateKey(elliptic.P256(), r)
priv, err := ccrypto.GenerateKeyGo119(elliptic.P256(), r)
if err != nil {
t.Fatal(err)
}
Expand All @@ -100,7 +99,7 @@ func TestVerifyFingerprint(t *testing.T) {
t.Fatal(err)
}
r := ccrypto.NewDetermRand([]byte("test123"))
priv, err := ecdsa.GenerateKey(elliptic.P256(), r)
priv, err := ccrypto.GenerateKeyGo119(elliptic.P256(), r)
if err != nil {
t.Fatal(err)
}
Expand Down
23 changes: 22 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
chclient "github.com/jpillora/chisel/client"
chserver "github.com/jpillora/chisel/server"
chshare "github.com/jpillora/chisel/share"
"github.com/jpillora/chisel/share/ccrypto"
"github.com/jpillora/chisel/share/cos"
)

Expand Down Expand Up @@ -103,12 +104,18 @@ var serverHelp = `
--port, -p, Defines the HTTP listening port (defaults to the environment
variable PORT and fallsback to port 8080).

--key, An optional string to seed the generation of a ECDSA public
--key, (deprecated) An optional string to seed the generation of a ECDSA public
and private key pair. All communications will be secured using this
key pair. Share the subsequent fingerprint with clients to enable detection
of man-in-the-middle attacks (defaults to the CHISEL_KEY environment
variable, otherwise a new key is generate each run).

--keyfile, An optional path to a PEM-encoded SSH private key. When
this flag is set, the --key option is omitted, and the provided private key
is used to secure all communications.

--keygen, Generates a PEM-encoded SSH private key file

--authfile, An optional path to a users.json file. This file should
be an object with users defined like:
{
Expand Down Expand Up @@ -170,6 +177,7 @@ func server(args []string) {

config := &chserver.Config{}
flags.StringVar(&config.KeySeed, "key", "", "")
flags.StringVar(&config.KeyFile, "keyfile", "", "")
flags.StringVar(&config.AuthFile, "authfile", "", "")
flags.StringVar(&config.Auth, "auth", "", "")
flags.DurationVar(&config.KeepAlive, "keepalive", 25*time.Second, "")
Expand All @@ -187,13 +195,26 @@ func server(args []string) {
port := flags.String("port", "", "")
pid := flags.Bool("pid", false, "")
verbose := flags.Bool("v", false, "")
keyGen := flags.String("keygen", "", "")

flags.Usage = func() {
fmt.Print(serverHelp)
os.Exit(0)
}
flags.Parse(args)

if *keyGen != "" {
if err := ccrypto.GenerateKeyFile(*keyGen, config.KeySeed); err != nil {
log.Fatal(err)
}
return
}

if config.KeySeed != "" {
log.Print("Option `--key` is deprecated and will be removed.")
log.Print("Please use `chisel server --keygen /file/path`, followed by `chisel server --keyfile /file/path` to specify the SSH private key")
}

if *host == "" {
*host = os.Getenv("HOST")
}
Expand Down
23 changes: 19 additions & 4 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package chserver
import (
"context"
"errors"
"fmt"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"regexp"
"time"

Expand All @@ -30,6 +32,7 @@ type Config struct {
Reverse bool
KeepAlive time.Duration
TLS TLSConfig
KeyFile string
}

// Server respresent a chisel service
Expand Down Expand Up @@ -73,11 +76,23 @@ func NewServer(c *Config) (*Server, error) {
server.users.AddUser(u)
}
}
//generate private key (optionally using seed)
key, err := ccrypto.GenerateKey(c.KeySeed)
if err != nil {
log.Fatal("Failed to generate key")

var key []byte
var err error
if c.KeyFile != "" {
//read private key from the file specified by the --keyfile flag
key, err = os.ReadFile(c.KeyFile)
if err != nil {
log.Fatal(fmt.Sprintf("Failed to read the SSH private key %s", c.KeyFile))
}
} else {
//generate private key (optionally using seed)
key, err = ccrypto.GenerateKey(c.KeySeed)
if err != nil {
log.Fatal("Failed to generate key")
}
}

//convert into ssh.PrivateKey
private, err := ssh.ParsePrivateKey(key)
if err != nil {
Expand Down
42 changes: 42 additions & 0 deletions share/ccrypto/generate_key_go119.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ccrypto

import (
"crypto/ecdsa"
"crypto/elliptic"
"io"
"math/big"
)

var one = new(big.Int).SetInt64(1)

// This function is copied from ecdsa.GenerateKey() of Go 1.19
func GenerateKeyGo119(c elliptic.Curve, rand io.Reader) (*ecdsa.PrivateKey, error) {
k, err := randFieldElement(c, rand)
if err != nil {
return nil, err
}

priv := new(ecdsa.PrivateKey)
priv.PublicKey.Curve = c
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
return priv, nil
}

// This function is copied from Go 1.19
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
params := c.Params()
// Note that for P-521 this will actually be 63 bits more than the order, as
// division rounds down, but the extra bit is inconsequential.
b := make([]byte, params.N.BitLen()/8+8)
_, err = io.ReadFull(rand, b)
if err != nil {
return
}

k = new(big.Int).SetBytes(b)
n := new(big.Int).Sub(params.N, one)
k.Mod(k, n)
k.Add(k, one)
return
}
26 changes: 23 additions & 3 deletions share/ccrypto/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,27 @@ import (
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"

"golang.org/x/crypto/ssh"
)

//GenerateKey for use as an SSH private key
// GenerateKey for use as an SSH private key
func GenerateKey(seed string) ([]byte, error) {
var err error
var priv *ecdsa.PrivateKey

r := rand.Reader
if seed != "" {
r = NewDetermRand([]byte(seed))
}
priv, err := ecdsa.GenerateKey(elliptic.P256(), r)

if seed == "" {
priv, err = ecdsa.GenerateKey(elliptic.P256(), r)
} else {
priv, err = GenerateKeyGo119(elliptic.P256(), r)
}

if err != nil {
return nil, err
}
Expand All @@ -30,7 +40,17 @@ func GenerateKey(seed string) ([]byte, error) {
return pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b}), nil
}

//FingerprintKey calculates the SHA256 hash of an SSH public key
// GenerateKeyFile generates an SSH private key file
func GenerateKeyFile(keyFilePath, seed string) error {
keyBytes, err := GenerateKey(seed)
if err != nil {
return err
}

return ioutil.WriteFile(keyFilePath, keyBytes, 0600)
}

// FingerprintKey calculates the SHA256 hash of an SSH public key
func FingerprintKey(k ssh.PublicKey) string {
bytes := sha256.Sum256(k.Marshal())
return base64.StdEncoding.EncodeToString(bytes[:])
Expand Down