diff --git a/agent/conn.go b/agent/conn.go index c7a0586..2c308f9 100644 --- a/agent/conn.go +++ b/agent/conn.go @@ -115,7 +115,14 @@ func (conn *Conn) Close() error { } func keyScan(key *Key, line string) error { - parts := strings.Split(line, " ") + unfilteredParts := strings.Split(line, " ") + parts := []string{} + for _, p := range unfilteredParts { + if p != "" { + parts = append(parts, p) + } + } + if len(parts) != 10 { return fmt.Errorf("illegal format for KEYINFO line") } @@ -226,6 +233,60 @@ func (conn *Conn) Keys() ([]Key, error) { return keyList, nil } +// KeyGrips returns a list of available keygrips, indexed by CardID, by querying the card +func (conn *Conn) KeyGrips() (map[string]string, error) { + grips := map[string]string{} + + scan := func(key *Key, line string) error { + unfilteredParts := strings.Split(line, " ") + var parts []string + for _, p := range unfilteredParts { + if p != "" { + parts = append(parts, p) + } + } + + if len(parts) < 3 { + return fmt.Errorf("illegal format for KEYINFO line: %s", line) + } + + keygrip := parts[1] + cardID := parts[2] + if strings.HasPrefix(cardID, "OPENPGP.") && len(keygrip) == 40 { + // grips[cardID] = keygrip + grips[cardID] = keygrip + } else { + fmt.Printf("Warning: card ID or keygrip invalid, skipping: %s", line) + } + + + return nil + } + + respFunc := func(respType, data string) error { + if respType != "S" || !strings.HasPrefix(data, "KEYPAIRINFO ") { + return nil + } + + var key Key + if err := scan(&key, data); err != nil { + return err + } + + return nil + } + + conn.mu.Lock() + defer conn.mu.Unlock() + + err := conn.Raw(respFunc, "scd LEARN --force") + if err != nil { + return nil, err + } + + return grips, nil +} + // Raw executes a command and pipes its results to the specified ResponseFunc // parameter. func (conn *Conn) Raw(f ResponseFunc, format string, a ...interface{}) error { diff --git a/agent/key.go b/agent/key.go index 38ed34d..7210ccf 100644 --- a/agent/key.go +++ b/agent/key.go @@ -1,6 +1,7 @@ package agent import ( + "bytes" "crypto" "crypto/rsa" "encoding/hex" @@ -119,8 +120,22 @@ func (key Key) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signatu return internalrsa.SignPSS(rand, priv, pssOpts.Hash, msg, pssOpts) } - return key.signPKCS1v15(msg, opts.HashFunc()) + sig, err := key.signPKCS1v15(msg, opts.HashFunc()) + if err != nil { + return nil, err + } + + if diff := len(sig) - pub.Size(); diff > 0 { + // It seems sometimes the signature can have leading zero bytes such that its over the keysize + leadingZeros := bytes.Repeat([]byte{00}, diff) + if !bytes.HasPrefix(sig, leadingZeros) { + return nil, errors.New("github.com/prep/gpg/agent: signature length too large with nonzero leading bytes") + } + // We trim the leading zeros + sig = bytes.TrimPrefix(sig, leadingZeros) + } + return sig, nil default: return nil, errors.New("github.com/prep/gpg/agent: unknown public key") }