Source file src/encoding/pem/pem.go

Documentation: encoding/pem

     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.
     4  
     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
     9  
    10  import (
    11  	"bytes"
    12  	"encoding/base64"
    13  	"errors"
    14  	"io"
    15  	"sort"
    16  	"strings"
    17  )
    18  
    19  // A Block represents a PEM encoded structure.
    20  //
    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.
    31  }
    32  
    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--
    48  		}
    49  	}
    50  	return bytes.TrimRight(data[0:i], " \t"), data[j:]
    51  }
    52  
    53  // removeSpacesAndTabs returns a copy of its input with all spaces and tabs
    54  // removed, if there were any. Otherwise, the input is returned unchanged.
    55  //
    56  // The base64 decoder already skips newline characters, so we don't need to
    57  // filter them out here.
    58  func removeSpacesAndTabs(data []byte) []byte {
    59  	if !bytes.ContainsAny(data, " \t") {
    60  		// Fast path; most base64 data within PEM contains newlines, but
    61  		// no spaces nor tabs. Skip the extra alloc and work.
    62  		return data
    63  	}
    64  	result := make([]byte, len(data))
    65  	n := 0
    66  
    67  	for _, b := range data {
    68  		if b == ' ' || b == '\t' {
    69  			continue
    70  		}
    71  		result[n] = b
    72  		n++
    73  	}
    74  
    75  	return result[0:n]
    76  }
    77  
    78  var pemStart = []byte("\n-----BEGIN ")
    79  var pemEnd = []byte("\n-----END ")
    80  var pemEndOfLine = []byte("-----")
    81  
    82  // Decode will find the next PEM formatted block (certificate, private key
    83  // etc) in the input. It returns that block and the remainder of the input. If
    84  // no PEM data is found, p is nil and the whole of the input is returned in
    85  // rest.
    86  func Decode(data []byte) (p *Block, rest []byte) {
    87  	// pemStart begins with a newline. However, at the very beginning of
    88  	// the byte array, we'll accept the start string without it.
    89  	rest = data
    90  	if bytes.HasPrefix(data, pemStart[1:]) {
    91  		rest = rest[len(pemStart)-1 : len(data)]
    92  	} else if i := bytes.Index(data, pemStart); i >= 0 {
    93  		rest = rest[i+len(pemStart) : len(data)]
    94  	} else {
    95  		return nil, data
    96  	}
    97  
    98  	typeLine, rest := getLine(rest)
    99  	if !bytes.HasSuffix(typeLine, pemEndOfLine) {
   100  		return decodeError(data, rest)
   101  	}
   102  	typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
   103  
   104  	p = &Block{
   105  		Headers: make(map[string]string),
   106  		Type:    string(typeLine),
   107  	}
   108  
   109  	for {
   110  		// This loop terminates because getLine's second result is
   111  		// always smaller than its argument.
   112  		if len(rest) == 0 {
   113  			return nil, data
   114  		}
   115  		line, next := getLine(rest)
   116  
   117  		i := bytes.IndexByte(line, ':')
   118  		if i == -1 {
   119  			break
   120  		}
   121  
   122  		// TODO(agl): need to cope with values that spread across lines.
   123  		key, val := line[:i], line[i+1:]
   124  		key = bytes.TrimSpace(key)
   125  		val = bytes.TrimSpace(val)
   126  		p.Headers[string(key)] = string(val)
   127  		rest = next
   128  	}
   129  
   130  	var endIndex, endTrailerIndex int
   131  
   132  	// If there were no headers, the END line might occur
   133  	// immediately, without a leading newline.
   134  	if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
   135  		endIndex = 0
   136  		endTrailerIndex = len(pemEnd) - 1
   137  	} else {
   138  		endIndex = bytes.Index(rest, pemEnd)
   139  		endTrailerIndex = endIndex + len(pemEnd)
   140  	}
   141  
   142  	if endIndex < 0 {
   143  		return decodeError(data, rest)
   144  	}
   145  
   146  	// After the "-----" of the ending line, there should be the same type
   147  	// and then a final five dashes.
   148  	endTrailer := rest[endTrailerIndex:]
   149  	endTrailerLen := len(typeLine) + len(pemEndOfLine)
   150  	if len(endTrailer) < endTrailerLen {
   151  		return decodeError(data, rest)
   152  	}
   153  
   154  	restOfEndLine := endTrailer[endTrailerLen:]
   155  	endTrailer = endTrailer[:endTrailerLen]
   156  	if !bytes.HasPrefix(endTrailer, typeLine) ||
   157  		!bytes.HasSuffix(endTrailer, pemEndOfLine) {
   158  		return decodeError(data, rest)
   159  	}
   160  
   161  	// The line must end with only whitespace.
   162  	if s, _ := getLine(restOfEndLine); len(s) != 0 {
   163  		return decodeError(data, rest)
   164  	}
   165  
   166  	base64Data := removeSpacesAndTabs(rest[:endIndex])
   167  	p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
   168  	n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
   169  	if err != nil {
   170  		return decodeError(data, rest)
   171  	}
   172  	p.Bytes = p.Bytes[:n]
   173  
   174  	// the -1 is because we might have only matched pemEnd without the
   175  	// leading newline if the PEM block was empty.
   176  	_, rest = getLine(rest[endIndex+len(pemEnd)-1:])
   177  
   178  	return
   179  }
   180  
   181  func decodeError(data, rest []byte) (*Block, []byte) {
   182  	// If we get here then we have rejected a likely looking, but
   183  	// ultimately invalid PEM block. We need to start over from a new
   184  	// position. We have consumed the preamble line and will have consumed
   185  	// any lines which could be header lines. However, a valid preamble
   186  	// line is not a valid header line, therefore we cannot have consumed
   187  	// the preamble line for the any subsequent block. Thus, we will always
   188  	// find any valid block, no matter what bytes precede it.
   189  	//
   190  	// For example, if the input is
   191  	//
   192  	//    -----BEGIN MALFORMED BLOCK-----
   193  	//    junk that may look like header lines
   194  	//   or data lines, but no END line
   195  	//
   196  	//    -----BEGIN ACTUAL BLOCK-----
   197  	//    realdata
   198  	//    -----END ACTUAL BLOCK-----
   199  	//
   200  	// we've failed to parse using the first BEGIN line
   201  	// and now will try again, using the second BEGIN line.
   202  	p, rest := Decode(rest)
   203  	if p == nil {
   204  		rest = data
   205  	}
   206  	return p, rest
   207  }
   208  
   209  const pemLineLength = 64
   210  
   211  type lineBreaker struct {
   212  	line [pemLineLength]byte
   213  	used int
   214  	out  io.Writer
   215  }
   216  
   217  var nl = []byte{'\n'}
   218  
   219  func (l *lineBreaker) Write(b []byte) (n int, err error) {
   220  	if l.used+len(b) < pemLineLength {
   221  		copy(l.line[l.used:], b)
   222  		l.used += len(b)
   223  		return len(b), nil
   224  	}
   225  
   226  	n, err = l.out.Write(l.line[0:l.used])
   227  	if err != nil {
   228  		return
   229  	}
   230  	excess := pemLineLength - l.used
   231  	l.used = 0
   232  
   233  	n, err = l.out.Write(b[0:excess])
   234  	if err != nil {
   235  		return
   236  	}
   237  
   238  	n, err = l.out.Write(nl)
   239  	if err != nil {
   240  		return
   241  	}
   242  
   243  	return l.Write(b[excess:])
   244  }
   245  
   246  func (l *lineBreaker) Close() (err error) {
   247  	if l.used > 0 {
   248  		_, err = l.out.Write(l.line[0:l.used])
   249  		if err != nil {
   250  			return
   251  		}
   252  		_, err = l.out.Write(nl)
   253  	}
   254  
   255  	return
   256  }
   257  
   258  func writeHeader(out io.Writer, k, v string) error {
   259  	_, err := out.Write([]byte(k + ": " + v + "\n"))
   260  	return err
   261  }
   262  
   263  // Encode writes the PEM encoding of b to out.
   264  func Encode(out io.Writer, b *Block) error {
   265  	// Check for invalid block before writing any output.
   266  	for k := range b.Headers {
   267  		if strings.Contains(k, ":") {
   268  			return errors.New("pem: cannot encode a header key that contains a colon")
   269  		}
   270  	}
   271  
   272  	// All errors below are relayed from underlying io.Writer,
   273  	// so it is now safe to write data.
   274  
   275  	if _, err := out.Write(pemStart[1:]); err != nil {
   276  		return err
   277  	}
   278  	if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
   279  		return err
   280  	}
   281  
   282  	if len(b.Headers) > 0 {
   283  		const procType = "Proc-Type"
   284  		h := make([]string, 0, len(b.Headers))
   285  		hasProcType := false
   286  		for k := range b.Headers {
   287  			if k == procType {
   288  				hasProcType = true
   289  				continue
   290  			}
   291  			h = append(h, k)
   292  		}
   293  		// The Proc-Type header must be written first.
   294  		// See RFC 1421, section 4.6.1.1
   295  		if hasProcType {
   296  			if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
   297  				return err
   298  			}
   299  		}
   300  		// For consistency of output, write other headers sorted by key.
   301  		sort.Strings(h)
   302  		for _, k := range h {
   303  			if err := writeHeader(out, k, b.Headers[k]); err != nil {
   304  				return err
   305  			}
   306  		}
   307  		if _, err := out.Write(nl); err != nil {
   308  			return err
   309  		}
   310  	}
   311  
   312  	var breaker lineBreaker
   313  	breaker.out = out
   314  
   315  	b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
   316  	if _, err := b64.Write(b.Bytes); err != nil {
   317  		return err
   318  	}
   319  	b64.Close()
   320  	breaker.Close()
   321  
   322  	if _, err := out.Write(pemEnd[1:]); err != nil {
   323  		return err
   324  	}
   325  	_, err := out.Write([]byte(b.Type + "-----\n"))
   326  	return err
   327  }
   328  
   329  // EncodeToMemory returns the PEM encoding of b.
   330  //
   331  // If b has invalid headers and cannot be encoded,
   332  // EncodeToMemory returns nil. If it is important to
   333  // report details about this error case, use Encode instead.
   334  func EncodeToMemory(b *Block) []byte {
   335  	var buf bytes.Buffer
   336  	if err := Encode(&buf, b); err != nil {
   337  		return nil
   338  	}
   339  	return buf.Bytes()
   340  }
   341  

View as plain text