Skip to content

openpgp: ReadKeyRing panics with index out of range on a malformed exportable-certification subpacket #327

Description

@elimisteve

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions