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 "io"
14 "sort"
15 )
16
17
18
19
20
21
22
23
24
25 type Block struct {
26 Type string
27 Headers map[string]string
28 Bytes []byte
29 }
30
31
32
33
34
35
36 func getLine(data []byte) (line, rest []byte) {
37 i := bytes.Index(data, []byte{'\n'})
38 var j int
39 if i < 0 {
40 i = len(data)
41 j = i
42 } else {
43 j = i + 1
44 if i > 0 && data[i-1] == '\r' {
45 i--
46 }
47 }
48 return bytes.TrimRight(data[0:i], " \t"), data[j:]
49 }
50
51
52
53 func removeWhitespace(data []byte) []byte {
54 result := make([]byte, len(data))
55 n := 0
56
57 for _, b := range data {
58 if b == ' ' || b == '\t' || b == '\r' || b == '\n' {
59 continue
60 }
61 result[n] = b
62 n++
63 }
64
65 return result[0:n]
66 }
67
68 var pemStart = []byte("\n-----BEGIN ")
69 var pemEnd = []byte("\n-----END ")
70 var pemEndOfLine = []byte("-----")
71
72
73
74
75
76 func Decode(data []byte) (p *Block, rest []byte) {
77
78
79 rest = data
80 if bytes.HasPrefix(data, pemStart[1:]) {
81 rest = rest[len(pemStart)-1 : len(data)]
82 } else if i := bytes.Index(data, pemStart); i >= 0 {
83 rest = rest[i+len(pemStart) : len(data)]
84 } else {
85 return nil, data
86 }
87
88 typeLine, rest := getLine(rest)
89 if !bytes.HasSuffix(typeLine, pemEndOfLine) {
90 return decodeError(data, rest)
91 }
92 typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
93
94 p = &Block{
95 Headers: make(map[string]string),
96 Type: string(typeLine),
97 }
98
99 for {
100
101
102 if len(rest) == 0 {
103 return nil, data
104 }
105 line, next := getLine(rest)
106
107 i := bytes.Index(line, []byte{':'})
108 if i == -1 {
109 break
110 }
111
112
113 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
118 }
119
120 i := bytes.Index(rest, pemEnd)
121 if i < 0 {
122 return decodeError(data, rest)
123 }
124 base64Data := removeWhitespace(rest[0:i])
125
126 p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
127 n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
128 if err != nil {
129 return decodeError(data, rest)
130 }
131 p.Bytes = p.Bytes[0:n]
132
133 _, rest = getLine(rest[i+len(pemEnd):])
134
135 return
136 }
137
138 func decodeError(data, rest []byte) (*Block, []byte) {
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 p, rest := Decode(rest)
160 if p == nil {
161 rest = data
162 }
163 return p, rest
164 }
165
166 const pemLineLength = 64
167
168 type lineBreaker struct {
169 line [pemLineLength]byte
170 used int
171 out io.Writer
172 }
173
174 func (l *lineBreaker) Write(b []byte) (n int, err error) {
175 if l.used+len(b) < pemLineLength {
176 copy(l.line[l.used:], b)
177 l.used += len(b)
178 return len(b), nil
179 }
180
181 n, err = l.out.Write(l.line[0:l.used])
182 if err != nil {
183 return
184 }
185 excess := pemLineLength - l.used
186 l.used = 0
187
188 n, err = l.out.Write(b[0:excess])
189 if err != nil {
190 return
191 }
192
193 n, err = l.out.Write([]byte{'\n'})
194 if err != nil {
195 return
196 }
197
198 return l.Write(b[excess:])
199 }
200
201 func (l *lineBreaker) Close() (err error) {
202 if l.used > 0 {
203 _, err = l.out.Write(l.line[0:l.used])
204 if err != nil {
205 return
206 }
207 _, err = l.out.Write([]byte{'\n'})
208 }
209
210 return
211 }
212
213 func writeHeader(out io.Writer, k, v string) error {
214 _, err := out.Write([]byte(k + ": " + v + "\n"))
215 return err
216 }
217
218 func Encode(out io.Writer, b *Block) error {
219 if _, err := out.Write(pemStart[1:]); err != nil {
220 return err
221 }
222 if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
223 return err
224 }
225
226 if len(b.Headers) > 0 {
227 const procType = "Proc-Type"
228 h := make([]string, 0, len(b.Headers))
229 hasProcType := false
230 for k := range b.Headers {
231 if k == procType {
232 hasProcType = true
233 continue
234 }
235 h = append(h, k)
236 }
237
238
239 if hasProcType {
240 if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
241 return err
242 }
243 }
244
245 sort.Strings(h)
246 for _, k := range h {
247 if err := writeHeader(out, k, b.Headers[k]); err != nil {
248 return err
249 }
250 }
251 if _, err := out.Write([]byte{'\n'}); err != nil {
252 return err
253 }
254 }
255
256 var breaker lineBreaker
257 breaker.out = out
258
259 b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
260 if _, err := b64.Write(b.Bytes); err != nil {
261 return err
262 }
263 b64.Close()
264 breaker.Close()
265
266 if _, err := out.Write(pemEnd[1:]); err != nil {
267 return err
268 }
269 _, err := out.Write([]byte(b.Type + "-----\n"))
270 return err
271 }
272
273 func EncodeToMemory(b *Block) []byte {
274 var buf bytes.Buffer
275 Encode(&buf, b)
276 return buf.Bytes()
277 }
View as plain text