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