...
1
2
3
4
5
6
7
8
9
10
11
12
13 package armor
14
15 import (
16 "bufio"
17 "bytes"
18 "encoding/base64"
19 "golang.org/x/crypto/openpgp/errors"
20 "io"
21 )
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 type Block struct {
39 Type string
40 Header map[string]string
41 Body io.Reader
42 lReader lineReader
43 oReader openpgpReader
44 }
45
46 var ArmorCorrupt error = errors.StructuralError("armor invalid")
47
48 const crc24Init = 0xb704ce
49 const crc24Poly = 0x1864cfb
50 const crc24Mask = 0xffffff
51
52
53 func crc24(crc uint32, d []byte) uint32 {
54 for _, b := range d {
55 crc ^= uint32(b) << 16
56 for i := 0; i < 8; i++ {
57 crc <<= 1
58 if crc&0x1000000 != 0 {
59 crc ^= crc24Poly
60 }
61 }
62 }
63 return crc
64 }
65
66 var armorStart = []byte("-----BEGIN ")
67 var armorEnd = []byte("-----END ")
68 var armorEndOfLine = []byte("-----")
69
70
71
72 type lineReader struct {
73 in *bufio.Reader
74 buf []byte
75 eof bool
76 crc uint32
77 crcSet bool
78 }
79
80 func (l *lineReader) Read(p []byte) (n int, err error) {
81 if l.eof {
82 return 0, io.EOF
83 }
84
85 if len(l.buf) > 0 {
86 n = copy(p, l.buf)
87 l.buf = l.buf[n:]
88 return
89 }
90
91 line, isPrefix, err := l.in.ReadLine()
92 if err != nil {
93 return
94 }
95 if isPrefix {
96 return 0, ArmorCorrupt
97 }
98
99 if bytes.HasPrefix(line, armorEnd) {
100 l.eof = true
101 return 0, io.EOF
102 }
103
104 if len(line) == 5 && line[0] == '=' {
105
106 var expectedBytes [3]byte
107 var m int
108 m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:])
109 if m != 3 || err != nil {
110 return
111 }
112 l.crc = uint32(expectedBytes[0])<<16 |
113 uint32(expectedBytes[1])<<8 |
114 uint32(expectedBytes[2])
115
116 line, _, err = l.in.ReadLine()
117 if err != nil && err != io.EOF {
118 return
119 }
120 if !bytes.HasPrefix(line, armorEnd) {
121 return 0, ArmorCorrupt
122 }
123
124 l.eof = true
125 l.crcSet = true
126 return 0, io.EOF
127 }
128
129 if len(line) > 96 {
130 return 0, ArmorCorrupt
131 }
132
133 n = copy(p, line)
134 bytesToSave := len(line) - n
135 if bytesToSave > 0 {
136 if cap(l.buf) < bytesToSave {
137 l.buf = make([]byte, 0, bytesToSave)
138 }
139 l.buf = l.buf[0:bytesToSave]
140 copy(l.buf, line[n:])
141 }
142
143 return
144 }
145
146
147
148
149 type openpgpReader struct {
150 lReader *lineReader
151 b64Reader io.Reader
152 currentCRC uint32
153 }
154
155 func (r *openpgpReader) Read(p []byte) (n int, err error) {
156 n, err = r.b64Reader.Read(p)
157 r.currentCRC = crc24(r.currentCRC, p[:n])
158
159 if err == io.EOF && r.lReader.crcSet && r.lReader.crc != r.currentCRC&crc24Mask {
160 return 0, ArmorCorrupt
161 }
162
163 return
164 }
165
166
167
168
169
170 func Decode(in io.Reader) (p *Block, err error) {
171 r := bufio.NewReaderSize(in, 100)
172 var line []byte
173 ignoreNext := false
174
175 TryNextBlock:
176 p = nil
177
178
179 for {
180 ignoreThis := ignoreNext
181 line, ignoreNext, err = r.ReadLine()
182 if err != nil {
183 return
184 }
185 if ignoreNext || ignoreThis {
186 continue
187 }
188 line = bytes.TrimSpace(line)
189 if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) {
190 break
191 }
192 }
193
194 p = new(Block)
195 p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)])
196 p.Header = make(map[string]string)
197 nextIsContinuation := false
198 var lastKey string
199
200
201 for {
202 isContinuation := nextIsContinuation
203 line, nextIsContinuation, err = r.ReadLine()
204 if err != nil {
205 p = nil
206 return
207 }
208 if isContinuation {
209 p.Header[lastKey] += string(line)
210 continue
211 }
212 line = bytes.TrimSpace(line)
213 if len(line) == 0 {
214 break
215 }
216
217 i := bytes.Index(line, []byte(": "))
218 if i == -1 {
219 goto TryNextBlock
220 }
221 lastKey = string(line[:i])
222 p.Header[lastKey] = string(line[i+2:])
223 }
224
225 p.lReader.in = r
226 p.oReader.currentCRC = crc24Init
227 p.oReader.lReader = &p.lReader
228 p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader)
229 p.Body = &p.oReader
230
231 return
232 }
233
View as plain text