Skip to content

Commit eb7534d

Browse files
authored
Merge pull request #141 from smlx/setkey
fix: synchronise access to security key hardware
2 parents 4a270a5 + be80dd6 commit eb7534d

File tree

3 files changed

+45
-19
lines changed

3 files changed

+45
-19
lines changed

internal/keyservice/piv/ecdhkey.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99
"regexp"
10+
"sync"
1011

1112
pivgo "github.com/go-piv/piv-go/piv"
1213
"github.com/smlx/piv-agent/internal/assuan"
@@ -17,12 +18,16 @@ var ciphertextECDH = regexp.MustCompile(
1718

1819
// ECDHKey implements ECDH using an underlying ECDSA key.
1920
type ECDHKey struct {
20-
ecdsa *pivgo.ECDSAPrivateKey
21+
mu *sync.Mutex
22+
*pivgo.ECDSAPrivateKey
2123
}
2224

23-
// Decrypt performs ECDH as per gpg-agent.
25+
// Decrypt performs ECDH as per gpg-agent, and implements the crypto.Decrypter
26+
// interface.
2427
func (k *ECDHKey) Decrypt(_ io.Reader, sexp []byte,
2528
_ crypto.DecrypterOpts) ([]byte, error) {
29+
k.mu.Lock()
30+
defer k.mu.Unlock()
2631
// parse out the ephemeral public key
2732
matches := ciphertextECDH.FindAllSubmatch(sexp, -1)
2833
ciphertext := matches[0][2]
@@ -40,7 +45,7 @@ func (k *ECDHKey) Decrypt(_ io.Reader, sexp []byte,
4045
Y: ephPubY,
4146
}
4247
// marshal, encode, and return the result
43-
shared, err := k.ecdsa.SharedKey(&ephPub)
48+
shared, err := k.SharedKey(&ephPub)
4449
if err != nil {
4550
return nil, fmt.Errorf("couldn't generate shared secret: %v", err)
4651
}
@@ -49,8 +54,10 @@ func (k *ECDHKey) Decrypt(_ io.Reader, sexp []byte,
4954
return []byte(fmt.Sprintf("D (5:value%d:%s)\nOK\n", sharedLen, shared)), nil
5055
}
5156

52-
// Public implements the other required method of the crypto.Decrypter and
53-
// crypto.Signer interfaces.
54-
func (k *ECDHKey) Public() crypto.PublicKey {
55-
return k.ecdsa.Public()
57+
// Sign wraps the underlying private key Sign operation in a mutex.
58+
func (k *ECDHKey) Sign(rand io.Reader, digest []byte,
59+
opts crypto.SignerOpts) ([]byte, error) {
60+
k.mu.Lock()
61+
defer k.mu.Unlock()
62+
return k.ECDSAPrivateKey.Sign(rand, digest, opts)
5663
}

internal/keyservice/piv/keyservice.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ func (*KeyService) Name() string {
3939
// Keygrips returns a single slice of concatenated keygrip byteslices - one for
4040
// each cryptographic key available on the keyservice.
4141
func (p *KeyService) Keygrips() ([][]byte, error) {
42+
p.mu.Lock()
43+
defer p.mu.Unlock()
4244
var grips [][]byte
43-
securityKeys, err := p.SecurityKeys()
45+
securityKeys, err := p.getSecurityKeys()
4446
if err != nil {
4547
return nil, fmt.Errorf("couldn't get security keys: %w", err)
4648
}
@@ -64,7 +66,9 @@ func (p *KeyService) Keygrips() ([][]byte, error) {
6466
// HaveKey takes a list of keygrips, and returns a boolean indicating if any of
6567
// the given keygrips were found, the found keygrip, and an error, if any.
6668
func (p *KeyService) HaveKey(keygrips [][]byte) (bool, []byte, error) {
67-
securityKeys, err := p.SecurityKeys()
69+
p.mu.Lock()
70+
defer p.mu.Unlock()
71+
securityKeys, err := p.getSecurityKeys()
6872
if err != nil {
6973
return false, nil, fmt.Errorf("couldn't get security keys: %w", err)
7074
}
@@ -90,7 +94,7 @@ func (p *KeyService) HaveKey(keygrips [][]byte) (bool, []byte, error) {
9094
}
9195

9296
func (p *KeyService) getPrivateKey(keygrip []byte) (crypto.PrivateKey, error) {
93-
securityKeys, err := p.SecurityKeys()
97+
securityKeys, err := p.getSecurityKeys()
9498
if err != nil {
9599
return nil, fmt.Errorf("couldn't get security keys: %w", err)
96100
}
@@ -110,7 +114,11 @@ func (p *KeyService) getPrivateKey(keygrip []byte) (crypto.PrivateKey, error) {
110114
if err != nil {
111115
return nil, fmt.Errorf("couldn't get private key from slot")
112116
}
113-
return privKey, nil
117+
pivGoPrivKey, ok := privKey.(*pivgo.ECDSAPrivateKey)
118+
if !ok {
119+
return nil, fmt.Errorf("unexpected private key type: %T", privKey)
120+
}
121+
return &ECDHKey{mu: &p.mu, ECDSAPrivateKey: pivGoPrivKey}, nil
114122
}
115123
}
116124
}
@@ -119,33 +127,39 @@ func (p *KeyService) getPrivateKey(keygrip []byte) (crypto.PrivateKey, error) {
119127

120128
// GetSigner returns a crypto.Signer associated with the given keygrip.
121129
func (p *KeyService) GetSigner(keygrip []byte) (crypto.Signer, error) {
130+
p.mu.Lock()
131+
defer p.mu.Unlock()
122132
privKey, err := p.getPrivateKey(keygrip)
123133
if err != nil {
124134
return nil, fmt.Errorf("couldn't get private key: %v", err)
125135
}
126136
signingPrivKey, ok := privKey.(crypto.Signer)
127137
if !ok {
128-
return nil, fmt.Errorf("private key is invalid type")
138+
return nil, fmt.Errorf("private key is not a signer")
129139
}
130140
return signingPrivKey, nil
131141
}
132142

133143
// GetDecrypter returns a crypto.Decrypter associated with the given keygrip.
134144
func (p *KeyService) GetDecrypter(keygrip []byte) (crypto.Decrypter, error) {
145+
p.mu.Lock()
146+
defer p.mu.Unlock()
135147
privKey, err := p.getPrivateKey(keygrip)
136148
if err != nil {
137149
return nil, fmt.Errorf("couldn't get private key: %v", err)
138150
}
139-
ecdsaPrivKey, ok := privKey.(*pivgo.ECDSAPrivateKey)
151+
decryptingPrivKey, ok := privKey.(crypto.Decrypter)
140152
if !ok {
141-
return nil, fmt.Errorf("private key is invalid type")
153+
return nil, fmt.Errorf("private key is not a decrypter")
142154
}
143-
return &ECDHKey{ecdsa: ecdsaPrivKey}, nil
155+
return decryptingPrivKey, nil
144156
}
145157

146158
// CloseAll closes all security keys without checking for errors.
147159
// This should be called to clean up connections to `pcscd`.
148160
func (p *KeyService) CloseAll() {
161+
p.mu.Lock()
162+
defer p.mu.Unlock()
149163
p.log.Debug("closing security keys", zap.Int("count", len(p.securityKeys)))
150164
for _, k := range p.securityKeys {
151165
if err := k.Close(); err != nil {

internal/keyservice/piv/list.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,7 @@ func (p *KeyService) reloadSecurityKeys() error {
5454
return nil
5555
}
5656

57-
// SecurityKeys returns a slice containing all available security keys.
58-
func (p *KeyService) SecurityKeys() ([]SecurityKey, error) {
59-
p.mu.Lock()
60-
defer p.mu.Unlock()
57+
func (p *KeyService) getSecurityKeys() ([]SecurityKey, error) {
6158
var err error
6259
// check if any securityKeys are cached, and if not then cache them
6360
if len(p.securityKeys) == 0 {
@@ -68,6 +65,7 @@ func (p *KeyService) SecurityKeys() ([]SecurityKey, error) {
6865
// check they are healthy, and reload if not
6966
for _, k := range p.securityKeys {
7067
if _, err = k.AttestationCertificate(); err != nil {
68+
p.log.Debug("PIV KeyService: couldn't get AttestationCertificate()", zap.Error(err))
7169
if err = p.reloadSecurityKeys(); err != nil {
7270
return nil, fmt.Errorf("couldn't reload security keys: %v", err)
7371
}
@@ -76,3 +74,10 @@ func (p *KeyService) SecurityKeys() ([]SecurityKey, error) {
7674
}
7775
return p.securityKeys, nil
7876
}
77+
78+
// SecurityKeys returns a slice containing all available security keys.
79+
func (p *KeyService) SecurityKeys() ([]SecurityKey, error) {
80+
p.mu.Lock()
81+
defer p.mu.Unlock()
82+
return p.getSecurityKeys()
83+
}

0 commit comments

Comments
 (0)