Skip to content

Commit

Permalink
Rewrite Key to use Enclave (BROKEN)
Browse files Browse the repository at this point in the history
  • Loading branch information
awnumar committed Jul 16, 2019
1 parent d5970bc commit aac96dc
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 222 deletions.
17 changes: 7 additions & 10 deletions cmd/mole/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"fmt"
"os"
"syscall"

"github.com/awnumar/memguard"
"github.com/davrodpin/mole/cli"
Expand Down Expand Up @@ -233,7 +232,13 @@ func start(app *cli.App) error {
"options": app.String(),
}).Debug("cli options")

s, err := tunnel.NewServer(app.Server.User, app.Server.Address(), app.Key)
fmt.Printf("Enter password for private key (leave blank for none): ")
p, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return err
}

s, err := tunnel.NewServer(app.Server.User, app.Server.Address(), app.Key, p)
if err != nil {
log.Errorf("error processing server options: %v\n", err)

Expand All @@ -242,14 +247,6 @@ func start(app *cli.App) error {

s.Insecure = app.InsecureMode

s.Key.HandlePassphrase(func() ([]byte, error) {
fmt.Printf("The key provided is secured by a password. Please provide it below:\n")
fmt.Printf("Password: ")
p, err := terminal.ReadPassword(int(syscall.Stdin))
fmt.Printf("\n")
return p, err
})

log.Debugf("server: %s", s)

local := make([]string, len(app.Local))
Expand Down
2 changes: 1 addition & 1 deletion tunnel/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func Example() {

// Initialize the SSH Server configuration providing all values so
// tunnel.NewServer will not try to lookup any value using $HOME/.ssh/config
server, err := tunnel.NewServer("user", "172.17.0.20:2222", "/home/user/.ssh/key")
server, err := tunnel.NewServer("user", "172.17.0.20:2222", "/home/user/.ssh/key", []byte("password"))
if err != nil {
log.Fatalf("error processing server options: %v\n", err)
}
Expand Down
96 changes: 17 additions & 79 deletions tunnel/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,110 +10,48 @@ import (
"golang.org/x/crypto/ssh"
)

// PemKeyParser translates pem keys to a signature signer.
type PemKeyParser interface {
// Parse returns a key signer to create signatures that verify against a
// public key.
Parse() (*ssh.Signer, error)
}

// PemKey holds data related to PEM keys
// PemKey is the private key sealed inside an encrypted container.
type PemKey struct {
// Data holds the data for a PEM private key
Data []byte

// passphrase used to parse a PEM encoded private key
passphrase *memguard.LockedBuffer
*memguard.Enclave
}

func NewPemKey(keyPath, passphrase string) (*PemKey, error) {
// NewPemKey loads a private key from a file and seals it into an encrypted container in memory.
func NewPemKey(keyPath string, passphrase []byte) (*PemKey, error) {
data, err := ioutil.ReadFile(keyPath)
if err != nil {
return nil, err
}

k := &PemKey{Data: data}

if passphrase != "" {
k.updatePassphrase([]byte(passphrase))
}

return k, nil
}

// IsEncrypted inspects the key data block to tell if it is whether encrypted
// or not.
func (k PemKey) IsEncrypted() (bool, error) {
p, err := decodePemKey(k.Data)
if err != nil {
return false, err
}

return x509.IsEncryptedPEMBlock(p), nil
}

// Parse translates a pem key to a signer to create signatures that verify
// against a public key.
func (k *PemKey) Parse() (ssh.Signer, error) {
var signer ssh.Signer

enc, err := k.IsEncrypted()
keyBlock, err := decodePemKey(data)
if err != nil {
return nil, err
}

if enc {
if k.passphrase == nil {
return nil, fmt.Errorf("can't read protected ssh key because no passphrase was provided")
}

signer, err = ssh.ParsePrivateKeyWithPassphrase(k.Data, k.passphrase.Bytes())
if err != nil {
return nil, err
}
} else {
signer, err = ssh.ParsePrivateKey(k.Data)
if x509.IsEncryptedPEMBlock(keyBlock) {
key, err := x509.DecryptPEMBlock(keyBlock, passphrase)
if err != nil {
return nil, err
}
return &PemKey{memguard.NewEnclave(key)}, nil
}

return signer, nil
return &PemKey{memguard.NewEnclave(keyBlock.Bytes)}, nil
}

// HandlePassphrase securely records a passphrase given by a callback to the
// memory.
func (k *PemKey) HandlePassphrase(handler func() ([]byte, error)) error {
enc, err := k.IsEncrypted()
// Signer creates an ssh.Signer object from the sealed private key and returns it.
func (k *PemKey) Signer() (ssh.Signer, error) {
key, err := k.Open()
if err != nil {
return fmt.Errorf("error while reading ssh key: %v", err)
}

if !enc {
return nil
return nil, err
}
defer key.Destroy()

pp, err := handler()
signer, err := ssh.ParsePrivateKey(key.Bytes())
if err != nil {
return fmt.Errorf("error while reading password: %v", err)
}

k.updatePassphrase(pp)

return nil
}

func (k *PemKey) updatePassphrase(pp []byte) {
if k.passphrase != nil {
k.passphrase.Destroy()
}

if len(pp) < 1 {
k.passphrase = nil
return
return nil, err
}

k.passphrase = memguard.NewBufferFromBytes(pp)
return signer, nil
}

func decodePemKey(data []byte) (*pem.Block, error) {
Expand Down
89 changes: 14 additions & 75 deletions tunnel/key_test.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
package tunnel

import (
"io/ioutil"
"crypto/x509"
"fmt"
"testing"
)

func passwordHandler(password string) func() ([]byte, error) {
return func() ([]byte, error) { return []byte(password), nil }
}

func TestPemKey(t *testing.T) {
tests := []struct {
keyPath string
encrypted bool
passphrase string
passphrase []byte
}{
{
"testdata/dotssh/id_rsa",
false,
"",
[]byte{},
},
{
"testdata/dotssh/id_rsa_encrypted",
true,
"mole",
[]byte("mole"),
},
}

Expand All @@ -33,82 +30,24 @@ func TestPemKey(t *testing.T) {
t.Errorf("test failed for key %s: %v", test.keyPath, err)
}

enc, err := key.IsEncrypted()
opened, err := key.Open()
if err != nil {
t.Errorf("test failed for key %s: %v", test.keyPath, err)
}

if test.encrypted != enc {
t.Errorf("test for encryption check on %s failed : expected: %t, result: %t", test.keyPath, test.encrypted, enc)
t.Error(err)
}

_, err = key.Parse()
fmt.Println(opened.Bytes())
block, err := decodePemKey(opened.Bytes())
if err != nil {
t.Errorf("test failed for key %s: %v", test.keyPath, err)
t.Error(err)
}
}
}

func TestHandlePassword(t *testing.T) {
tests := []struct {
keyPath string
passphrase string
}{
{
"testdata/dotssh/id_rsa",
"",
},
{
"testdata/dotssh/id_rsa_encrypted",
"mole",
},
}
opened.Destroy()

for _, test := range tests {
data, err := ioutil.ReadFile(test.keyPath)
if err != nil {
t.Errorf("can't read key file %s: %v", test.keyPath, err)
if x509.IsEncryptedPEMBlock(block) != false {
t.Error("key should be decrypted")
}

key := &PemKey{Data: data}

key.HandlePassphrase(func() ([]byte, error) {
return []byte(test.passphrase), nil
})

enc, err := key.IsEncrypted()
_, err = key.Signer()
if err != nil {
t.Errorf("test failed for key %s: %v", test.keyPath, err)
}

if enc {
if test.passphrase != key.passphrase.String() {
t.Errorf("passphrases don't match for key %s: expected: %s, result: %s", test.keyPath, test.passphrase, key.passphrase.String())
}
} else {
if nil != key.passphrase {
t.Errorf("passphared is suppoed to be nil for %s", test.keyPath)
}
}
}
}

func TestUpdatePassphrase(t *testing.T) {
key, _ := NewPemKey("testdata/dotssh/id_rsa_encrypted", "mole")

key.updatePassphrase([]byte("hello"))
if !key.passphrase.EqualTo([]byte("hello")) {
t.Error("update failed")
}

key = new(PemKey) // nil
key.updatePassphrase([]byte("bye"))
if !key.passphrase.EqualTo([]byte("bye")) {
t.Error("update failed")
}

key.updatePassphrase([]byte(""))
if key.passphrase != nil {
t.Error("expected nil passphrase")
}
}
Loading

0 comments on commit aac96dc

Please sign in to comment.