Source file src/encoding/pem/pem.go

     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  var colon = []byte(":")
    82  
    83  // Decode will find the next PEM formatted block (certificate, private key
    84  // etc) in the input. It returns that block and the remainder of the input. If
    85  // no PEM data is found, p is nil and the whole of the input is returned in
    86  // rest.
    87  func Decode(data []byte) (p *Block, rest []byte) {
    88  	// pemStart begins with a newline. However, at the very beginning of
    89  	// the byte array, we'll accept the start string without it.
    90  	rest = data
    91  	for {
    92  		if bytes.HasPrefix(rest, pemStart[1:]) {
    93  			rest = rest[len(pemStart)-1:]
    94  		} else if _, after, ok := bytes.Cut(rest, pemStart); ok {
    95  			rest = after
    96  		} else {
    97  			return nil, data
    98  		}
    99  
   100  		var typeLine []byte
   101  		typeLine, rest = getLine(rest)
   102  		if !bytes.HasSuffix(typeLine, pemEndOfLine) {
   103  			continue
   104  		}
   105  		typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
   106  
   107  		p = &Block{
   108  			Headers: make(map[string]string),
   109  			Type:    string(typeLine),
   110  		}
   111  
   112  		for {
   113  			// This loop terminates because getLine's second result is
   114  			// always smaller than its argument.
   115  			if len(rest) == 0 {
   116  				return nil, data
   117  			}
   118  			line, next := getLine(rest)
   119  
   120  			key, val, ok := bytes.Cut(line, colon)
   121  			if !ok {
   122  				break
   123  			}
   124  
   125  			// TODO(agl): need to cope with values that spread across lines.
   126  			key = bytes.TrimSpace(key)
   127  			val = bytes.TrimSpace(val)
   128  			p.Headers[string(key)] = string(val)
   129  			rest = next
   130  		}
   131  
   132  		var endIndex, endTrailerIndex int
   133  
   134  		// If there were no headers, the END line might occur
   135  		// immediately, without a leading newline.
   136  		if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
   137  			endIndex = 0
   138  			endTrailerIndex = len(pemEnd) - 1
   139  		} else {
   140  			endIndex = bytes.Index(rest, pemEnd)
   141  			endTrailerIndex = endIndex + len(pemEnd)
   142  		}
   143  
   144  		if endIndex < 0 {
   145  			continue
   146  		}
   147  
   148  		// After the "-----" of the ending line, there should be the same type
   149  		// and then a final five dashes.
   150  		endTrailer := rest[endTrailerIndex:]
   151  		endTrailerLen := len(typeLine) + len(pemEndOfLine)
   152  		if len(endTrailer) < endTrailerLen {
   153  			continue
   154  		}
   155  
   156  		restOfEndLine := endTrailer[endTrailerLen:]
   157  		endTrailer = endTrailer[:endTrailerLen]
   158  		if !bytes.HasPrefix(endTrailer, typeLine) ||
   159  			!bytes.HasSuffix(endTrailer, pemEndOfLine) {
   160  			continue
   161  		}
   162  
   163  		// The line must end with only whitespace.
   164  		if s, _ := getLine(restOfEndLine); len(s) != 0 {
   165  			continue
   166  		}
   167  
   168  		base64Data := removeSpacesAndTabs(rest[:endIndex])
   169  		p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
   170  		n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
   171  		if err != nil {
   172  			continue
   173  		}
   174  		p.Bytes = p.Bytes[:n]
   175  
   176  		// the -1 is because we might have only matched pemEnd without the
   177  		// leading newline if the PEM block was empty.
   178  		_, rest = getLine(rest[endIndex+len(pemEnd)-1:])
   179  		return p, rest
   180  	}
   181  }
   182  
   183  const pemLineLength = 64
   184  
   185  type lineBreaker struct {
   186  	line [pemLineLength]byte
   187  	used int
   188  	out  io.Writer
   189  }
   190  
   191  var nl = []byte{'\n'}
   192  
   193  func (l *lineBreaker) Write(b []byte) (n int, err error) {
   194  	if l.used+len(b) < pemLineLength {
   195  		copy(l.line[l.used:], b)
   196  		l.used += len(b)
   197  		return len(b), nil
   198  	}
   199  
   200  	n, err = l.out.Write(l.line[0:l.used])
   201  	if err != nil {
   202  		return
   203  	}
   204  	excess := pemLineLength - l.used
   205  	l.used = 0
   206  
   207  	n, err = l.out.Write(b[0:excess])
   208  	if err != nil {
   209  		return
   210  	}
   211  
   212  	n, err = l.out.Write(nl)
   213  	if err != nil {
   214  		return
   215  	}
   216  
   217  	return l.Write(b[excess:])
   218  }
   219  
   220  func (l *lineBreaker) Close() (err error) {
   221  	if l.used > 0 {
   222  		_, err = l.out.Write(l.line[0:l.used])
   223  		if err != nil {
   224  			return
   225  		}
   226  		_, err = l.out.Write(nl)
   227  	}
   228  
   229  	return
   230  }
   231  
   232  func writeHeader(out io.Writer, k, v string) error {
   233  	_, err := out.Write([]byte(k + ": " + v + "\n"))
   234  	return err
   235  }
   236  
   237  // Encode writes the PEM encoding of b to out.
   238  func Encode(out io.Writer, b *Block) error {
   239  	// Check for invalid block before writing any output.
   240  	for k := range b.Headers {
   241  		if strings.Contains(k, ":") {
   242  			return errors.New("pem: cannot encode a header key that contains a colon")
   243  		}
   244  	}
   245  
   246  	// All errors below are relayed from underlying io.Writer,
   247  	// so it is now safe to write data.
   248  
   249  	if _, err := out.Write(pemStart[1:]); err != nil {
   250  		return err
   251  	}
   252  	if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
   253  		return err
   254  	}
   255  
   256  	if len(b.Headers) > 0 {
   257  		const procType = "Proc-Type"
   258  		h := make([]string, 0, len(b.Headers))
   259  		hasProcType := false
   260  		for k := range b.Headers {
   261  			if k == procType {
   262  				hasProcType = true
   263  				continue
   264  			}
   265  			h = append(h, k)
   266  		}
   267  		// The Proc-Type header must be written first.
   268  		// See RFC 1421, section 4.6.1.1
   269  		if hasProcType {
   270  			if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
   271  				return err
   272  			}
   273  		}
   274  		// For consistency of output, write other headers sorted by key.
   275  		sort.Strings(h)
   276  		for _, k := range h {
   277  			if err := writeHeader(out, k, b.Headers[k]); err != nil {
   278  				return err
   279  			}
   280  		}
   281  		if _, err := out.Write(nl); err != nil {
   282  			return err
   283  		}
   284  	}
   285  
   286  	var breaker lineBreaker
   287  	breaker.out = out
   288  
   289  	b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
   290  	if _, err := b64.Write(b.Bytes); err != nil {
   291  		return err
   292  	}
   293  	b64.Close()
   294  	breaker.Close()
   295  
   296  	if _, err := out.Write(pemEnd[1:]); err != nil {
   297  		return err
   298  	}
   299  	_, err := out.Write([]byte(b.Type + "-----\n"))
   300  	return err
   301  }
   302  
   303  // EncodeToMemory returns the PEM encoding of b.
   304  //
   305  // If b has invalid headers and cannot be encoded,
   306  // EncodeToMemory returns nil. If it is important to
   307  // report details about this error case, use Encode instead.
   308  func EncodeToMemory(b *Block) []byte {
   309  	var buf bytes.Buffer
   310  	if err := Encode(&buf, b); err != nil {
   311  		return nil
   312  	}
   313  	return buf.Bytes()
   314  }
   315  

View as plain text