Version: v1.4.1 (newest tag) and main d697e70 (2026-08-14). Go 1.26, linux/amd64. Affects both openpgp/v2 and the legacy openpgp package.
What happens
Parsing a 97-byte key ring (found by fuzzing) panics instead of returning an error:
panic: runtime error: index out of range [0] with length 0
github.com/ProtonMail/go-crypto/openpgp/packet.parseSignatureSubpacket openpgp/packet/signature.go:458
github.com/ProtonMail/go-crypto/openpgp/packet.parseSignatureSubpackets openpgp/packet/signature.go:349
github.com/ProtonMail/go-crypto/openpgp/packet.(*Signature).parse openpgp/packet/signature.go:240
github.com/ProtonMail/go-crypto/openpgp/packet.Read
github.com/ProtonMail/go-crypto/openpgp/packet.(*Reader).NextWithUnsupported
github.com/ProtonMail/go-crypto/openpgp/v2.readUser
github.com/ProtonMail/go-crypto/openpgp/v2.ReadEntity
github.com/ProtonMail/go-crypto/openpgp/v2.ReadKeyRing
The legacy openpgp.ReadKeyRing panics at the same line via openpgp.addUserID.
Cause
parseSignatureSubpacket handles exportableCertSubpacket as:
case exportableCertSubpacket:
if subpacket[0] == 0 {
with no length check, unlike the neighbouring cases (creationTimeSubpacket, signatureExpirationSubpacket, trustSubpacket) which verify the length first. A subpacket whose declared length is 1 consists of the type octet alone; after the type octet is stripped the body is empty, and subpacket[0] indexes an empty slice.
Expected
errors.StructuralError, as for the other malformed subpackets and as #66 ("Don't panic on corrupt fingerprint subpacket") established for the same switch. Key rings are routinely parsed from untrusted input.
Minimal reproduction
package repro
import (
"bytes"
"testing"
openpgp "github.com/ProtonMail/go-crypto/openpgp/v2"
)
func TestReadKeyRingPanics(t *testing.T) {
data := []byte("\xc67\x040000\x16\t+\x06\x01\x04\x01\xdaG\x0f\x01\x01\a@00000000000000000000000000000000\xcd\x1c0000000000000000000000000000\xc20\x040\x16\b\x00\x06\x01\x040000")
_, err := openpgp.ReadKeyRing(bytes.NewReader(data)) // panics; should return err
t.Log(err)
}
Suggested fix
case exportableCertSubpacket:
if len(subpacket) != 1 {
err = errors.StructuralError("exportable certification subpacket with bad length")
return
}
if subpacket[0] == 0 {
A quick scan of the same switch for other cases that index subpacket without a length check may be worthwhile.
Version: v1.4.1 (newest tag) and main d697e70 (2026-08-14). Go 1.26, linux/amd64. Affects both
openpgp/v2and the legacyopenpgppackage.What happens
Parsing a 97-byte key ring (found by fuzzing) panics instead of returning an error:
The legacy
openpgp.ReadKeyRingpanics at the same line viaopenpgp.addUserID.Cause
parseSignatureSubpackethandlesexportableCertSubpacketas:with no length check, unlike the neighbouring cases (
creationTimeSubpacket,signatureExpirationSubpacket,trustSubpacket) which verify the length first. A subpacket whose declared length is 1 consists of the type octet alone; after the type octet is stripped the body is empty, andsubpacket[0]indexes an empty slice.Expected
errors.StructuralError, as for the other malformed subpackets and as #66 ("Don't panic on corrupt fingerprint subpacket") established for the same switch. Key rings are routinely parsed from untrusted input.Minimal reproduction
Suggested fix
A quick scan of the same
switchfor other cases that indexsubpacketwithout a length check may be worthwhile.