diff --git a/cmd/piv-agent/list.go b/cmd/piv-agent/list.go index f19154c..969874b 100644 --- a/cmd/piv-agent/list.go +++ b/cmd/piv-agent/list.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/smlx/piv-agent/internal/pivservice" + "github.com/smlx/piv-agent/internal/keyservice/piv" "go.uber.org/zap" ) @@ -17,7 +17,7 @@ type ListCmd struct { // Run the list command. func (cmd *ListCmd) Run(l *zap.Logger) error { - p := pivservice.New(l) + p := piv.New(l) securityKeys, err := p.SecurityKeys() if err != nil { return fmt.Errorf("couldn't get security keys: %w", err) diff --git a/cmd/piv-agent/serve.go b/cmd/piv-agent/serve.go index e69dc0d..bb875de 100644 --- a/cmd/piv-agent/serve.go +++ b/cmd/piv-agent/serve.go @@ -8,8 +8,8 @@ import ( "time" "github.com/coreos/go-systemd/activation" + "github.com/smlx/piv-agent/internal/keyservice/piv" "github.com/smlx/piv-agent/internal/pinentry" - "github.com/smlx/piv-agent/internal/pivservice" "github.com/smlx/piv-agent/internal/server" "github.com/smlx/piv-agent/internal/ssh" "go.uber.org/zap" @@ -48,7 +48,7 @@ func (flagAgents *agentTypeFlag) AfterApply() error { func (cmd *ServeCmd) Run(log *zap.Logger) error { log.Info("startup", zap.String("version", version), zap.String("build date", date)) - p := pivservice.New(log) + p := piv.New(log) // use systemd socket activation ls, err := activation.Listeners() if err != nil { diff --git a/internal/assuan/assuan_test.go b/internal/assuan/assuan_test.go index 1b1f2bc..9c7e8bf 100644 --- a/internal/assuan/assuan_test.go +++ b/internal/assuan/assuan_test.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto" "crypto/ecdsa" + "encoding/hex" "fmt" "io" "math/big" @@ -13,7 +14,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/golang/mock/gomock" "github.com/smlx/piv-agent/internal/assuan" - "github.com/smlx/piv-agent/internal/gpg" + "github.com/smlx/piv-agent/internal/keyservice/gpg" "github.com/smlx/piv-agent/internal/mock" "github.com/smlx/piv-agent/internal/securitykey" "go.uber.org/zap" @@ -172,11 +173,13 @@ func TestSign(t *testing.T) { func TestKeyinfo(t *testing.T) { var testCases = map[string]struct { keyPath string + keyGrip string input []string expect []string }{ "keyinfo": { keyPath: "testdata/C54A8868468BC138.asc", + keyGrip: "38F053358EFD6C923D08EE4FC4CEB208CBCDF73C", input: []string{ "RESET\n", "KEYINFO 38F053358EFD6C923D08EE4FC4CEB208CBCDF73C\n", @@ -196,7 +199,7 @@ func TestKeyinfo(t *testing.T) { if err != nil { tt.Fatal(err) } - keygrip, err := gpg.KeygripECDSA(pubKey) + keygrip, err := hex.DecodeString(tc.keyGrip) if err != nil { tt.Fatal(err) } @@ -331,7 +334,7 @@ func TestDecryptRSAKeyfile(t *testing.T) { if err != nil { tt.Fatal(err) } - keyfileService, err := gpg.NewKeyfileService(log, mockPES, tc.keyPath) + keyfileService, err := gpg.New(log, mockPES, tc.keyPath) if err != nil { tt.Fatal(err) } @@ -429,7 +432,7 @@ func TestSignRSAKeyfile(t *testing.T) { if err != nil { tt.Fatal(err) } - keyfileService, err := gpg.NewKeyfileService(log, mockPES, tc.keyPath) + keyfileService, err := gpg.New(log, mockPES, tc.keyPath) if err != nil { tt.Fatal(err) } diff --git a/internal/assuan/sign.go b/internal/assuan/sign.go index cee44b8..629099d 100644 --- a/internal/assuan/sign.go +++ b/internal/assuan/sign.go @@ -2,10 +2,10 @@ package assuan import ( "crypto/rand" + "crypto/rsa" "fmt" "math/big" - "github.com/smlx/piv-agent/internal/gpg" "github.com/smlx/piv-agent/internal/notify" "golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/cryptobyte/asn1" @@ -15,8 +15,8 @@ import ( // "hashAlgo" hash algorithm. It then encodes the response into an s-expression // and returns it as a byte slice. func (a *Assuan) sign() ([]byte, error) { - switch a.signer.(type) { - case *gpg.RSAKey: + switch a.signer.Public().(type) { + case *rsa.PublicKey: return a.signRSA() default: // default also handles mock signers in the test suite diff --git a/internal/gpg/keyfile.go b/internal/keyservice/gpg/keyfile.go similarity index 100% rename from internal/gpg/keyfile.go rename to internal/keyservice/gpg/keyfile.go diff --git a/internal/gpg/keygrip.go b/internal/keyservice/gpg/keygrip.go similarity index 100% rename from internal/gpg/keygrip.go rename to internal/keyservice/gpg/keygrip.go diff --git a/internal/gpg/keygrip_test.go b/internal/keyservice/gpg/keygrip_test.go similarity index 97% rename from internal/gpg/keygrip_test.go rename to internal/keyservice/gpg/keygrip_test.go index 0a46e6c..4837fe7 100644 --- a/internal/gpg/keygrip_test.go +++ b/internal/keyservice/gpg/keygrip_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - "github.com/smlx/piv-agent/internal/gpg" + "github.com/smlx/piv-agent/internal/keyservice/gpg" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/armor" "golang.org/x/crypto/openpgp/packet" diff --git a/internal/gpg/keyservice.go b/internal/keyservice/gpg/keyservice.go similarity index 79% rename from internal/gpg/keyservice.go rename to internal/keyservice/gpg/keyservice.go index f1a8581..efdb1bd 100644 --- a/internal/gpg/keyservice.go +++ b/internal/keyservice/gpg/keyservice.go @@ -1,6 +1,6 @@ package gpg -//go:generate mockgen -source=keyservice.go -destination=../mock/mock_keyservice.go -package=mock +//go:generate mockgen -source=keyservice.go -destination=../../mock/mock_keyservice.go -package=mock import ( "bytes" @@ -17,9 +17,9 @@ type PINEntryService interface { GetPGPPassphrase(string) ([]byte, error) } -// KeyfileService implements an interface for getting cryptographic keys from +// KeyService implements an interface for getting cryptographic keys from // keyfiles on disk. -type KeyfileService struct { +type KeyService struct { // cache passphrases used for decryption passphrases [][]byte privKeys []*packet.PrivateKey @@ -27,15 +27,15 @@ type KeyfileService struct { pinentry PINEntryService } -// NewKeyfileService returns a keyservice initialised with keys found at path. +// New returns a keyservice initialised with keys found at path. // Path can be a file or directory. -func NewKeyfileService(l *zap.Logger, pe PINEntryService, - path string) (*KeyfileService, error) { +func New(l *zap.Logger, pe PINEntryService, + path string) (*KeyService, error) { p, err := keyfilePrivateKeys(path) if err != nil { return nil, err } - return &KeyfileService{ + return &KeyService{ privKeys: p, log: l, pinentry: pe, @@ -43,13 +43,13 @@ func NewKeyfileService(l *zap.Logger, pe PINEntryService, } // Name returns the name of the keyservice. -func (g *KeyfileService) Name() string { +func (*KeyService) Name() string { return "GPG Keyfile" } // HaveKey takes a list of keygrips, and returns a boolean indicating if any of // the given keygrips were found, the found keygrip, and an error, if any. -func (g *KeyfileService) HaveKey(keygrips [][]byte) (bool, []byte, error) { +func (g *KeyService) HaveKey(keygrips [][]byte) (bool, []byte, error) { for _, kg := range keygrips { key, err := g.getKey(kg) if err != nil { @@ -64,7 +64,7 @@ func (g *KeyfileService) HaveKey(keygrips [][]byte) (bool, []byte, error) { // getKey returns a matching private RSA key if the keygrip matches. If a key // is returned err will be nil. If no key is found, both values may be nil. -func (g *KeyfileService) getKey(keygrip []byte) (*rsa.PrivateKey, error) { +func (g *KeyService) getKey(keygrip []byte) (*rsa.PrivateKey, error) { var pass []byte var err error for _, k := range g.privKeys { @@ -113,7 +113,7 @@ func (g *KeyfileService) getKey(keygrip []byte) (*rsa.PrivateKey, error) { } // GetSigner returns a crypto.Signer associated with the given keygrip. -func (g *KeyfileService) GetSigner(keygrip []byte) (crypto.Signer, error) { +func (g *KeyService) GetSigner(keygrip []byte) (crypto.Signer, error) { rsaPrivKey, err := g.getKey(keygrip) if err != nil { return nil, fmt.Errorf("couldn't getKey: %v", err) @@ -122,7 +122,7 @@ func (g *KeyfileService) GetSigner(keygrip []byte) (crypto.Signer, error) { } // GetDecrypter returns a crypto.Decrypter associated with the given keygrip. -func (g *KeyfileService) GetDecrypter(keygrip []byte) (crypto.Decrypter, error) { +func (g *KeyService) GetDecrypter(keygrip []byte) (crypto.Decrypter, error) { rsaPrivKey, err := g.getKey(keygrip) if err != nil { return nil, fmt.Errorf("couldn't getKey: %v", err) diff --git a/internal/gpg/keyservice_test.go b/internal/keyservice/gpg/keyservice_test.go similarity index 92% rename from internal/gpg/keyservice_test.go rename to internal/keyservice/gpg/keyservice_test.go index 1762f2f..f5739e9 100644 --- a/internal/gpg/keyservice_test.go +++ b/internal/keyservice/gpg/keyservice_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/smlx/piv-agent/internal/gpg" + "github.com/smlx/piv-agent/internal/keyservice/gpg" "github.com/smlx/piv-agent/internal/mock" "go.uber.org/zap" ) @@ -47,7 +47,7 @@ func TestGetSigner(t *testing.T) { mockPES.EXPECT().GetPGPPassphrase(gomock.Any()). Return([]byte("trustno1"), nil) } - ks, err := gpg.NewKeyfileService(log, mockPES, tc.path) + ks, err := gpg.New(log, mockPES, tc.path) if err != nil { tt.Fatal(err) } diff --git a/internal/gpg/key.go b/internal/keyservice/gpg/rsakey.go similarity index 100% rename from internal/gpg/key.go rename to internal/keyservice/gpg/rsakey.go diff --git a/internal/gpg/testdata/key1.asc b/internal/keyservice/gpg/testdata/key1.asc similarity index 100% rename from internal/gpg/testdata/key1.asc rename to internal/keyservice/gpg/testdata/key1.asc diff --git a/internal/gpg/testdata/key2.asc b/internal/keyservice/gpg/testdata/key2.asc similarity index 100% rename from internal/gpg/testdata/key2.asc rename to internal/keyservice/gpg/testdata/key2.asc diff --git a/internal/gpg/testdata/key3.asc b/internal/keyservice/gpg/testdata/key3.asc similarity index 100% rename from internal/gpg/testdata/key3.asc rename to internal/keyservice/gpg/testdata/key3.asc diff --git a/internal/gpg/testdata/key4.asc b/internal/keyservice/gpg/testdata/key4.asc similarity index 100% rename from internal/gpg/testdata/key4.asc rename to internal/keyservice/gpg/testdata/key4.asc diff --git a/internal/gpg/testdata/private/bar-protected@example.com.gpg b/internal/keyservice/gpg/testdata/private/bar-protected@example.com.gpg similarity index 100% rename from internal/gpg/testdata/private/bar-protected@example.com.gpg rename to internal/keyservice/gpg/testdata/private/bar-protected@example.com.gpg diff --git a/internal/gpg/testdata/private/bar@example.com.gpg b/internal/keyservice/gpg/testdata/private/bar@example.com.gpg similarity index 100% rename from internal/gpg/testdata/private/bar@example.com.gpg rename to internal/keyservice/gpg/testdata/private/bar@example.com.gpg diff --git a/internal/pivservice/pivservice.go b/internal/keyservice/piv/keyservice.go similarity index 83% rename from internal/pivservice/pivservice.go rename to internal/keyservice/piv/keyservice.go index 15c95b6..c7c403e 100644 --- a/internal/pivservice/pivservice.go +++ b/internal/keyservice/piv/keyservice.go @@ -1,4 +1,4 @@ -package pivservice +package piv import ( "bytes" @@ -7,33 +7,33 @@ import ( "fmt" "sync" - "github.com/smlx/piv-agent/internal/gpg" + "github.com/smlx/piv-agent/internal/keyservice/gpg" "go.uber.org/zap" ) -// PIVService represents a collection of tokens and slots accessed by the +// KeyService represents a collection of tokens and slots accessed by the // Personal Identity Verifaction card interface. -type PIVService struct { +type KeyService struct { mu sync.Mutex log *zap.Logger securityKeys []SecurityKey } // New constructs a PIV and returns it. -func New(l *zap.Logger) *PIVService { - return &PIVService{ +func New(l *zap.Logger) *KeyService { + return &KeyService{ log: l, } } // Name returns the name of the keyservice. -func (p *PIVService) Name() string { +func (*KeyService) Name() string { return "PIV" } // HaveKey takes a list of keygrips, and returns a boolean indicating if any of // the given keygrips were found, the found keygrip, and an error, if any. -func (p *PIVService) HaveKey(keygrips [][]byte) (bool, []byte, error) { +func (p *KeyService) HaveKey(keygrips [][]byte) (bool, []byte, error) { securityKeys, err := p.SecurityKeys() if err != nil { return false, nil, fmt.Errorf("couldn't get security keys: %w", err) @@ -60,7 +60,7 @@ func (p *PIVService) HaveKey(keygrips [][]byte) (bool, []byte, error) { } // GetSigner returns a crypto.Signer associated with the given keygrip. -func (p *PIVService) GetSigner(keygrip []byte) (crypto.Signer, error) { +func (p *KeyService) GetSigner(keygrip []byte) (crypto.Signer, error) { securityKeys, err := p.SecurityKeys() if err != nil { return nil, fmt.Errorf("couldn't get security keys: %w", err) @@ -93,7 +93,7 @@ func (p *PIVService) GetSigner(keygrip []byte) (crypto.Signer, error) { } // GetDecrypter returns a crypto.Decrypter associated with the given keygrip. -func (p *PIVService) GetDecrypter(keygrip []byte) (crypto.Decrypter, error) { +func (p *KeyService) GetDecrypter(keygrip []byte) (crypto.Decrypter, error) { // TODO: implement this return nil, fmt.Errorf("not implemented") } diff --git a/internal/pivservice/list.go b/internal/keyservice/piv/list.go similarity index 89% rename from internal/pivservice/list.go rename to internal/keyservice/piv/list.go index f8f042a..7979247 100644 --- a/internal/pivservice/list.go +++ b/internal/keyservice/piv/list.go @@ -1,6 +1,6 @@ -package pivservice +package piv -//go:generate mockgen -source=list.go -destination=../mock/mock_pivservice.go -package=mock +//go:generate mockgen -source=list.go -destination=../../mock/mock_pivservice.go -package=mock import ( "crypto" @@ -26,7 +26,7 @@ type SecurityKey interface { StringsSSH() []string } -func (p *PIVService) reloadSecurityKeys() error { +func (p *KeyService) reloadSecurityKeys() error { // try to clean up and reset state for _, k := range p.securityKeys { _ = k.Close() @@ -53,7 +53,7 @@ func (p *PIVService) reloadSecurityKeys() error { } // SecurityKeys returns a slice containing all available security keys. -func (p *PIVService) SecurityKeys() ([]SecurityKey, error) { +func (p *KeyService) SecurityKeys() ([]SecurityKey, error) { p.mu.Lock() defer p.mu.Unlock() var err error diff --git a/internal/securitykey/string.go b/internal/securitykey/string.go index 47c0d92..57d39ee 100644 --- a/internal/securitykey/string.go +++ b/internal/securitykey/string.go @@ -35,7 +35,7 @@ func (k *SecurityKey) StringsSSH() []string { for _, s := range k.SigningKeys() { ss = append(ss, fmt.Sprintf("%s %s\n", strings.TrimSuffix(string(ssh.MarshalAuthorizedKey(s.PubSSH)), "\n"), - fmt.Sprintf("%v #%v, touch policy: %s", k.Card(), k.Serial(), + fmt.Sprintf("%v #%v, touch policy: %s", k.card, k.serial, touchStringMap[s.SlotSpec.TouchPolicy]))) } return ss @@ -114,7 +114,7 @@ func (k *SecurityKey) StringsGPG(name, email string) ([]string, error) { w, err := armor.Encode(&buf, openpgp.PublicKeyType, map[string]string{ "Comment": fmt.Sprintf("%v #%v, touch policy: %s", - k.Card(), k.Serial(), touchStringMap[e.SlotSpec.TouchPolicy]), + k.card, k.serial, touchStringMap[e.SlotSpec.TouchPolicy]), }) if err != nil { return nil, fmt.Errorf("couldn't get PGP public key armorer: %w", err) diff --git a/internal/server/gpg.go b/internal/server/gpg.go index c3bf1c2..4494672 100644 --- a/internal/server/gpg.go +++ b/internal/server/gpg.go @@ -7,29 +7,29 @@ import ( "time" "github.com/smlx/piv-agent/internal/assuan" - "github.com/smlx/piv-agent/internal/gpg" - "github.com/smlx/piv-agent/internal/pivservice" + "github.com/smlx/piv-agent/internal/keyservice/gpg" + "github.com/smlx/piv-agent/internal/keyservice/piv" "go.uber.org/zap" ) // GPG represents a gpg-agent server. type GPG struct { - log *zap.Logger - pivService *pivservice.PIVService - keyfileService *gpg.KeyfileService // fallback keyfile keys + log *zap.Logger + pivKeyService *piv.KeyService + gpgKeyService *gpg.KeyService // fallback keyfile keys } // NewGPG initialises a new gpg-agent server. -func NewGPG(piv *pivservice.PIVService, pinentry gpg.PINEntryService, +func NewGPG(piv *piv.KeyService, pinentry gpg.PINEntryService, log *zap.Logger, path string) *GPG { - kfs, err := gpg.NewKeyfileService(log, pinentry, path) + kfs, err := gpg.New(log, pinentry, path) if err != nil { log.Info("couldn't load keyfiles", zap.String("path", path), zap.Error(err)) } return &GPG{ - pivService: piv, - log: log, - keyfileService: kfs, + log: log, + pivKeyService: piv, + gpgKeyService: kfs, } } @@ -53,7 +53,7 @@ func (g *GPG) Serve(ctx context.Context, l net.Listener, exit *time.Ticker, return fmt.Errorf("couldn't set deadline: %v", err) } // init protocol state machine - a := assuan.New(conn, g.log, g.pivService, g.keyfileService) + a := assuan.New(conn, g.log, g.pivKeyService, g.gpgKeyService) // run the protocol state machine to completion // (client severs connection) if err := a.Run(); err != nil { diff --git a/internal/ssh/agent.go b/internal/ssh/agent.go index f0cd370..d8cbf5a 100644 --- a/internal/ssh/agent.go +++ b/internal/ssh/agent.go @@ -10,9 +10,9 @@ import ( "path/filepath" "sync" + "github.com/smlx/piv-agent/internal/keyservice/piv" "github.com/smlx/piv-agent/internal/notify" pinentry "github.com/smlx/piv-agent/internal/pinentry" - "github.com/smlx/piv-agent/internal/pivservice" "go.uber.org/zap" gossh "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" @@ -22,7 +22,7 @@ import ( // https://pkg.go.dev/golang.org/x/crypto/ssh/agent#Agent type Agent struct { mu sync.Mutex - pivService *pivservice.PIVService + piv *piv.KeyService log *zap.Logger loadKeyfile bool } @@ -37,8 +37,8 @@ var ErrUnknownKey = errors.New("requested signature of unknown key") var passphrases = map[string][]byte{} // NewAgent returns a new Agent. -func NewAgent(p *pivservice.PIVService, log *zap.Logger, loadKeyfile bool) *Agent { - return &Agent{pivService: p, log: log, loadKeyfile: loadKeyfile} +func NewAgent(p *piv.KeyService, log *zap.Logger, loadKeyfile bool) *Agent { + return &Agent{piv: p, log: log, loadKeyfile: loadKeyfile} } // List returns the identities known to the agent. @@ -64,7 +64,7 @@ func (a *Agent) List() ([]*agent.Key, error) { // returns the identities from hardware tokens func (a *Agent) securityKeyIDs() ([]*agent.Key, error) { var keys []*agent.Key - securityKeys, err := a.pivService.SecurityKeys() + securityKeys, err := a.piv.SecurityKeys() if err != nil { return nil, fmt.Errorf("couldn't get security keys: %v", err) } @@ -201,7 +201,7 @@ func (a *Agent) Signers() ([]gossh.Signer, error) { // get signers for all keys stored in hardware tokens func (a *Agent) tokenSigners() ([]gossh.Signer, error) { var signers []gossh.Signer - securityKeys, err := a.pivService.SecurityKeys() + securityKeys, err := a.piv.SecurityKeys() if err != nil { return nil, fmt.Errorf("couldn't get security keys: %v", err) }