...

Source file src/pkg/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	
    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