// line bytes. The remainder of the byte array (also not including the new line
// bytes) is also returned and this will always be smaller than the original
// argument.
-func getLine(data []byte) (line, rest []byte) {
+func getLine(data []byte) (line, rest []byte, consumed int) {
i := bytes.IndexByte(data, '\n')
var j int
if i < 0 {
i--
}
}
- return bytes.TrimRight(data[0:i], " \t"), data[j:]
+ return bytes.TrimRight(data[0:i], " \t"), data[j:], j
}
// removeSpacesAndTabs returns a copy of its input with all spaces and tabs
// pemStart begins with a newline. However, at the very beginning of
// the byte array, we'll accept the start string without it.
rest = data
+
for {
- if bytes.HasPrefix(rest, pemStart[1:]) {
- rest = rest[len(pemStart)-1:]
- } else if _, after, ok := bytes.Cut(rest, pemStart); ok {
- rest = after
- } else {
+ // 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.
+ endIndex := bytes.Index(rest, pemEnd)
+ if endIndex < 0 {
+ return nil, data
+ }
+ endTrailerIndex := endIndex + len(pemEnd)
+ beginIndex := bytes.LastIndex(rest[:endIndex], pemStart[1:])
+ if beginIndex < 0 || beginIndex > 0 && rest[beginIndex-1] != '\n' {
return nil, data
}
+ rest = rest[beginIndex+len(pemStart)-1:]
+ endIndex -= beginIndex + len(pemStart) - 1
+ endTrailerIndex -= beginIndex + len(pemStart) - 1
var typeLine []byte
- typeLine, rest = getLine(rest)
+ var consumed int
+ typeLine, rest, consumed = getLine(rest)
if !bytes.HasSuffix(typeLine, pemEndOfLine) {
continue
}
+ endIndex -= consumed
+ endTrailerIndex -= consumed
typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
p = &Block{
if len(rest) == 0 {
return nil, data
}
- line, next := getLine(rest)
+ line, next, consumed := getLine(rest)
key, val, ok := bytes.Cut(line, colon)
if !ok {
val = bytes.TrimSpace(val)
p.Headers[string(key)] = string(val)
rest = next
+ endIndex -= consumed
+ endTrailerIndex -= consumed
}
- var endIndex, endTrailerIndex int
-
- // If there were no headers, the END line might occur
- // immediately, without a leading newline.
- if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
- endIndex = 0
- endTrailerIndex = len(pemEnd) - 1
- } else {
- endIndex = bytes.Index(rest, pemEnd)
- endTrailerIndex = endIndex + len(pemEnd)
- }
-
- if endIndex < 0 {
+ // If there were headers, there must be a newline between the headers
+ // and the END line, so endIndex should be >= 0.
+ if len(p.Headers) > 0 && endIndex < 0 {
continue
}
}
// The line must end with only whitespace.
- if s, _ := getLine(restOfEndLine); len(s) != 0 {
+ if s, _, _ := getLine(restOfEndLine); len(s) != 0 {
continue
}
- base64Data := removeSpacesAndTabs(rest[:endIndex])
- p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
- n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
- if err != nil {
- continue
+ p.Bytes = []byte{}
+ if endIndex > 0 {
+ base64Data := removeSpacesAndTabs(rest[:endIndex])
+ p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
+ n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
+ if err != nil {
+ continue
+ }
+ p.Bytes = p.Bytes[:n]
}
- p.Bytes = p.Bytes[:n]
// the -1 is because we might have only matched pemEnd without the
// leading newline if the PEM block was empty.
- _, rest = getLine(rest[endIndex+len(pemEnd)-1:])
+ _, rest, _ = getLine(rest[endIndex+len(pemEnd)-1:])
return p, rest
}
}
func TestGetLine(t *testing.T) {
for i, test := range getLineTests {
- x, y := getLine([]byte(test.in))
+ x, y, _ := getLine([]byte(test.in))
if string(x) != test.out1 || string(y) != test.out2 {
t.Errorf("#%d got:%+v,%+v want:%s,%s", i, x, y, test.out1, test.out2)
}
if !reflect.DeepEqual(result, certificate) {
t.Errorf("#0 got:%#v want:%#v", result, certificate)
}
+
result, remainder = Decode(remainder)
if !reflect.DeepEqual(result, privateKey) {
t.Errorf("#1 got:%#v want:%#v", result, privateKey)
}
result, remainder = Decode(remainder)
- if result == nil || result.Type != "HEADERS" || len(result.Headers) != 1 {
+ if result == nil || result.Type != "VALID HEADERS" || len(result.Headers) != 1 {
t.Errorf("#5 expected single header block but got :%v", result)
}
# This shouldn't be recognised because of the missing newline after the
headers.
------BEGIN HEADERS-----
+-----BEGIN INVALID HEADERS-----
Header: 1
------END HEADERS-----
+-----END INVALID HEADERS-----
# This should be valid, however.
------BEGIN HEADERS-----
+-----BEGIN VALID HEADERS-----
Header: 1
------END HEADERS-----`)
+-----END VALID HEADERS-----`)
var certificate = &Block{Type: "CERTIFICATE",
Headers: map[string]string{},