Skip to content

Commit d664700

Browse files
committed
feat: Allow to set a decompressed size limit
1 parent e1ea40e commit d664700

File tree

7 files changed

+155
-3
lines changed

7 files changed

+155
-3
lines changed

openpgp/errors/errors.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,16 @@ func (dke ErrMalformedMessage) Error() string {
180180
return "openpgp: malformed message " + string(dke)
181181
}
182182

183+
type messageTooLargeError int
184+
185+
func (e messageTooLargeError) Error() string {
186+
return "openpgp: decompressed message size exceeds provided limit"
187+
}
188+
189+
// ErrMessageTooLarge is returned if the read data from
190+
// a compressed packet exceeds the provided limit.
191+
var ErrMessageTooLarge error = messageTooLargeError(0)
192+
183193
// ErrEncryptionKeySelection is returned if encryption key selection fails (v2 API).
184194
type ErrEncryptionKeySelection struct {
185195
PrimaryKeyId string

openpgp/packet/compressed.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ func (c *Compressed) parse(r io.Reader) error {
9898
return err
9999
}
100100

101+
// LimitedBodyReader wraps the provided body reader with a limiter that restricts
102+
// the number of bytes read to the specified limit.
103+
// If limit is nil, the reader is unbounded.
104+
func (c *Compressed) LimitedBodyReader(limit *int64) io.Reader {
105+
if limit == nil {
106+
return c.Body
107+
}
108+
return &LimitReader{R: c.Body, N: *limit}
109+
}
110+
101111
// compressedWriterCloser represents the serialized compression stream
102112
// header and the compressor. Its Close() method ensures that both the
103113
// compressor and serialized stream header are closed. Its Write()
@@ -159,3 +169,24 @@ func SerializeCompressed(w io.WriteCloser, algo CompressionAlgo, cc *Compression
159169

160170
return
161171
}
172+
173+
// LimitReader is an io.Reader that fails with MessageToLarge if read bytes exceed N.
174+
type LimitReader struct {
175+
R io.Reader // underlying reader
176+
N int64 // max bytes allowed
177+
}
178+
179+
func (l *LimitReader) Read(p []byte) (int, error) {
180+
if l.N <= 0 {
181+
return 0, errors.ErrMessageTooLarge
182+
}
183+
184+
n, err := l.R.Read(p)
185+
l.N -= int64(n)
186+
187+
if err == nil && l.N <= 0 {
188+
err = errors.ErrMessageTooLarge
189+
}
190+
191+
return n, err
192+
}

openpgp/packet/config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ type Config struct {
178178
// When set to true, a key without flags is treated as if all flags are enabled.
179179
// This behavior is consistent with GPG.
180180
InsecureAllowAllKeyFlagsWhenMissing bool
181+
182+
// MaxDecompressedMessageSize specifies the maximum number of bytes that can be
183+
// read from a compressed packet. This serves as an upper limit to prevent
184+
// excessively large decompressed messages.
185+
MaxDecompressedMessageSize *int64
181186
}
182187

183188
func (c *Config) Random() io.Reader {
@@ -415,6 +420,13 @@ func (c *Config) AllowAllKeyFlagsWhenMissing() bool {
415420
return c.InsecureAllowAllKeyFlagsWhenMissing
416421
}
417422

423+
func (c *Config) DecompressedMessageSizeLimit() *int64 {
424+
if c == nil {
425+
return nil
426+
}
427+
return c.MaxDecompressedMessageSize
428+
}
429+
418430
// BoolPointer is a helper function to set a boolean pointer in the Config.
419431
// e.g., config.CheckPacketSequence = BoolPointer(true)
420432
func BoolPointer(value bool) *bool {

openpgp/read.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ FindLiteralData:
259259
}
260260
switch p := p.(type) {
261261
case *packet.Compressed:
262-
if err := packets.Push(p.Body); err != nil {
262+
if err := packets.Push(p.LimitedBodyReader(config.DecompressedMessageSizeLimit())); err != nil {
263263
return nil, err
264264
}
265265
case *packet.OnePassSignature:

openpgp/v2/read.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ FindLiteralData:
364364
}
365365
switch p := p.(type) {
366366
case *packet.Compressed:
367-
if err := packets.Push(p.Body); err != nil {
367+
if err := packets.Push(p.LimitedBodyReader(config.DecompressedMessageSizeLimit())); err != nil {
368368
return nil, err
369369
}
370370
case *packet.OnePassSignature:

openpgp/v2/read_test.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"crypto/sha512"
1010
"encoding/base64"
1111
"encoding/hex"
12+
goerrors "errors"
1213
"io"
1314
"io/ioutil"
1415
"math/bits"
@@ -1056,8 +1057,52 @@ func TestReadMessageWithSignOnly(t *testing.T) {
10561057
t.Error(err)
10571058
return
10581059
}
1059-
md, err = ReadMessage(msgReader.Body, key, nil, nil)
1060+
_, err = ReadMessage(msgReader.Body, key, nil, nil)
10601061
if err == nil {
10611062
t.Fatal("Should not decrypt")
10621063
}
10631064
}
1065+
1066+
func TestReadMessageCompressionLimit(t *testing.T) {
1067+
// Limit 1KB
1068+
max := int64(1024)
1069+
config := packet.Config{
1070+
MaxDecompressedMessageSize: &max,
1071+
}
1072+
1073+
msgReader, err := armor.Decode(strings.NewReader(compressed2KB))
1074+
if err != nil {
1075+
t.Error(err)
1076+
return
1077+
}
1078+
md, err := ReadMessage(msgReader.Body, nil, nil, &config)
1079+
if err != nil {
1080+
t.Error(err)
1081+
return
1082+
}
1083+
// Should not be able to read all data
1084+
_, err = io.ReadAll(md.UnverifiedBody)
1085+
if !goerrors.Is(err, errors.ErrMessageTooLarge) {
1086+
t.Errorf("Wrong error")
1087+
}
1088+
1089+
// Limit 4KB
1090+
max = int64(4 * 1024)
1091+
config = packet.Config{
1092+
MaxDecompressedMessageSize: &max,
1093+
}
1094+
msgReader, err = armor.Decode(strings.NewReader(compressed2KB))
1095+
if err != nil {
1096+
t.Error(err)
1097+
return
1098+
}
1099+
md, err = ReadMessage(msgReader.Body, nil, nil, &config)
1100+
if err != nil {
1101+
t.Error(err)
1102+
return
1103+
}
1104+
// Should be able to read all data
1105+
if _, err = io.ReadAll(md.UnverifiedBody); err != nil {
1106+
t.Fatal(err)
1107+
}
1108+
}

openpgp/v2/read_write_test_data.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,3 +788,57 @@ U9I6AUkZWdcsueib9ghKDDy+HbUbf2kCJWUnuyeOCKqQifDb8bsLmdQY4Wb6
788788
EBeLgD8oZHVsH3NLjPakPw==
789789
=STqy
790790
-----END PGP MESSAGE-----`
791+
792+
const compressed2KB = `-----BEGIN PGP MESSAGE-----
793+
794+
yOsCeAEABAn79sQNAwAIAaNNfhjCDDG7AcvHRmIAAAAAAFRUVFRUVFRUVFRUVFRU
795+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
796+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
797+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
798+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
799+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
800+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
801+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
802+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
803+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
804+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
805+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
806+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
807+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
808+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
809+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
810+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
811+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
812+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
813+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
814+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
815+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
816+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
817+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
818+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
819+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
820+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
821+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
822+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
823+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
824+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
825+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
826+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
827+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
828+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
829+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
830+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
831+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
832+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
833+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
834+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
835+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRU
836+
VFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVMBVVFRUVFRUVFRUVFRU
837+
VFRUVFRUVFRUVFRUVFRUVFRUVFTCwCkEAAEIAF0Fgmgu3EMJEKNNfhjCDDG7NRQA
838+
AAAAABwAEHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmdpoIeCV9r1q63ur3kQ
839+
tmDhFiEEX7dLHQOx48sxvC+Ko01+GMIMMbsAAGXDA/4+XjyYlcW5AQlK4bP71f4J
840+
dmmg+ijVY48OvALy35BE/W68t+vrWmCaSsXwg1NIPqaHQGNkL6I1qjSkQAxe8K90
841+
Z9JTerD46t0XuA6/v+W0j4uG5frNxfZ2D2AoNBW0yu6wgo5MM4IA+PH0xhtj0xa3
842+
e9Jwn5aNHhCQpFB3y/FDXAEAAP//nNYTdw==
843+
=ETAD
844+
-----END PGP MESSAGE-----`

0 commit comments

Comments
 (0)