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