Skip to content

Commit

Permalink
Add keyring so we can overwrite the signing request hash
Browse files Browse the repository at this point in the history
  • Loading branch information
tlbdk committed Nov 15, 2019
1 parent 7c32206 commit e61e094
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.vscode
auth-wrapper
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.vscode
/auth-wrapper
1 change: 1 addition & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func runWithSSHAgent(command string, args []string, sshKeyPath string, sshKeyPas
}
}

// TODO: Change interface so we give a Agent instead
sshAuthSock, err := ssh.SetupAgent(privateKey)
if err != nil {
return 1, fmt.Errorf("Failed to start ssh agent server: %v", err)
Expand Down
107 changes: 107 additions & 0 deletions ssh/kms-keyring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package ssh

import (
"bytes"
"crypto/rand"
"errors"
"fmt"
"sync"

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

type kmsKeyring struct {
mu sync.Mutex
signer *KMSSigner

locked bool
passphrase []byte
}

var errLocked = errors.New("agent: locked")

// NewKMSKeyring returns an Agent that holds keys in memory. It is safe
// for concurrent use by multiple goroutines.
func NewKMSKeyring(signer *KMSSigner) agent.Agent {
// TODO: Create keys and use signer key
return &kmsKeyring{signer: signer}
}

func (r *kmsKeyring) RemoveAll() error {
return fmt.Errorf("removing keys not allowed")
}

func (r *kmsKeyring) Remove(_ ssh.PublicKey) error {
return fmt.Errorf("removing keys not allowed")
}

func (r *kmsKeyring) Lock(_ []byte) error {
return fmt.Errorf("locking agent not allowed")
}

func (r *kmsKeyring) Unlock(_ []byte) error {
return fmt.Errorf("unlocking agent not allowed")
}

func (r *kmsKeyring) Add(_ agent.AddedKey) error {
return fmt.Errorf("adding new keys not allowed")
}

// Signers returns signers for all the known keys.
func (r *kmsKeyring) Signers() ([]ssh.Signer, error) {
return nil, fmt.Errorf("Signers not allowed")
}

// The keyring does not support any extensions
func (r *kmsKeyring) Extension(extensionType string, contents []byte) ([]byte, error) {
return nil, agent.ErrExtensionUnsupported
}

// List returns the identities known to the agent.
func (r *kmsKeyring) List() ([]*agent.Key, error) {
r.mu.Lock()
defer r.mu.Unlock()

var ids []*agent.Key

pub := r.signer.PublicKey()
ids = append(ids, &agent.Key{
Format: pub.Type(),
Blob: pub.Marshal(),
Comment: "my kms key"})

return ids, nil
}

// Sign returns a signature for the data.
func (r *kmsKeyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
return r.SignWithFlags(key, data, 0)
}

func (r *kmsKeyring) SignWithFlags(key ssh.PublicKey, data []byte, flags agent.SignatureFlags) (*ssh.Signature, error) {
r.mu.Lock()
defer r.mu.Unlock()

wanted := key.Marshal()

if bytes.Equal(r.signer.PublicKey().Marshal(), wanted) {
if flags == 0 {
return r.signer.SignWithSSHAlgorithm(rand.Reader, data, ssh.SigAlgoRSASHA2512)
} else {
var algorithm string
switch flags {
case agent.SignatureFlagRsaSha256:
algorithm = ssh.SigAlgoRSASHA2256
case agent.SignatureFlagRsaSha512:
algorithm = ssh.SigAlgoRSASHA2512
default:
return nil, fmt.Errorf("agent: unsupported signature flags: %d", flags)
}
return r.signer.SignWithSSHAlgorithm(rand.Reader, data, algorithm)

}

}
return nil, errors.New("not found")
}
67 changes: 61 additions & 6 deletions ssh/kms.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@ import (
"log"

cloudkms "cloud.google.com/go/kms/apiv1"
"golang.org/x/crypto/ssh"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
)

// KMSSigner is a key
type KMSSigner struct {
ctx context.Context
client *cloudkms.KeyManagementClient
keyName string
publicKey crypto.PublicKey
digest crypto.Hash
ctx context.Context
client *cloudkms.KeyManagementClient
keyName string
publicKey crypto.PublicKey
sshPublicKey ssh.PublicKey
digest crypto.Hash
}

var hashLookup = map[crypto.Hash]string{
Expand Down Expand Up @@ -88,11 +90,59 @@ func (kmss *KMSSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpt
return res.Signature, nil
}

// TODO: Move to own file as Sign does not match ssh.signer.Sign

// SignWithSSHAlgorithm signs with algorithm
func (kmss *KMSSigner) SignWithSSHAlgorithm(rand io.Reader, data []byte, algorithm string) (*ssh.Signature, error) {
var hashFunc crypto.Hash

if _, ok := kmss.publicKey.(*rsa.PublicKey); ok {
// RSA keys support a few hash functions determined by the requested signature algorithm
switch algorithm {
case "", ssh.SigAlgoRSA:
algorithm = ssh.SigAlgoRSA
hashFunc = crypto.SHA1
case ssh.SigAlgoRSASHA2256:
hashFunc = crypto.SHA256
case ssh.SigAlgoRSASHA2512:
hashFunc = crypto.SHA512
default:
return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
}
} else {
return nil, fmt.Errorf("ssh: unsupported key type %T", kmss.publicKey)
}

var digest []byte
if hashFunc != 0 {
h := hashFunc.New()
h.Write(data)
digest = h.Sum(nil)
} else {
digest = data
}

signature, err := kmss.Sign(rand, digest, hashFunc)
if err != nil {
return nil, err
}

return &ssh.Signature{
Format: algorithm,
Blob: signature,
}, nil
}

// Public fetches public key
func (kmss *KMSSigner) Public() crypto.PublicKey {
return kmss.publicKey
}

// PublicKey fetches public key in ssh format
func (kmss *KMSSigner) PublicKey() ssh.PublicKey {
return kmss.sshPublicKey
}

// NewKMSSigner creates a new instance
func NewKMSSigner(keyName string) (signer crypto.Signer, err error) {
// Create the KMS client.
Expand All @@ -114,6 +164,11 @@ func NewKMSSigner(keyName string) (signer crypto.Signer, err error) {
return nil, fmt.Errorf("x509.ParsePKIXPublicKey: %+v", err)
}

sshPublicKey, err := ssh.NewPublicKey(abstractKey)
if err != nil {
return nil, fmt.Errorf("ssh.ParsePublicKey: %+v", err)
}

var publicKey crypto.PublicKey
var digestType crypto.Hash
switch abstractKey.(type) {
Expand Down Expand Up @@ -147,5 +202,5 @@ func NewKMSSigner(keyName string) (signer crypto.Signer, err error) {
return nil, fmt.Errorf("key %q is not supported format", keyName)
}

return &KMSSigner{keyName: keyName, ctx: ctx, client: client, publicKey: publicKey, digest: digestType}, nil
return &KMSSigner{keyName: keyName, ctx: ctx, client: client, publicKey: publicKey, digest: digestType, sshPublicKey: sshPublicKey}, nil
}
20 changes: 15 additions & 5 deletions ssh/sshagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,23 @@ import (
"golang.org/x/crypto/ssh/agent"
)

type readOnlyAgent struct {
agent.Agent
}

// SetupAgent start an SSH Agent server and loads the given private key
func SetupAgent(privateKey interface{}) (sshAuthSock string, error error) {
sshAgent := agent.NewKeyring()

err := sshAgent.Add(agent.AddedKey{PrivateKey: privateKey, Comment: "my private key"})
if err != nil {
return "", err
// TODO: Move outside function
var sshAgent agent.Agent
switch privateKey.(type) {
case *KMSSigner:
sshAgent = NewKMSKeyring(privateKey.(*KMSSigner))
default:
sshAgent = agent.NewKeyring()
err := sshAgent.Add(agent.AddedKey{PrivateKey: privateKey, Comment: "my private key"})
if err != nil {
return "", err
}
}

// Generate random filename
Expand Down

0 comments on commit e61e094

Please sign in to comment.