Skip to content

encoding/pem: PEM multiline headers #43580

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 118 additions & 14 deletions src/encoding/pem/pem.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func Decode(data []byte) (p *Block, rest []byte) {
Type: string(typeLine),
}

var prev_key string
for {
// This loop terminates because getLine's second result is
// always smaller than its argument.
Expand All @@ -114,17 +115,23 @@ func Decode(data []byte) (p *Block, rest []byte) {
}
line, next := getLine(rest)

i := bytes.IndexByte(line, ':')
if i == -1 {
break
}
if len(line) > 1 && (line[0] == 0x20 || line[0] == 0x9) && prev_key != "" {
// Joins values that spread across lines.
p.Headers[prev_key] = p.Headers[prev_key] + string(bytes.TrimSpace(line))
rest = next
} else {
i := bytes.IndexByte(line, ':')
if i == -1 {
break
}

// TODO(agl): need to cope with values that spread across lines.
key, val := line[:i], line[i+1:]
key = bytes.TrimSpace(key)
val = bytes.TrimSpace(val)
p.Headers[string(key)] = string(val)
rest = next
key, val := line[:i], line[i+1:]
key = bytes.TrimSpace(key)
val = bytes.TrimSpace(val)
p.Headers[string(key)] = string(val)
prev_key = string(key)
rest = next
}
}

var endIndex, endTrailerIndex int
Expand Down Expand Up @@ -212,17 +219,28 @@ type lineBreaker struct {
line [pemLineLength]byte
used int
out io.Writer
pad bool
}

var nl = []byte{'\n'}
var sp = []byte{' '}

func (l *lineBreaker) Write(b []byte) (n int, err error) {
// Write headers and PEM block. RFC1421
if l.used+len(b) < pemLineLength {
copy(l.line[l.used:], b)
l.used += len(b)
return len(b), nil
}

if l.pad {
// Make sure to add a pad if we need to.
_, err = l.out.Write(sp)
if err != nil {
return
}
}

n, err = l.out.Write(l.line[0:l.used])
if err != nil {
return
Expand All @@ -245,19 +263,105 @@ func (l *lineBreaker) Write(b []byte) (n int, err error) {

func (l *lineBreaker) Close() (err error) {
if l.used > 0 {
if l.pad {
// Make sure to add a pad if we need to.
_, err = l.out.Write(sp)
if err != nil {
return
}
}

_, err = l.out.Write(l.line[0:l.used])
if err != nil {
return
}
_, err = l.out.Write(nl)
}

return
}

func writeHeader(out io.Writer, k, v string) error {
_, err := out.Write([]byte(k + ": " + v + "\n"))
return err
func writeHeader(out io.Writer, k, v string) (err error) {
// This follows the output specified by RFC1421

if len(k)+len(v)+2 < pemLineLength {
// Write header with key and value on one line.
_, err = out.Write([]byte(k + ": " + v + "\n"))

} else {
// Write values that spread across lines.
_, err = out.Write([]byte(k + ": "))
if err != nil {
return
}

used := len(k) + 2
var blob string

for len(v) > 0 {
i := bytes.IndexByte([]byte(v), ',')
if i == -1 {
i = len(v)
blob = v
v = v[:0]
} else {
i++
blob = v[:i]
v = v[i:]
}
if used == 0 {
_, err = out.Write(sp)
if err != nil {
return
}
used++
}

if used+i <= pemLineLength {
// If we can fit it on the rest of the line write the blob.
_, err = out.Write([]byte(blob))
if err != nil {
return
}
used += i
} else if i > pemLineLength {
// PEM / long blocks, should have their own section with the same length
// as the usual PEM, start on a new line, and have a pad space in front.
// The reason behind this is that they can be converted back into
// a standard PEM just by removing the padding and any trailing ','.
_, err = out.Write(nl)
if err != nil {
return
}
// Create a new lineBreaker writer.
breaker := &lineBreaker{pad: true}
breaker.out = out

// Block out the rest and call it done.
_, err = breaker.Write([]byte(blob))
if err != nil {
return
}
err = breaker.Close()
if err != nil {
return
}
used = 0
} else {
_, err = out.Write([]byte("\n " + blob))
if err != nil {
return
}
used = i
}
}
if used > 0 {
_, err = out.Write(nl)
if err != nil {
return
}
}
}
return
}

// Encode writes the PEM encoding of b to out.
Expand Down