diff options
| author | Roland Shoemaker <roland@golang.org> | 2025-10-15 10:45:04 -0700 |
|---|---|---|
| committer | David Chase <drchase@google.com> | 2025-10-24 11:03:15 -0700 |
| commit | bbf8f423abc9f92f4c1c5d98281ed0b21107b00d (patch) | |
| tree | 464a1eb424967124610f108cc9c9493c90b02564 | |
| parent | a216ddc295c3e983f78a7ff911ae20ca147b09b7 (diff) | |
| download | go-bbf8f423abc9f92f4c1c5d98281ed0b21107b00d.tar.xz | |
[release-branch.go1.24] encoding/pem: properly decode strange PEM data
When the passed byte slice has leading garbage, properly handle ignoring
it and continuing to parse the slice until we find a valid block (or
nothing).
Fixes #75951
Change-Id: I07e937d9c754fd71b028b99450b48f57b4464457
Reviewed-on: https://go-review.googlesource.com/c/go/+/712140
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
(cherry picked from commit 09830901714d8b3a2cc5fb33e87a81886b21ea24)
Reviewed-on: https://go-review.googlesource.com/c/go/+/712641
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
| -rw-r--r-- | src/encoding/pem/pem.go | 11 | ||||
| -rw-r--r-- | src/encoding/pem/pem_test.go | 97 |
2 files changed, 105 insertions, 3 deletions
diff --git a/src/encoding/pem/pem.go b/src/encoding/pem/pem.go index 8ad964a4b2..53e0069709 100644 --- a/src/encoding/pem/pem.go +++ b/src/encoding/pem/pem.go @@ -91,7 +91,12 @@ func Decode(data []byte) (p *Block, rest []byte) { // the byte array, we'll accept the start string without it. rest = data + endTrailerIndex := 0 for { + // If we've already tried parsing a block, skip past the END we already + // saw. + rest = rest[endTrailerIndex:] + // Find the first END line, and then find the last BEGIN line before // the end line. This lets us skip any repeated BEGIN lines that don't // have a matching END. @@ -99,10 +104,10 @@ func Decode(data []byte) (p *Block, rest []byte) { if endIndex < 0 { return nil, data } - endTrailerIndex := endIndex + len(pemEnd) + endTrailerIndex = endIndex + len(pemEnd) beginIndex := bytes.LastIndex(rest[:endIndex], pemStart[1:]) - if beginIndex < 0 || beginIndex > 0 && rest[beginIndex-1] != '\n' { - return nil, data + if beginIndex < 0 || (beginIndex > 0 && rest[beginIndex-1] != '\n') { + continue } rest = rest[beginIndex+len(pemStart)-1:] endIndex -= beginIndex + len(pemStart) - 1 diff --git a/src/encoding/pem/pem_test.go b/src/encoding/pem/pem_test.go index 2c9b3eabcd..5bdc2f66a7 100644 --- a/src/encoding/pem/pem_test.go +++ b/src/encoding/pem/pem_test.go @@ -639,3 +639,100 @@ func TestBadEncode(t *testing.T) { } func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") } + +func TestDecodeStrangeCases(t *testing.T) { + sentinelType := "TEST BLOCK" + sentinelBytes := []byte("hello") + for _, tc := range []struct { + name string + pem string + }{ + { + name: "invalid section (not base64)", + pem: `-----BEGIN COMMENT----- +foo foo foo +-----END COMMENT----- +-----BEGIN TEST BLOCK----- +aGVsbG8= +-----END TEST BLOCK-----`, + }, + { + name: "leading garbage on block", + pem: `foo foo foo-----BEGIN CERTIFICATE----- +MCowBQYDK2VwAyEApVjJeLW5MoP6uR3+OeITokM+rBDng6dgl1vvhcy+wws= +-----END PUBLIC KEY----- +-----BEGIN TEST BLOCK----- +aGVsbG8= +-----END TEST BLOCK-----`, + }, + { + name: "leading garbage", + pem: `foo foo foo +-----BEGIN TEST BLOCK----- +aGVsbG8= +-----END TEST BLOCK-----`, + }, + { + name: "leading partial block", + pem: `foo foo foo +-----END COMMENT----- +-----BEGIN TEST BLOCK----- +aGVsbG8= +-----END TEST BLOCK-----`, + }, + { + name: "multiple BEGIN", + pem: `-----BEGIN TEST BLOCK----- +-----BEGIN TEST BLOCK----- +-----BEGIN TEST BLOCK----- +aGVsbG8= +-----END TEST BLOCK-----`, + }, + { + name: "multiple END", + pem: `-----BEGIN TEST BLOCK----- +aGVsbG8= +-----END TEST BLOCK----- +-----END TEST BLOCK----- +-----END TEST BLOCK-----`, + }, + { + name: "leading malformed BEGIN", + pem: `-----BEGIN PUBLIC KEY +aGVsbG8= +-----END PUBLIC KEY----- +-----BEGIN TEST BLOCK----- +aGVsbG8= +-----END TEST BLOCK-----`, + }, + } { + t.Run(tc.name, func(t *testing.T) { + block, _ := Decode([]byte(tc.pem)) + if block == nil { + t.Fatal("expected valid block") + } + if block.Type != sentinelType { + t.Fatalf("unexpected block returned, got type %q, want type %q", block.Type, sentinelType) + } + if !bytes.Equal(block.Bytes, sentinelBytes) { + t.Fatalf("unexpected block content, got %x, want %x", block.Bytes, sentinelBytes) + } + }) + } +} + +func TestJustEnd(t *testing.T) { + pemData := ` +-----END PUBLIC KEY-----` + + block, _ := Decode([]byte(pemData)) + if block != nil { + t.Fatal("unexpected block") + } +} + +func FuzzDecode(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + Decode(data) + }) +} |
