]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.24] encoding/pem: properly decode strange PEM data
authorRoland Shoemaker <roland@golang.org>
Wed, 15 Oct 2025 17:45:04 +0000 (10:45 -0700)
committerDavid Chase <drchase@google.com>
Fri, 24 Oct 2025 18:03:15 +0000 (11:03 -0700)
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>
src/encoding/pem/pem.go
src/encoding/pem/pem_test.go

index 8ad964a4b2519e3a98e0eea4b0c594e7917643b6..53e0069709d523fb6ab2f9493d66fae038425111 100644 (file)
@@ -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
index 2c9b3eabcd1c12a78fe89266f88abd60e6deeef0..5bdc2f66a7b3abb377ec0680b89bfc74e9cccfb1 100644 (file)
@@ -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)
+       })
+}