2018-11-11 Richard Biener <rguenther@suse.de>
[official-gcc.git] / libgo / go / encoding / pem / pem.go
blob35058c306bc75be8fc1c46b38656dcef2440edad
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package pem implements the PEM data encoding, which originated in Privacy
6 // Enhanced Mail. The most common use of PEM encoding today is in TLS keys and
7 // certificates. See RFC 1421.
8 package pem
10 import (
11 "bytes"
12 "encoding/base64"
13 "errors"
14 "io"
15 "sort"
16 "strings"
19 // A Block represents a PEM encoded structure.
21 // The encoded form is:
22 // -----BEGIN Type-----
23 // Headers
24 // base64-encoded Bytes
25 // -----END Type-----
26 // where Headers is a possibly empty sequence of Key: Value lines.
27 type Block struct {
28 Type string // The type, taken from the preamble (i.e. "RSA PRIVATE KEY").
29 Headers map[string]string // Optional headers.
30 Bytes []byte // The decoded bytes of the contents. Typically a DER encoded ASN.1 structure.
33 // getLine results the first \r\n or \n delineated line from the given byte
34 // array. The line does not include trailing whitespace or the trailing new
35 // line bytes. The remainder of the byte array (also not including the new line
36 // bytes) is also returned and this will always be smaller than the original
37 // argument.
38 func getLine(data []byte) (line, rest []byte) {
39 i := bytes.IndexByte(data, '\n')
40 var j int
41 if i < 0 {
42 i = len(data)
43 j = i
44 } else {
45 j = i + 1
46 if i > 0 && data[i-1] == '\r' {
47 i--
50 return bytes.TrimRight(data[0:i], " \t"), data[j:]
53 // removeWhitespace returns a copy of its input with all spaces, tab and
54 // newline characters removed.
55 func removeWhitespace(data []byte) []byte {
56 result := make([]byte, len(data))
57 n := 0
59 for _, b := range data {
60 if b == ' ' || b == '\t' || b == '\r' || b == '\n' {
61 continue
63 result[n] = b
64 n++
67 return result[0:n]
70 var pemStart = []byte("\n-----BEGIN ")
71 var pemEnd = []byte("\n-----END ")
72 var pemEndOfLine = []byte("-----")
74 // Decode will find the next PEM formatted block (certificate, private key
75 // etc) in the input. It returns that block and the remainder of the input. If
76 // no PEM data is found, p is nil and the whole of the input is returned in
77 // rest.
78 func Decode(data []byte) (p *Block, rest []byte) {
79 // pemStart begins with a newline. However, at the very beginning of
80 // the byte array, we'll accept the start string without it.
81 rest = data
82 if bytes.HasPrefix(data, pemStart[1:]) {
83 rest = rest[len(pemStart)-1 : len(data)]
84 } else if i := bytes.Index(data, pemStart); i >= 0 {
85 rest = rest[i+len(pemStart) : len(data)]
86 } else {
87 return nil, data
90 typeLine, rest := getLine(rest)
91 if !bytes.HasSuffix(typeLine, pemEndOfLine) {
92 return decodeError(data, rest)
94 typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
96 p = &Block{
97 Headers: make(map[string]string),
98 Type: string(typeLine),
101 for {
102 // This loop terminates because getLine's second result is
103 // always smaller than its argument.
104 if len(rest) == 0 {
105 return nil, data
107 line, next := getLine(rest)
109 i := bytes.IndexByte(line, ':')
110 if i == -1 {
111 break
114 // TODO(agl): need to cope with values that spread across lines.
115 key, val := line[:i], line[i+1:]
116 key = bytes.TrimSpace(key)
117 val = bytes.TrimSpace(val)
118 p.Headers[string(key)] = string(val)
119 rest = next
122 var endIndex, endTrailerIndex int
124 // If there were no headers, the END line might occur
125 // immediately, without a leading newline.
126 if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
127 endIndex = 0
128 endTrailerIndex = len(pemEnd) - 1
129 } else {
130 endIndex = bytes.Index(rest, pemEnd)
131 endTrailerIndex = endIndex + len(pemEnd)
134 if endIndex < 0 {
135 return decodeError(data, rest)
138 // After the "-----" of the ending line, there should be the same type
139 // and then a final five dashes.
140 endTrailer := rest[endTrailerIndex:]
141 endTrailerLen := len(typeLine) + len(pemEndOfLine)
142 if len(endTrailer) < endTrailerLen {
143 return decodeError(data, rest)
146 restOfEndLine := endTrailer[endTrailerLen:]
147 endTrailer = endTrailer[:endTrailerLen]
148 if !bytes.HasPrefix(endTrailer, typeLine) ||
149 !bytes.HasSuffix(endTrailer, pemEndOfLine) {
150 return decodeError(data, rest)
153 // The line must end with only whitespace.
154 if s, _ := getLine(restOfEndLine); len(s) != 0 {
155 return decodeError(data, rest)
158 base64Data := removeWhitespace(rest[:endIndex])
159 p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
160 n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
161 if err != nil {
162 return decodeError(data, rest)
164 p.Bytes = p.Bytes[:n]
166 // the -1 is because we might have only matched pemEnd without the
167 // leading newline if the PEM block was empty.
168 _, rest = getLine(rest[endIndex+len(pemEnd)-1:])
170 return
173 func decodeError(data, rest []byte) (*Block, []byte) {
174 // If we get here then we have rejected a likely looking, but
175 // ultimately invalid PEM block. We need to start over from a new
176 // position. We have consumed the preamble line and will have consumed
177 // any lines which could be header lines. However, a valid preamble
178 // line is not a valid header line, therefore we cannot have consumed
179 // the preamble line for the any subsequent block. Thus, we will always
180 // find any valid block, no matter what bytes precede it.
182 // For example, if the input is
184 // -----BEGIN MALFORMED BLOCK-----
185 // junk that may look like header lines
186 // or data lines, but no END line
188 // -----BEGIN ACTUAL BLOCK-----
189 // realdata
190 // -----END ACTUAL BLOCK-----
192 // we've failed to parse using the first BEGIN line
193 // and now will try again, using the second BEGIN line.
194 p, rest := Decode(rest)
195 if p == nil {
196 rest = data
198 return p, rest
201 const pemLineLength = 64
203 type lineBreaker struct {
204 line [pemLineLength]byte
205 used int
206 out io.Writer
209 var nl = []byte{'\n'}
211 func (l *lineBreaker) Write(b []byte) (n int, err error) {
212 if l.used+len(b) < pemLineLength {
213 copy(l.line[l.used:], b)
214 l.used += len(b)
215 return len(b), nil
218 n, err = l.out.Write(l.line[0:l.used])
219 if err != nil {
220 return
222 excess := pemLineLength - l.used
223 l.used = 0
225 n, err = l.out.Write(b[0:excess])
226 if err != nil {
227 return
230 n, err = l.out.Write(nl)
231 if err != nil {
232 return
235 return l.Write(b[excess:])
238 func (l *lineBreaker) Close() (err error) {
239 if l.used > 0 {
240 _, err = l.out.Write(l.line[0:l.used])
241 if err != nil {
242 return
244 _, err = l.out.Write(nl)
247 return
250 func writeHeader(out io.Writer, k, v string) error {
251 _, err := out.Write([]byte(k + ": " + v + "\n"))
252 return err
255 // Encode writes the PEM encoding of b to out.
256 func Encode(out io.Writer, b *Block) error {
257 // Check for invalid block before writing any output.
258 for k := range b.Headers {
259 if strings.Contains(k, ":") {
260 return errors.New("pem: cannot encode a header key that contains a colon")
264 // All errors below are relayed from underlying io.Writer,
265 // so it is now safe to write data.
267 if _, err := out.Write(pemStart[1:]); err != nil {
268 return err
270 if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
271 return err
274 if len(b.Headers) > 0 {
275 const procType = "Proc-Type"
276 h := make([]string, 0, len(b.Headers))
277 hasProcType := false
278 for k := range b.Headers {
279 if k == procType {
280 hasProcType = true
281 continue
283 h = append(h, k)
285 // The Proc-Type header must be written first.
286 // See RFC 1421, section 4.6.1.1
287 if hasProcType {
288 if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
289 return err
292 // For consistency of output, write other headers sorted by key.
293 sort.Strings(h)
294 for _, k := range h {
295 if err := writeHeader(out, k, b.Headers[k]); err != nil {
296 return err
299 if _, err := out.Write(nl); err != nil {
300 return err
304 var breaker lineBreaker
305 breaker.out = out
307 b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
308 if _, err := b64.Write(b.Bytes); err != nil {
309 return err
311 b64.Close()
312 breaker.Close()
314 if _, err := out.Write(pemEnd[1:]); err != nil {
315 return err
317 _, err := out.Write([]byte(b.Type + "-----\n"))
318 return err
321 // EncodeToMemory returns the PEM encoding of b.
323 // If b has invalid headers and cannot be encoded,
324 // EncodeToMemory returns nil. If it is important to
325 // report details about this error case, use Encode instead.
326 func EncodeToMemory(b *Block) []byte {
327 var buf bytes.Buffer
328 if err := Encode(&buf, b); err != nil {
329 return nil
331 return buf.Bytes()