Skip to content

Commit

Permalink
Fix endless loop in x/crypto/openpgp func ReadMessage (#690)
Browse files Browse the repository at this point in the history
* Fix tests

* Fix endless loop in x/crypto/openpgp func ReadMessage

This fixes #665
See also golang/go#28786

In some strange situations it can happen, that openpgp.ReadMessage()
runs into a endless loop. This seems to be triggered by a slightly
inconsistency in key settings.
It happened to me, but I wasn't able to reproduce it with a fresh key.
A proposed solution from the x/crypto community was, to break this loop
in the callback passphrasePrompt.

* Revert "Fix tests"

This reverts commit 285f4dc.

* Improve error description

#690 (comment)
  • Loading branch information
uwehdaub committed Jul 14, 2020
1 parent 260ca36 commit 4f06780
Showing 1 changed file with 38 additions and 29 deletions.
67 changes: 38 additions & 29 deletions pgp/keysource.go
Expand Up @@ -216,7 +216,7 @@ func (key *MasterKey) decryptWithCryptoOpenpgp() ([]byte, error) {
if err != nil {
return nil, fmt.Errorf("Armor decoding failed: %s", err)
}
md, err := openpgp.ReadMessage(block.Body, ring, key.passphrasePrompt, nil)
md, err := openpgp.ReadMessage(block.Body, ring, key.passphrasePrompt(), nil)
if err != nil {
return nil, fmt.Errorf("Reading PGP message failed: %s", err)
}
Expand Down Expand Up @@ -318,39 +318,48 @@ func (key *MasterKey) fingerprintMap(ring openpgp.EntityList) map[string]openpgp
return fps
}

func (key *MasterKey) passphrasePrompt(keys []openpgp.Key, symmetric bool) ([]byte, error) {
conn, err := gpgagent.NewConn()
if err == gpgagent.ErrNoAgent {
log.Infof("gpg-agent not found, continuing with manual passphrase " +
"input...")
fmt.Print("Enter PGP key passphrase: ")
pass, err := gopass.GetPasswd()
if err != nil {
return nil, err
}
for _, k := range keys {
k.PrivateKey.Decrypt(pass)
func (key *MasterKey) passphrasePrompt() func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
callCounter := 0
maxCalls := 3
return func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
if callCounter >= maxCalls {
return nil, fmt.Errorf("function passphrasePrompt called too many times")
}
return pass, err
}
if err != nil {
return nil, fmt.Errorf("Could not establish connection with gpg-agent: %s", err)
}
defer conn.Close()
for _, k := range keys {
req := gpgagent.PassphraseRequest{
CacheKey: k.PublicKey.KeyIdShortString(),
Prompt: "Passphrase",
Desc: fmt.Sprintf("Unlock key %s to decrypt sops's key", k.PublicKey.KeyIdShortString()),
callCounter++

conn, err := gpgagent.NewConn()
if err == gpgagent.ErrNoAgent {
log.Infof("gpg-agent not found, continuing with manual passphrase " +
"input...")
fmt.Print("Enter PGP key passphrase: ")
pass, err := gopass.GetPasswd()
if err != nil {
return nil, err
}
for _, k := range keys {
k.PrivateKey.Decrypt(pass)
}
return pass, err
}
pass, err := conn.GetPassphrase(&req)
if err != nil {
return nil, fmt.Errorf("gpg-agent passphrase request errored: %s", err)
return nil, fmt.Errorf("Could not establish connection with gpg-agent: %s", err)
}
defer conn.Close()
for _, k := range keys {
req := gpgagent.PassphraseRequest{
CacheKey: k.PublicKey.KeyIdShortString(),
Prompt: "Passphrase",
Desc: fmt.Sprintf("Unlock key %s to decrypt sops's key", k.PublicKey.KeyIdShortString()),
}
pass, err := conn.GetPassphrase(&req)
if err != nil {
return nil, fmt.Errorf("gpg-agent passphrase request errored: %s", err)
}
k.PrivateKey.Decrypt([]byte(pass))
return []byte(pass), nil
}
k.PrivateKey.Decrypt([]byte(pass))
return []byte(pass), nil
return nil, fmt.Errorf("No key to unlock")
}
return nil, fmt.Errorf("No key to unlock")
}

// ToMap converts the MasterKey into a map for serialization purposes
Expand Down

0 comments on commit 4f06780

Please sign in to comment.