Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error handling #84

Merged
merged 2 commits into from
Nov 20, 2021
Merged
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
2 changes: 1 addition & 1 deletion cmd/piv-agent/setupslots.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type SetupSlotsCmd struct {
Card string `kong:"help='Specify a smart card device'"`
ResetSlots bool `kong:"help='Overwrite existing keys in the targeted slots'"`
PIN uint64 `kong:"help='The PIN/PUK of the device (6-8 digits). Will be prompted interactively if not provided.'"`
SigningKeys []string `kong:"default='',enum='cached,always,never',help='Set up slots for signing keys with various touch policies (default none, possible values cached,always,never)'"`
SigningKeys []string `kong:"required,enum='cached,always,never',help='Set up slots for signing keys with various touch policies (possible values cached,always,never)'"`
DecryptingKey bool `kong:"default='false',help='Set up slot for a decrypting key (default false)'"`
}

Expand Down
93 changes: 56 additions & 37 deletions internal/assuan/assuan.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ type KeyService interface {
// New initialises a new gpg-agent server assuan FSM.
// It returns a *fsm.Machine configured in the ready state.
func New(rw io.ReadWriter, log *zap.Logger, ks ...KeyService) *Assuan {
var keyFound bool
var signature []byte
var keygrips, hash [][]byte
assuan := Assuan{
Expand All @@ -49,10 +48,7 @@ func New(rw io.ReadWriter, log *zap.Logger, ks ...KeyService) *Assuan {
_, err = io.WriteString(rw,
"OK Pleased to meet you, process 123456789\n")
case reset:
assuan.signer = nil
assuan.decrypter = nil
assuan.hashAlgo = 0
assuan.hash = []byte{}
assuan.reset()
_, err = io.WriteString(rw, "OK\n")
case option:
// ignore option values - piv-agent doesn't use them
Expand All @@ -65,38 +61,7 @@ func New(rw io.ReadWriter, log *zap.Logger, ks ...KeyService) *Assuan {
err = fmt.Errorf("unknown getinfo command: %q", assuan.data[0])
}
case havekey:
// HAVEKEY arguments are either:
// * a list of keygrips; or
// * --list=1000
// if _any_ key is available, we return OK, otherwise No_Secret_Key.
// handle --list
if bytes.HasPrefix(assuan.data[0], []byte("--list")) {
var grips []byte
grips, err = allKeygrips(ks)
if err != nil {
_, _ = io.WriteString(rw, "ERR 1 couldn't list keygrips\n")
return err
}
// apply buggy libgcrypt encoding
_, err = io.WriteString(rw, fmt.Sprintf("D %s\nOK\n",
PercentEncodeSExp(grips)))
return err
}
// handle list of keygrips
keygrips, err = hexDecode(assuan.data...)
if err != nil {
return fmt.Errorf("couldn't decode keygrips: %v", err)
}
keyFound, _, err = haveKey(ks, keygrips)
if err != nil {
_, _ = io.WriteString(rw, "ERR 1 couldn't check for keygrip\n")
return err
}
if keyFound {
_, err = io.WriteString(rw, "OK\n")
} else {
_, err = io.WriteString(rw, "No_Secret_Key\n")
}
err = assuan.havekey(rw, ks)
case keyinfo:
err = doKeyinfo(rw, assuan.data, ks)
case scd:
Expand Down Expand Up @@ -291,6 +256,14 @@ func New(rw io.ReadWriter, log *zap.Logger, ks ...KeyService) *Assuan {
// ignore this event since we don't currently use the client's
// description in the prompt
_, err = io.WriteString(rw, "OK\n")
case havekey:
// gpg skips the RESET command occasionally so we have to emulate it.
assuan.reset()
// now jump straight to havekey
if err = assuan.havekey(rw, ks); err != nil {
return err
}
_, err = io.WriteString(rw, "OK\n")
default:
return fmt.Errorf("unknown event: %v", Event(e))
}
Expand All @@ -301,6 +274,52 @@ func New(rw io.ReadWriter, log *zap.Logger, ks ...KeyService) *Assuan {
return &assuan
}

func (assuan *Assuan) reset() {
assuan.signer = nil
assuan.decrypter = nil
assuan.hashAlgo = 0
assuan.hash = []byte{}
}

func (assuan *Assuan) havekey(rw io.ReadWriter, ks []KeyService) error {
var err error
var keyFound bool
var keygrips [][]byte
// HAVEKEY arguments are either:
// * a list of keygrips; or
// * --list=1000
// if _any_ key is available, we return OK, otherwise No_Secret_Key.
// handle --list
if bytes.HasPrefix(assuan.data[0], []byte("--list")) {
var grips []byte
grips, err = allKeygrips(ks)
if err != nil {
_, _ = io.WriteString(rw, "ERR 1 couldn't list keygrips\n")
return err
}
// apply buggy libgcrypt encoding
_, err = io.WriteString(rw, fmt.Sprintf("D %s\nOK\n",
PercentEncodeSExp(grips)))
return err
}
// handle list of keygrips
keygrips, err = hexDecode(assuan.data...)
if err != nil {
return fmt.Errorf("couldn't decode keygrips: %v", err)
}
keyFound, _, err = haveKey(ks, keygrips)
if err != nil {
_, _ = io.WriteString(rw, "ERR 1 couldn't check for keygrip\n")
return err
}
if keyFound {
_, err = io.WriteString(rw, "OK\n")
} else {
_, err = io.WriteString(rw, "No_Secret_Key\n")
}
return err
}

// doKeyinfo checks for key availability by keygrip, writing the result to rw.
func doKeyinfo(rw io.ReadWriter, data [][]byte, ks []KeyService) error {
// KEYINFO arguments are a list of keygrips
Expand Down
4 changes: 2 additions & 2 deletions internal/assuan/event_enumer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions internal/assuan/fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,5 +156,9 @@ var assuanTransitions = []fsm.Transition{
Src: fsm.State(decryptingKeyIsSet),
Event: fsm.Event(pkdecrypt),
Dst: fsm.State(waitingForCiphertext),
}, {
Src: fsm.State(waitingForCiphertext),
Event: fsm.Event(havekey),
Dst: fsm.State(connected),
},
}
4 changes: 2 additions & 2 deletions internal/assuan/state_enumer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.