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, 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