blob: 90fe3dc50cf16cca37e1269807544545a768ab9a [file] [log] [blame]
Adam Langleyfd74a832009-10-21 17:53:50 -07001// 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
Nigel Tao6a186d32011-04-20 09:57:05 +10005// Package pem implements the PEM data encoding, which originated in Privacy
Adam Langleyfd74a832009-10-21 17:53:50 -07006// Enhanced Mail. The most common use of PEM encoding today is in TLS keys and
7// certificates. See RFC 1421.
8package pem
9
10import (
Robert Griesemer1c729592009-12-15 15:27:16 -080011 "bytes"
12 "encoding/base64"
Adam Langleyd5841ca2010-02-07 15:22:36 -050013 "io"
Roger Peppe1e1733a2012-11-12 15:29:17 +000014 "sort"
Adam Langleyfd74a832009-10-21 17:53:50 -070015)
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.
25type Block struct {
Robert Griesemer1c729592009-12-15 15:27:16 -080026 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.
Adam Langleyfd74a832009-10-21 17:53:50 -070029}
30
31// getLine results the first \r\n or \n delineated line from the given byte
Adam Langley55af51d2012-04-12 12:33:52 -040032// 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.
Adam Langleyfd74a832009-10-21 17:53:50 -070036func getLine(data []byte) (line, rest []byte) {
Robert Griesemer1c729592009-12-15 15:27:16 -080037 i := bytes.Index(data, []byte{'\n'})
38 var j int
Adam Langleyfd74a832009-10-21 17:53:50 -070039 if i < 0 {
Robert Griesemer1c729592009-12-15 15:27:16 -080040 i = len(data)
41 j = i
Adam Langleyfd74a832009-10-21 17:53:50 -070042 } else {
Robert Griesemer1c729592009-12-15 15:27:16 -080043 j = i + 1
Adam Langleyfd74a832009-10-21 17:53:50 -070044 if i > 0 && data[i-1] == '\r' {
Robert Griesemer40621d52009-11-09 12:07:39 -080045 i--
Adam Langleyfd74a832009-10-21 17:53:50 -070046 }
47 }
Adam Langley55af51d2012-04-12 12:33:52 -040048 return bytes.TrimRight(data[0:i], " \t"), data[j:]
Adam Langleyfd74a832009-10-21 17:53:50 -070049}
50
51// removeWhitespace returns a copy of its input with all spaces, tab and
52// newline characters removed.
53func removeWhitespace(data []byte) []byte {
Robert Griesemer1c729592009-12-15 15:27:16 -080054 result := make([]byte, len(data))
55 n := 0
Adam Langleyfd74a832009-10-21 17:53:50 -070056
57 for _, b := range data {
58 if b == ' ' || b == '\t' || b == '\r' || b == '\n' {
Robert Griesemer40621d52009-11-09 12:07:39 -080059 continue
Adam Langleyfd74a832009-10-21 17:53:50 -070060 }
Robert Griesemer1c729592009-12-15 15:27:16 -080061 result[n] = b
62 n++
Adam Langleyfd74a832009-10-21 17:53:50 -070063 }
64
Robert Griesemer1c729592009-12-15 15:27:16 -080065 return result[0:n]
Adam Langleyfd74a832009-10-21 17:53:50 -070066}
67
Russ Cox9750adb2010-02-25 16:01:29 -080068var pemStart = []byte("\n-----BEGIN ")
69var pemEnd = []byte("\n-----END ")
70var pemEndOfLine = []byte("-----")
Adam Langleyfd74a832009-10-21 17:53:50 -070071
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
Adam Langley7d680932009-10-21 19:47:52 -070074// no PEM data is found, p is nil and the whole of the input is returned in
75// rest.
Adam Langleyfd74a832009-10-21 17:53:50 -070076func 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.
Robert Griesemer1c729592009-12-15 15:27:16 -080079 rest = data
Russ Cox9ac44492009-11-20 11:45:05 -080080 if bytes.HasPrefix(data, pemStart[1:]) {
Robert Griesemer40621d52009-11-09 12:07:39 -080081 rest = rest[len(pemStart)-1 : len(data)]
Adam Langleyfd74a832009-10-21 17:53:50 -070082 } else if i := bytes.Index(data, pemStart); i >= 0 {
Robert Griesemer40621d52009-11-09 12:07:39 -080083 rest = rest[i+len(pemStart) : len(data)]
Adam Langleyfd74a832009-10-21 17:53:50 -070084 } else {
Robert Griesemer40621d52009-11-09 12:07:39 -080085 return nil, data
Adam Langleyfd74a832009-10-21 17:53:50 -070086 }
87
Robert Griesemer1c729592009-12-15 15:27:16 -080088 typeLine, rest := getLine(rest)
Adam Langleyfd74a832009-10-21 17:53:50 -070089 if !bytes.HasSuffix(typeLine, pemEndOfLine) {
Russ Cox21e75da2011-06-17 06:07:13 -040090 return decodeError(data, rest)
Adam Langleyfd74a832009-10-21 17:53:50 -070091 }
Robert Griesemer1c729592009-12-15 15:27:16 -080092 typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
Adam Langleyfd74a832009-10-21 17:53:50 -070093
94 p = &Block{
95 Headers: make(map[string]string),
Robert Griesemerf44fa9b2010-03-02 13:46:51 -080096 Type: string(typeLine),
Robert Griesemer1c729592009-12-15 15:27:16 -080097 }
Adam Langleyfd74a832009-10-21 17:53:50 -070098
99 for {
100 // This loop terminates because getLine's second result is
Brad Fitzpatrickdcdaeeb2011-07-13 10:54:51 -0700101 // always smaller than its argument.
Adam Langleyfd74a832009-10-21 17:53:50 -0700102 if len(rest) == 0 {
Robert Griesemer40621d52009-11-09 12:07:39 -0800103 return nil, data
Adam Langleyfd74a832009-10-21 17:53:50 -0700104 }
Robert Griesemer1c729592009-12-15 15:27:16 -0800105 line, next := getLine(rest)
Adam Langleyfd74a832009-10-21 17:53:50 -0700106
Robert Griesemer1c729592009-12-15 15:27:16 -0800107 i := bytes.Index(line, []byte{':'})
Adam Langleyfd74a832009-10-21 17:53:50 -0700108 if i == -1 {
Robert Griesemer40621d52009-11-09 12:07:39 -0800109 break
Adam Langleyfd74a832009-10-21 17:53:50 -0700110 }
111
112 // TODO(agl): need to cope with values that spread across lines.
Robert Griesemer1c729592009-12-15 15:27:16 -0800113 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
Adam Langleyfd74a832009-10-21 17:53:50 -0700118 }
119
Robert Griesemer1c729592009-12-15 15:27:16 -0800120 i := bytes.Index(rest, pemEnd)
Adam Langleyfd74a832009-10-21 17:53:50 -0700121 if i < 0 {
Russ Cox21e75da2011-06-17 06:07:13 -0400122 return decodeError(data, rest)
Adam Langleyfd74a832009-10-21 17:53:50 -0700123 }
Robert Griesemer1c729592009-12-15 15:27:16 -0800124 base64Data := removeWhitespace(rest[0:i])
Adam Langleyfd74a832009-10-21 17:53:50 -0700125
Robert Griesemer1c729592009-12-15 15:27:16 -0800126 p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
127 n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
Adam Langleyfd74a832009-10-21 17:53:50 -0700128 if err != nil {
Russ Cox21e75da2011-06-17 06:07:13 -0400129 return decodeError(data, rest)
Adam Langleyfd74a832009-10-21 17:53:50 -0700130 }
Robert Griesemer1c729592009-12-15 15:27:16 -0800131 p.Bytes = p.Bytes[0:n]
Adam Langleyfd74a832009-10-21 17:53:50 -0700132
Robert Griesemer1c729592009-12-15 15:27:16 -0800133 _, rest = getLine(rest[i+len(pemEnd):])
Adam Langleyfd74a832009-10-21 17:53:50 -0700134
Robert Griesemer1c729592009-12-15 15:27:16 -0800135 return
Russ Cox21e75da2011-06-17 06:07:13 -0400136}
Adam Langleyfd74a832009-10-21 17:53:50 -0700137
Russ Cox21e75da2011-06-17 06:07:13 -0400138func decodeError(data, rest []byte) (*Block, []byte) {
Adam Langleyfd74a832009-10-21 17:53:50 -0700139 // 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
Robert Henckec8727c82011-05-18 13:14:56 -0400145 // find any valid block, no matter what bytes precede it.
Adam Langleyfd74a832009-10-21 17:53:50 -0700146 //
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.
Russ Cox21e75da2011-06-17 06:07:13 -0400159 p, rest := Decode(rest)
Adam Langleyfd74a832009-10-21 17:53:50 -0700160 if p == nil {
Robert Griesemer40621d52009-11-09 12:07:39 -0800161 rest = data
Adam Langleyfd74a832009-10-21 17:53:50 -0700162 }
Russ Cox21e75da2011-06-17 06:07:13 -0400163 return p, rest
Adam Langleyfd74a832009-10-21 17:53:50 -0700164}
Adam Langleyd5841ca2010-02-07 15:22:36 -0500165
166const pemLineLength = 64
167
168type lineBreaker struct {
169 line [pemLineLength]byte
170 used int
171 out io.Writer
172}
173
Ben Burkerte4c22292014-12-21 13:31:23 -0800174var nl = []byte{'\n'}
175
Russ Coxc2049d22011-11-01 22:04:37 -0400176func (l *lineBreaker) Write(b []byte) (n int, err error) {
Adam Langleyd5841ca2010-02-07 15:22:36 -0500177 if l.used+len(b) < pemLineLength {
178 copy(l.line[l.used:], b)
179 l.used += len(b)
180 return len(b), nil
181 }
182
183 n, err = l.out.Write(l.line[0:l.used])
184 if err != nil {
185 return
186 }
187 excess := pemLineLength - l.used
188 l.used = 0
189
190 n, err = l.out.Write(b[0:excess])
191 if err != nil {
192 return
193 }
194
Ben Burkerte4c22292014-12-21 13:31:23 -0800195 n, err = l.out.Write(nl)
Adam Langleyd5841ca2010-02-07 15:22:36 -0500196 if err != nil {
197 return
198 }
199
200 return l.Write(b[excess:])
201}
202
Russ Coxc2049d22011-11-01 22:04:37 -0400203func (l *lineBreaker) Close() (err error) {
Adam Langleyd5841ca2010-02-07 15:22:36 -0500204 if l.used > 0 {
205 _, err = l.out.Write(l.line[0:l.used])
206 if err != nil {
207 return
208 }
Ben Burkerte4c22292014-12-21 13:31:23 -0800209 _, err = l.out.Write(nl)
Adam Langleyd5841ca2010-02-07 15:22:36 -0500210 }
211
212 return
213}
214
Roger Peppe1e1733a2012-11-12 15:29:17 +0000215func writeHeader(out io.Writer, k, v string) error {
216 _, err := out.Write([]byte(k + ": " + v + "\n"))
217 return err
218}
219
220func Encode(out io.Writer, b *Block) error {
221 if _, err := out.Write(pemStart[1:]); err != nil {
222 return err
Adam Langleyd5841ca2010-02-07 15:22:36 -0500223 }
Roger Peppe1e1733a2012-11-12 15:29:17 +0000224 if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
225 return err
Adam Langleyd5841ca2010-02-07 15:22:36 -0500226 }
227
Russ Cox36560d22010-07-12 16:48:49 -0700228 if len(b.Headers) > 0 {
Roger Peppe1e1733a2012-11-12 15:29:17 +0000229 const procType = "Proc-Type"
230 h := make([]string, 0, len(b.Headers))
231 hasProcType := false
232 for k := range b.Headers {
233 if k == procType {
234 hasProcType = true
235 continue
236 }
237 h = append(h, k)
238 }
239 // The Proc-Type header must be written first.
240 // See RFC 1421, section 4.6.1.1
241 if hasProcType {
242 if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
243 return err
Russ Cox36560d22010-07-12 16:48:49 -0700244 }
Adam Langleyd5841ca2010-02-07 15:22:36 -0500245 }
Roger Peppe1e1733a2012-11-12 15:29:17 +0000246 // For consistency of output, write other headers sorted by key.
247 sort.Strings(h)
248 for _, k := range h {
249 if err := writeHeader(out, k, b.Headers[k]); err != nil {
250 return err
251 }
252 }
Ben Burkerte4c22292014-12-21 13:31:23 -0800253 if _, err := out.Write(nl); err != nil {
Roger Peppe1e1733a2012-11-12 15:29:17 +0000254 return err
Adam Langleyd5841ca2010-02-07 15:22:36 -0500255 }
256 }
257
258 var breaker lineBreaker
259 breaker.out = out
260
261 b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
Roger Peppe1e1733a2012-11-12 15:29:17 +0000262 if _, err := b64.Write(b.Bytes); err != nil {
263 return err
Adam Langleyd5841ca2010-02-07 15:22:36 -0500264 }
265 b64.Close()
266 breaker.Close()
267
Roger Peppe1e1733a2012-11-12 15:29:17 +0000268 if _, err := out.Write(pemEnd[1:]); err != nil {
269 return err
Adam Langleyd5841ca2010-02-07 15:22:36 -0500270 }
Roger Peppe1e1733a2012-11-12 15:29:17 +0000271 _, err := out.Write([]byte(b.Type + "-----\n"))
272 return err
Adam Langleyd5841ca2010-02-07 15:22:36 -0500273}
274
275func EncodeToMemory(b *Block) []byte {
Rob Pike5be24042012-02-06 14:09:00 +1100276 var buf bytes.Buffer
277 Encode(&buf, b)
Adam Langleyd5841ca2010-02-07 15:22:36 -0500278 return buf.Bytes()
279}