diff --git a/openpgp/packet/packet.go b/openpgp/packet/packet.go index 6f84a25e..5c6808e5 100644 --- a/openpgp/packet/packet.go +++ b/openpgp/packet/packet.go @@ -161,6 +161,15 @@ func (l *spanReader) Read(p []byte) (n int, err error) { return } +// readerRemaining returns the number of bytes that can still be read from a +// length-delimited packet reader, or -1 if the amount is not known in advance. +func readerRemaining(r io.Reader) int64 { + if sr, ok := r.(*spanReader); ok { + return sr.n + } + return -1 +} + // readHeader parses a packet header and returns an io.Reader which will return // the contents of the packet. See RFC 4880, section 4.2. func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err error) { diff --git a/openpgp/packet/signature.go b/openpgp/packet/signature.go index 1bfcb21c..238ccfcc 100644 --- a/openpgp/packet/signature.go +++ b/openpgp/packet/signature.go @@ -239,6 +239,9 @@ func (sig *Signature) parse(r io.Reader) (err error) { } else { hashedSubpacketsLength = int(buf[3])<<8 | int(buf[4]) } + if remaining := readerRemaining(r); remaining >= 0 && int64(hashedSubpacketsLength) > remaining { + return errors.StructuralError("hashed subpacket data length is larger than the packet") + } hashedSubpackets := make([]byte, hashedSubpacketsLength) _, err = readFull(r, hashedSubpackets) if err != nil { @@ -269,6 +272,9 @@ func (sig *Signature) parse(r io.Reader) (err error) { } else { unhashedSubpacketsLength = uint32(buf[0])<<8 | uint32(buf[1]) } + if remaining := readerRemaining(r); remaining >= 0 && int64(unhashedSubpacketsLength) > remaining { + return errors.StructuralError("unhashed subpacket data length is larger than the packet") + } unhashedSubpackets := make([]byte, unhashedSubpacketsLength) _, err = readFull(r, unhashedSubpackets) if err != nil { diff --git a/openpgp/packet/signature_security_test.go b/openpgp/packet/signature_security_test.go new file mode 100644 index 00000000..fedbed0d --- /dev/null +++ b/openpgp/packet/signature_security_test.go @@ -0,0 +1,31 @@ +package packet + +import ( + "bytes" + "runtime" + "testing" +) + +func TestV6SignatureHugeSubpacketLength(t *testing.T) { + body := []byte{ + 0x06, // version 6 + 0x00, // signature type + 0x01, // pubkey algo + 0x08, // hash algo + 0x7f, 0xff, 0xff, 0xff, // hashed subpacket length (~2 GiB) + } + pkt := append([]byte{0xc0 | 2, byte(len(body))}, body...) + + var before, after runtime.MemStats + runtime.GC() + runtime.ReadMemStats(&before) + _, err := Read(bytes.NewReader(pkt)) + runtime.ReadMemStats(&after) + + if err == nil { + t.Fatal("expected an error for oversized hashed subpacket length, got nil") + } + if allocated := after.TotalAlloc - before.TotalAlloc; allocated > 10<<20 { + t.Fatalf("Read allocated %d bytes for a tiny packet declaring a huge subpacket length", allocated) + } +}