...
Run Format

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.Index(data, []byte{'\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	// 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
    58	
    59		for _, b := range data {
    60			if b == ' ' || b == '\t' || b == '\r' || b == '\n' {
    61				continue
    62			}
    63			result[n] = b
    64			n++
    65		}
    66	
    67		return result[0:n]
    68	}
    69	
    70	var pemStart = []byte("\n-----BEGIN ")
    71	var pemEnd = []byte("\n-----END ")
    72	var pemEndOfLine = []byte("-----")
    73	
    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
    88		}
    89	
    90		typeLine, rest := getLine(rest)
    91		if !bytes.HasSuffix(typeLine, pemEndOfLine) {
    92			return decodeError(data, rest)
    93		}
    94		typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
    95	
    96		p = &Block{
    97			Headers: make(map[string]string),
    98			Type:    string(typeLine),
    99		}
   100	
   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
   106			}
   107			line, next := getLine(rest)
   108	
   109			i := bytes.Index(line, []byte{':'})
   110			if i == -1 {
   111				break
   112			}
   113	
   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
   120		}
   121	
   122		var endIndex, endTrailerIndex int
   123	
   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)
   132		}
   133	
   134		if endIndex < 0 {
   135			return decodeError(data, rest)
   136		}
   137	
   138		// After the "-----" of the ending line should be the same type and a
   139		// final five dashes.
   140		endTrailer := rest[endTrailerIndex:]
   141		endTrailerLen := len(typeLine) + len(pemEndOfLine)
   142		if len(endTrailer) < endTrailerLen {
   143			return decodeError(data, rest)
   144		}
   145	
   146		endTrailer = endTrailer[:endTrailerLen]
   147		if !bytes.HasPrefix(endTrailer, typeLine) ||
   148			!bytes.HasSuffix(endTrailer, pemEndOfLine) {
   149			return decodeError(data, rest)
   150		}
   151	
   152		base64Data := removeWhitespace(rest[:endIndex])
   153		p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
   154		n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
   155		if err != nil {
   156			return decodeError(data, rest)
   157		}
   158		p.Bytes = p.Bytes[:n]
   159	
   160		// the -1 is because we might have only matched pemEnd without the
   161		// leading newline if the PEM block was empty.
   162		_, rest = getLine(rest[endIndex+len(pemEnd)-1:])
   163	
   164		return
   165	}
   166	
   167	func decodeError(data, rest []byte) (*Block, []byte) {
   168		// If we get here then we have rejected a likely looking, but
   169		// ultimately invalid PEM block. We need to start over from a new
   170		// position. We have consumed the preamble line and will have consumed
   171		// any lines which could be header lines. However, a valid preamble
   172		// line is not a valid header line, therefore we cannot have consumed
   173		// the preamble line for the any subsequent block. Thus, we will always
   174		// find any valid block, no matter what bytes precede it.
   175		//
   176		// For example, if the input is
   177		//
   178		//    -----BEGIN MALFORMED BLOCK-----
   179		//    junk that may look like header lines
   180		//   or data lines, but no END line
   181		//
   182		//    -----BEGIN ACTUAL BLOCK-----
   183		//    realdata
   184		//    -----END ACTUAL BLOCK-----
   185		//
   186		// we've failed to parse using the first BEGIN line
   187		// and now will try again, using the second BEGIN line.
   188		p, rest := Decode(rest)
   189		if p == nil {
   190			rest = data
   191		}
   192		return p, rest
   193	}
   194	
   195	const pemLineLength = 64
   196	
   197	type lineBreaker struct {
   198		line [pemLineLength]byte
   199		used int
   200		out  io.Writer
   201	}
   202	
   203	var nl = []byte{'\n'}
   204	
   205	func (l *lineBreaker) Write(b []byte) (n int, err error) {
   206		if l.used+len(b) < pemLineLength {
   207			copy(l.line[l.used:], b)
   208			l.used += len(b)
   209			return len(b), nil
   210		}
   211	
   212		n, err = l.out.Write(l.line[0:l.used])
   213		if err != nil {
   214			return
   215		}
   216		excess := pemLineLength - l.used
   217		l.used = 0
   218	
   219		n, err = l.out.Write(b[0:excess])
   220		if err != nil {
   221			return
   222		}
   223	
   224		n, err = l.out.Write(nl)
   225		if err != nil {
   226			return
   227		}
   228	
   229		return l.Write(b[excess:])
   230	}
   231	
   232	func (l *lineBreaker) Close() (err error) {
   233		if l.used > 0 {
   234			_, err = l.out.Write(l.line[0:l.used])
   235			if err != nil {
   236				return
   237			}
   238			_, err = l.out.Write(nl)
   239		}
   240	
   241		return
   242	}
   243	
   244	func writeHeader(out io.Writer, k, v string) error {
   245		_, err := out.Write([]byte(k + ": " + v + "\n"))
   246		return err
   247	}
   248	
   249	func Encode(out io.Writer, b *Block) error {
   250		if _, err := out.Write(pemStart[1:]); err != nil {
   251			return err
   252		}
   253		if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
   254			return err
   255		}
   256	
   257		if len(b.Headers) > 0 {
   258			const procType = "Proc-Type"
   259			h := make([]string, 0, len(b.Headers))
   260			hasProcType := false
   261			for k := range b.Headers {
   262				if k == procType {
   263					hasProcType = true
   264					continue
   265				}
   266				h = append(h, k)
   267			}
   268			// The Proc-Type header must be written first.
   269			// See RFC 1421, section 4.6.1.1
   270			if hasProcType {
   271				if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
   272					return err
   273				}
   274			}
   275			// For consistency of output, write other headers sorted by key.
   276			sort.Strings(h)
   277			for _, k := range h {
   278				if strings.Contains(k, ":") {
   279					return errors.New("pem: cannot encode a header key that contains a colon")
   280				}
   281				if err := writeHeader(out, k, b.Headers[k]); err != nil {
   282					return err
   283				}
   284			}
   285			if _, err := out.Write(nl); err != nil {
   286				return err
   287			}
   288		}
   289	
   290		var breaker lineBreaker
   291		breaker.out = out
   292	
   293		b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
   294		if _, err := b64.Write(b.Bytes); err != nil {
   295			return err
   296		}
   297		b64.Close()
   298		breaker.Close()
   299	
   300		if _, err := out.Write(pemEnd[1:]); err != nil {
   301			return err
   302		}
   303		_, err := out.Write([]byte(b.Type + "-----\n"))
   304		return err
   305	}
   306	
   307	func EncodeToMemory(b *Block) []byte {
   308		var buf bytes.Buffer
   309		Encode(&buf, b)
   310		return buf.Bytes()
   311	}
   312	

View as plain text