Source file src/pkg/encoding/pem/pem.go
1
2
3
4
5
6
7
8 package pem
9
10 import (
11 "bytes"
12 "encoding/base64"
13 "errors"
14 "io"
15 "sort"
16 "strings"
17 )
18
19
20
21
22
23
24
25
26
27 type Block struct {
28 Type string
29 Headers map[string]string
30 Bytes []byte
31 }
32
33
34
35
36
37
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
54
55
56
57
58 func removeSpacesAndTabs(data []byte) []byte {
59 if !bytes.ContainsAny(data, " \t") {
60
61
62 return data
63 }
64 result := make([]byte, len(data))
65 n := 0
66
67 for _, b := range data {
68 if b == ' ' || b == '\t' {
69 continue
70 }
71 result[n] = b
72 n++
73 }
74
75 return result[0:n]
76 }
77
78 var pemStart = []byte("\n-----BEGIN ")
79 var pemEnd = []byte("\n-----END ")
80 var pemEndOfLine = []byte("-----")
81
82
83
84
85
86 func Decode(data []byte) (p *Block, rest []byte) {
87
88
89 rest = data
90 if bytes.HasPrefix(data, pemStart[1:]) {
91 rest = rest[len(pemStart)-1 : len(data)]
92 } else if i := bytes.Index(data, pemStart); i >= 0 {
93 rest = rest[i+len(pemStart) : len(data)]
94 } else {
95 return nil, data
96 }
97
98 typeLine, rest := getLine(rest)
99 if !bytes.HasSuffix(typeLine, pemEndOfLine) {
100 return decodeError(data, rest)
101 }
102 typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
103
104 p = &Block{
105 Headers: make(map[string]string),
106 Type: string(typeLine),
107 }
108
109 for {
110
111
112 if len(rest) == 0 {
113 return nil, data
114 }
115 line, next := getLine(rest)
116
117 i := bytes.IndexByte(line, ':')
118 if i == -1 {
119 break
120 }
121
122
123 key, val := line[:i], line[i+1:]
124 key = bytes.TrimSpace(key)
125 val = bytes.TrimSpace(val)
126 p.Headers[string(key)] = string(val)
127 rest = next
128 }
129
130 var endIndex, endTrailerIndex int
131
132
133
134 if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
135 endIndex = 0
136 endTrailerIndex = len(pemEnd) - 1
137 } else {
138 endIndex = bytes.Index(rest, pemEnd)
139 endTrailerIndex = endIndex + len(pemEnd)
140 }
141
142 if endIndex < 0 {
143 return decodeError(data, rest)
144 }
145
146
147
148 endTrailer := rest[endTrailerIndex:]
149 endTrailerLen := len(typeLine) + len(pemEndOfLine)
150 if len(endTrailer) < endTrailerLen {
151 return decodeError(data, rest)
152 }
153
154 restOfEndLine := endTrailer[endTrailerLen:]
155 endTrailer = endTrailer[:endTrailerLen]
156 if !bytes.HasPrefix(endTrailer, typeLine) ||
157 !bytes.HasSuffix(endTrailer, pemEndOfLine) {
158 return decodeError(data, rest)
159 }
160
161
162 if s, _ := getLine(restOfEndLine); len(s) != 0 {
163 return decodeError(data, rest)
164 }
165
166 base64Data := removeSpacesAndTabs(rest[:endIndex])
167 p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
168 n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
169 if err != nil {
170 return decodeError(data, rest)
171 }
172 p.Bytes = p.Bytes[:n]
173
174
175
176 _, rest = getLine(rest[endIndex+len(pemEnd)-1:])
177
178 return
179 }
180
181 func decodeError(data, rest []byte) (*Block, []byte) {
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 p, rest := Decode(rest)
203 if p == nil {
204 rest = data
205 }
206 return p, rest
207 }
208
209 const pemLineLength = 64
210
211 type lineBreaker struct {
212 line [pemLineLength]byte
213 used int
214 out io.Writer
215 }
216
217 var nl = []byte{'\n'}
218
219 func (l *lineBreaker) Write(b []byte) (n int, err error) {
220 if l.used+len(b) < pemLineLength {
221 copy(l.line[l.used:], b)
222 l.used += len(b)
223 return len(b), nil
224 }
225
226 n, err = l.out.Write(l.line[0:l.used])
227 if err != nil {
228 return
229 }
230 excess := pemLineLength - l.used
231 l.used = 0
232
233 n, err = l.out.Write(b[0:excess])
234 if err != nil {
235 return
236 }
237
238 n, err = l.out.Write(nl)
239 if err != nil {
240 return
241 }
242
243 return l.Write(b[excess:])
244 }
245
246 func (l *lineBreaker) Close() (err error) {
247 if l.used > 0 {
248 _, err = l.out.Write(l.line[0:l.used])
249 if err != nil {
250 return
251 }
252 _, err = l.out.Write(nl)
253 }
254
255 return
256 }
257
258 func writeHeader(out io.Writer, k, v string) error {
259 _, err := out.Write([]byte(k + ": " + v + "\n"))
260 return err
261 }
262
263
264 func Encode(out io.Writer, b *Block) error {
265
266 for k := range b.Headers {
267 if strings.Contains(k, ":") {
268 return errors.New("pem: cannot encode a header key that contains a colon")
269 }
270 }
271
272
273
274
275 if _, err := out.Write(pemStart[1:]); err != nil {
276 return err
277 }
278 if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
279 return err
280 }
281
282 if len(b.Headers) > 0 {
283 const procType = "Proc-Type"
284 h := make([]string, 0, len(b.Headers))
285 hasProcType := false
286 for k := range b.Headers {
287 if k == procType {
288 hasProcType = true
289 continue
290 }
291 h = append(h, k)
292 }
293
294
295 if hasProcType {
296 if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
297 return err
298 }
299 }
300
301 sort.Strings(h)
302 for _, k := range h {
303 if err := writeHeader(out, k, b.Headers[k]); err != nil {
304 return err
305 }
306 }
307 if _, err := out.Write(nl); err != nil {
308 return err
309 }
310 }
311
312 var breaker lineBreaker
313 breaker.out = out
314
315 b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
316 if _, err := b64.Write(b.Bytes); err != nil {
317 return err
318 }
319 b64.Close()
320 breaker.Close()
321
322 if _, err := out.Write(pemEnd[1:]); err != nil {
323 return err
324 }
325 _, err := out.Write([]byte(b.Type + "-----\n"))
326 return err
327 }
328
329
330
331
332
333
334 func EncodeToMemory(b *Block) []byte {
335 var buf bytes.Buffer
336 if err := Encode(&buf, b); err != nil {
337 return nil
338 }
339 return buf.Bytes()
340 }
341
View as plain text