Skip to content

Commit 0983090

Browse files
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). 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>
1 parent 36863d6 commit 0983090

File tree

2 files changed

+105
-3
lines changed

2 files changed

+105
-3
lines changed

src/encoding/pem/pem.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,18 +91,23 @@ func Decode(data []byte) (p *Block, rest []byte) {
9191
// the byte array, we'll accept the start string without it.
9292
rest = data
9393

94+
endTrailerIndex := 0
9495
for {
96+
// If we've already tried parsing a block, skip past the END we already
97+
// saw.
98+
rest = rest[endTrailerIndex:]
99+
95100
// Find the first END line, and then find the last BEGIN line before
96101
// the end line. This lets us skip any repeated BEGIN lines that don't
97102
// have a matching END.
98103
endIndex := bytes.Index(rest, pemEnd)
99104
if endIndex < 0 {
100105
return nil, data
101106
}
102-
endTrailerIndex := endIndex + len(pemEnd)
107+
endTrailerIndex = endIndex + len(pemEnd)
103108
beginIndex := bytes.LastIndex(rest[:endIndex], pemStart[1:])
104-
if beginIndex < 0 || beginIndex > 0 && rest[beginIndex-1] != '\n' {
105-
return nil, data
109+
if beginIndex < 0 || (beginIndex > 0 && rest[beginIndex-1] != '\n') {
110+
continue
106111
}
107112
rest = rest[beginIndex+len(pemStart)-1:]
108113
endIndex -= beginIndex + len(pemStart) - 1

src/encoding/pem/pem_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,3 +639,100 @@ func TestBadEncode(t *testing.T) {
639639
}
640640

641641
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
642+
643+
func TestDecodeStrangeCases(t *testing.T) {
644+
sentinelType := "TEST BLOCK"
645+
sentinelBytes := []byte("hello")
646+
for _, tc := range []struct {
647+
name string
648+
pem string
649+
}{
650+
{
651+
name: "invalid section (not base64)",
652+
pem: `-----BEGIN COMMENT-----
653+
foo foo foo
654+
-----END COMMENT-----
655+
-----BEGIN TEST BLOCK-----
656+
aGVsbG8=
657+
-----END TEST BLOCK-----`,
658+
},
659+
{
660+
name: "leading garbage on block",
661+
pem: `foo foo foo-----BEGIN CERTIFICATE-----
662+
MCowBQYDK2VwAyEApVjJeLW5MoP6uR3+OeITokM+rBDng6dgl1vvhcy+wws=
663+
-----END PUBLIC KEY-----
664+
-----BEGIN TEST BLOCK-----
665+
aGVsbG8=
666+
-----END TEST BLOCK-----`,
667+
},
668+
{
669+
name: "leading garbage",
670+
pem: `foo foo foo
671+
-----BEGIN TEST BLOCK-----
672+
aGVsbG8=
673+
-----END TEST BLOCK-----`,
674+
},
675+
{
676+
name: "leading partial block",
677+
pem: `foo foo foo
678+
-----END COMMENT-----
679+
-----BEGIN TEST BLOCK-----
680+
aGVsbG8=
681+
-----END TEST BLOCK-----`,
682+
},
683+
{
684+
name: "multiple BEGIN",
685+
pem: `-----BEGIN TEST BLOCK-----
686+
-----BEGIN TEST BLOCK-----
687+
-----BEGIN TEST BLOCK-----
688+
aGVsbG8=
689+
-----END TEST BLOCK-----`,
690+
},
691+
{
692+
name: "multiple END",
693+
pem: `-----BEGIN TEST BLOCK-----
694+
aGVsbG8=
695+
-----END TEST BLOCK-----
696+
-----END TEST BLOCK-----
697+
-----END TEST BLOCK-----`,
698+
},
699+
{
700+
name: "leading malformed BEGIN",
701+
pem: `-----BEGIN PUBLIC KEY
702+
aGVsbG8=
703+
-----END PUBLIC KEY-----
704+
-----BEGIN TEST BLOCK-----
705+
aGVsbG8=
706+
-----END TEST BLOCK-----`,
707+
},
708+
} {
709+
t.Run(tc.name, func(t *testing.T) {
710+
block, _ := Decode([]byte(tc.pem))
711+
if block == nil {
712+
t.Fatal("expected valid block")
713+
}
714+
if block.Type != sentinelType {
715+
t.Fatalf("unexpected block returned, got type %q, want type %q", block.Type, sentinelType)
716+
}
717+
if !bytes.Equal(block.Bytes, sentinelBytes) {
718+
t.Fatalf("unexpected block content, got %x, want %x", block.Bytes, sentinelBytes)
719+
}
720+
})
721+
}
722+
}
723+
724+
func TestJustEnd(t *testing.T) {
725+
pemData := `
726+
-----END PUBLIC KEY-----`
727+
728+
block, _ := Decode([]byte(pemData))
729+
if block != nil {
730+
t.Fatal("unexpected block")
731+
}
732+
}
733+
734+
func FuzzDecode(f *testing.F) {
735+
f.Fuzz(func(t *testing.T, data []byte) {
736+
Decode(data)
737+
})
738+
}

0 commit comments

Comments
 (0)