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.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 // 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.IndexByte(line, ':')
    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, there should be the same type
    139 	// and then a 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 	restOfEndLine := endTrailer[endTrailerLen:]
    147 	endTrailer = endTrailer[:endTrailerLen]
    148 	if !bytes.HasPrefix(endTrailer, typeLine) ||
    149 		!bytes.HasSuffix(endTrailer, pemEndOfLine) {
    150 		return decodeError(data, rest)
    151 	}
    152 
    153 	// The line must end with only whitespace.
    154 	if s, _ := getLine(restOfEndLine); len(s) != 0 {
    155 		return decodeError(data, rest)
    156 	}
    157 
    158 	base64Data := removeWhitespace(rest[:endIndex])
    159 	p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
    160 	n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
    161 	if err != nil {
    162 		return decodeError(data, rest)
    163 	}
    164 	p.Bytes = p.Bytes[:n]
    165 
    166 	// the -1 is because we might have only matched pemEnd without the
    167 	// leading newline if the PEM block was empty.
    168 	_, rest = getLine(rest[endIndex+len(pemEnd)-1:])
    169 
    170 	return
    171 }
    172 
    173 func decodeError(data, rest []byte) (*Block, []byte) {
    174 	// If we get here then we have rejected a likely looking, but
    175 	// ultimately invalid PEM block. We need to start over from a new
    176 	// position. We have consumed the preamble line and will have consumed
    177 	// any lines which could be header lines. However, a valid preamble
    178 	// line is not a valid header line, therefore we cannot have consumed
    179 	// the preamble line for the any subsequent block. Thus, we will always
    180 	// find any valid block, no matter what bytes precede it.
    181 	//
    182 	// For example, if the input is
    183 	//
    184 	//    -----BEGIN MALFORMED BLOCK-----
    185 	//    junk that may look like header lines
    186 	//   or data lines, but no END line
    187 	//
    188 	//    -----BEGIN ACTUAL BLOCK-----
    189 	//    realdata
    190 	//    -----END ACTUAL BLOCK-----
    191 	//
    192 	// we've failed to parse using the first BEGIN line
    193 	// and now will try again, using the second BEGIN line.
    194 	p, rest := Decode(rest)
    195 	if p == nil {
    196 		rest = data
    197 	}
    198 	return p, rest
    199 }
    200 
    201 const pemLineLength = 64
    202 
    203 type lineBreaker struct {
    204 	line [pemLineLength]byte
    205 	used int
    206 	out  io.Writer
    207 }
    208 
    209 var nl = []byte{'\n'}
    210 
    211 func (l *lineBreaker) Write(b []byte) (n int, err error) {
    212 	if l.used+len(b) < pemLineLength {
    213 		copy(l.line[l.used:], b)
    214 		l.used += len(b)
    215 		return len(b), nil
    216 	}
    217 
    218 	n, err = l.out.Write(l.line[0:l.used])
    219 	if err != nil {
    220 		return
    221 	}
    222 	excess := pemLineLength - l.used
    223 	l.used = 0
    224 
    225 	n, err = l.out.Write(b[0:excess])
    226 	if err != nil {
    227 		return
    228 	}
    229 
    230 	n, err = l.out.Write(nl)
    231 	if err != nil {
    232 		return
    233 	}
    234 
    235 	return l.Write(b[excess:])
    236 }
    237 
    238 func (l *lineBreaker) Close() (err error) {
    239 	if l.used > 0 {
    240 		_, err = l.out.Write(l.line[0:l.used])
    241 		if err != nil {
    242 			return
    243 		}
    244 		_, err = l.out.Write(nl)
    245 	}
    246 
    247 	return
    248 }
    249 
    250 func writeHeader(out io.Writer, k, v string) error {
    251 	_, err := out.Write([]byte(k + ": " + v + "\n"))
    252 	return err
    253 }
    254 
    255 // Encode writes the PEM encoding of b to out.
    256 func Encode(out io.Writer, b *Block) error {
    257 	// Check for invalid block before writing any output.
    258 	for k := range b.Headers {
    259 		if strings.Contains(k, ":") {
    260 			return errors.New("pem: cannot encode a header key that contains a colon")
    261 		}
    262 	}
    263 
    264 	// All errors below are relayed from underlying io.Writer,
    265 	// so it is now safe to write data.
    266 
    267 	if _, err := out.Write(pemStart[1:]); err != nil {
    268 		return err
    269 	}
    270 	if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
    271 		return err
    272 	}
    273 
    274 	if len(b.Headers) > 0 {
    275 		const procType = "Proc-Type"
    276 		h := make([]string, 0, len(b.Headers))
    277 		hasProcType := false
    278 		for k := range b.Headers {
    279 			if k == procType {
    280 				hasProcType = true
    281 				continue
    282 			}
    283 			h = append(h, k)
    284 		}
    285 		// The Proc-Type header must be written first.
    286 		// See RFC 1421, section 4.6.1.1
    287 		if hasProcType {
    288 			if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
    289 				return err
    290 			}
    291 		}
    292 		// For consistency of output, write other headers sorted by key.
    293 		sort.Strings(h)
    294 		for _, k := range h {
    295 			if err := writeHeader(out, k, b.Headers[k]); err != nil {
    296 				return err
    297 			}
    298 		}
    299 		if _, err := out.Write(nl); err != nil {
    300 			return err
    301 		}
    302 	}
    303 
    304 	var breaker lineBreaker
    305 	breaker.out = out
    306 
    307 	b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
    308 	if _, err := b64.Write(b.Bytes); err != nil {
    309 		return err
    310 	}
    311 	b64.Close()
    312 	breaker.Close()
    313 
    314 	if _, err := out.Write(pemEnd[1:]); err != nil {
    315 		return err
    316 	}
    317 	_, err := out.Write([]byte(b.Type + "-----\n"))
    318 	return err
    319 }
    320 
    321 // EncodeToMemory returns the PEM encoding of b.
    322 //
    323 // If b has invalid headers and cannot be encoded,
    324 // EncodeToMemory returns nil. If it is important to
    325 // report details about this error case, use Encode instead.
    326 func EncodeToMemory(b *Block) []byte {
    327 	var buf bytes.Buffer
    328 	if err := Encode(&buf, b); err != nil {
    329 		return nil
    330 	}
    331 	return buf.Bytes()
    332 }
    333