pem.mx raw

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