...
Run Format

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		"io"
    14		"sort"
    15	)
    16	
    17	// A Block represents a PEM encoded structure.
    18	//
    19	// The encoded form is:
    20	//    -----BEGIN Type-----
    21	//    Headers
    22	//    base64-encoded Bytes
    23	//    -----END Type-----
    24	// where Headers is a possibly empty sequence of Key: Value lines.
    25	type Block struct {
    26		Type    string            // The type, taken from the preamble (i.e. "RSA PRIVATE KEY").
    27		Headers map[string]string // Optional headers.
    28		Bytes   []byte            // The decoded bytes of the contents. Typically a DER encoded ASN.1 structure.
    29	}
    30	
    31	// getLine results the first \r\n or \n delineated line from the given byte
    32	// array. The line does not include trailing whitespace or the trailing new
    33	// line bytes. The remainder of the byte array (also not including the new line
    34	// bytes) is also returned and this will always be smaller than the original
    35	// argument.
    36	func getLine(data []byte) (line, rest []byte) {
    37		i := bytes.Index(data, []byte{'\n'})
    38		var j int
    39		if i < 0 {
    40			i = len(data)
    41			j = i
    42		} else {
    43			j = i + 1
    44			if i > 0 && data[i-1] == '\r' {
    45				i--
    46			}
    47		}
    48		return bytes.TrimRight(data[0:i], " \t"), data[j:]
    49	}
    50	
    51	// removeWhitespace returns a copy of its input with all spaces, tab and
    52	// newline characters removed.
    53	func removeWhitespace(data []byte) []byte {
    54		result := make([]byte, len(data))
    55		n := 0
    56	
    57		for _, b := range data {
    58			if b == ' ' || b == '\t' || b == '\r' || b == '\n' {
    59				continue
    60			}
    61			result[n] = b
    62			n++
    63		}
    64	
    65		return result[0:n]
    66	}
    67	
    68	var pemStart = []byte("\n-----BEGIN ")
    69	var pemEnd = []byte("\n-----END ")
    70	var pemEndOfLine = []byte("-----")
    71	
    72	// Decode will find the next PEM formatted block (certificate, private key
    73	// etc) in the input. It returns that block and the remainder of the input. If
    74	// no PEM data is found, p is nil and the whole of the input is returned in
    75	// rest.
    76	func Decode(data []byte) (p *Block, rest []byte) {
    77		// pemStart begins with a newline. However, at the very beginning of
    78		// the byte array, we'll accept the start string without it.
    79		rest = data
    80		if bytes.HasPrefix(data, pemStart[1:]) {
    81			rest = rest[len(pemStart)-1 : len(data)]
    82		} else if i := bytes.Index(data, pemStart); i >= 0 {
    83			rest = rest[i+len(pemStart) : len(data)]
    84		} else {
    85			return nil, data
    86		}
    87	
    88		typeLine, rest := getLine(rest)
    89		if !bytes.HasSuffix(typeLine, pemEndOfLine) {
    90			return decodeError(data, rest)
    91		}
    92		typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
    93	
    94		p = &Block{
    95			Headers: make(map[string]string),
    96			Type:    string(typeLine),
    97		}
    98	
    99		for {
   100			// This loop terminates because getLine's second result is
   101			// always smaller than its argument.
   102			if len(rest) == 0 {
   103				return nil, data
   104			}
   105			line, next := getLine(rest)
   106	
   107			i := bytes.Index(line, []byte{':'})
   108			if i == -1 {
   109				break
   110			}
   111	
   112			// TODO(agl): need to cope with values that spread across lines.
   113			key, val := line[0:i], line[i+1:]
   114			key = bytes.TrimSpace(key)
   115			val = bytes.TrimSpace(val)
   116			p.Headers[string(key)] = string(val)
   117			rest = next
   118		}
   119	
   120		i := bytes.Index(rest, pemEnd)
   121		if i < 0 {
   122			return decodeError(data, rest)
   123		}
   124		base64Data := removeWhitespace(rest[0:i])
   125	
   126		p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
   127		n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
   128		if err != nil {
   129			return decodeError(data, rest)
   130		}
   131		p.Bytes = p.Bytes[0:n]
   132	
   133		_, rest = getLine(rest[i+len(pemEnd):])
   134	
   135		return
   136	}
   137	
   138	func decodeError(data, rest []byte) (*Block, []byte) {
   139		// If we get here then we have rejected a likely looking, but
   140		// ultimately invalid PEM block. We need to start over from a new
   141		// position.  We have consumed the preamble line and will have consumed
   142		// any lines which could be header lines. However, a valid preamble
   143		// line is not a valid header line, therefore we cannot have consumed
   144		// the preamble line for the any subsequent block. Thus, we will always
   145		// find any valid block, no matter what bytes precede it.
   146		//
   147		// For example, if the input is
   148		//
   149		//    -----BEGIN MALFORMED BLOCK-----
   150		//    junk that may look like header lines
   151		//   or data lines, but no END line
   152		//
   153		//    -----BEGIN ACTUAL BLOCK-----
   154		//    realdata
   155		//    -----END ACTUAL BLOCK-----
   156		//
   157		// we've failed to parse using the first BEGIN line
   158		// and now will try again, using the second BEGIN line.
   159		p, rest := Decode(rest)
   160		if p == nil {
   161			rest = data
   162		}
   163		return p, rest
   164	}
   165	
   166	const pemLineLength = 64
   167	
   168	type lineBreaker struct {
   169		line [pemLineLength]byte
   170		used int
   171		out  io.Writer
   172	}
   173	
   174	func (l *lineBreaker) Write(b []byte) (n int, err error) {
   175		if l.used+len(b) < pemLineLength {
   176			copy(l.line[l.used:], b)
   177			l.used += len(b)
   178			return len(b), nil
   179		}
   180	
   181		n, err = l.out.Write(l.line[0:l.used])
   182		if err != nil {
   183			return
   184		}
   185		excess := pemLineLength - l.used
   186		l.used = 0
   187	
   188		n, err = l.out.Write(b[0:excess])
   189		if err != nil {
   190			return
   191		}
   192	
   193		n, err = l.out.Write([]byte{'\n'})
   194		if err != nil {
   195			return
   196		}
   197	
   198		return l.Write(b[excess:])
   199	}
   200	
   201	func (l *lineBreaker) Close() (err error) {
   202		if l.used > 0 {
   203			_, err = l.out.Write(l.line[0:l.used])
   204			if err != nil {
   205				return
   206			}
   207			_, err = l.out.Write([]byte{'\n'})
   208		}
   209	
   210		return
   211	}
   212	
   213	func writeHeader(out io.Writer, k, v string) error {
   214		_, err := out.Write([]byte(k + ": " + v + "\n"))
   215		return err
   216	}
   217	
   218	func Encode(out io.Writer, b *Block) error {
   219		if _, err := out.Write(pemStart[1:]); err != nil {
   220			return err
   221		}
   222		if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
   223			return err
   224		}
   225	
   226		if len(b.Headers) > 0 {
   227			const procType = "Proc-Type"
   228			h := make([]string, 0, len(b.Headers))
   229			hasProcType := false
   230			for k := range b.Headers {
   231				if k == procType {
   232					hasProcType = true
   233					continue
   234				}
   235				h = append(h, k)
   236			}
   237			// The Proc-Type header must be written first.
   238			// See RFC 1421, section 4.6.1.1
   239			if hasProcType {
   240				if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
   241					return err
   242				}
   243			}
   244			// For consistency of output, write other headers sorted by key.
   245			sort.Strings(h)
   246			for _, k := range h {
   247				if err := writeHeader(out, k, b.Headers[k]); err != nil {
   248					return err
   249				}
   250			}
   251			if _, err := out.Write([]byte{'\n'}); err != nil {
   252				return err
   253			}
   254		}
   255	
   256		var breaker lineBreaker
   257		breaker.out = out
   258	
   259		b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
   260		if _, err := b64.Write(b.Bytes); err != nil {
   261			return err
   262		}
   263		b64.Close()
   264		breaker.Close()
   265	
   266		if _, err := out.Write(pemEnd[1:]); err != nil {
   267			return err
   268		}
   269		_, err := out.Write([]byte(b.Type + "-----\n"))
   270		return err
   271	}
   272	
   273	func EncodeToMemory(b *Block) []byte {
   274		var buf bytes.Buffer
   275		Encode(&buf, b)
   276		return buf.Bytes()
   277	}
   278	

View as plain text