1
0
mirror of https://github.com/getsops/sops.git synced 2026-02-05 12:45:21 +01:00

Fix endless loop in x/crypto/openpgp func ReadMessage (#690)

* Fix tests

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

This fixes https://github.com/mozilla/sops/issues/665
See also https://github.com/golang/go/issues/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 285f4dc8a1.

* Improve error description

https://github.com/mozilla/sops/pull/690#discussion_r451630193
This commit is contained in:
Dr. Uwe Daub
2020-07-14 21:25:06 +02:00
committed by GitHub
parent 260ca36ef1
commit 4f06780ab2

View File

@@ -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)
}
@@ -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
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")
}
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
}
if err != nil {
return nil, fmt.Errorf("Could not establish connection with gpg-agent: %s", err)
}
defer conn.Close()
for _, k := range keys {
k.PrivateKey.Decrypt(pass)
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
}
return pass, err
return nil, fmt.Errorf("No key to unlock")
}
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()),
}
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
}
return nil, fmt.Errorf("No key to unlock")
}
// ToMap converts the MasterKey into a map for serialization purposes