1
2
3
4
5
6
7
8
9
10 package clearsign
11
12 import (
13 "bufio"
14 "bytes"
15 "crypto"
16 "fmt"
17 "hash"
18 "io"
19 "net/textproto"
20 "strconv"
21 "strings"
22
23 "github.com/ProtonMail/go-crypto/openpgp"
24 "github.com/ProtonMail/go-crypto/openpgp/armor"
25 "github.com/ProtonMail/go-crypto/openpgp/errors"
26 "github.com/ProtonMail/go-crypto/openpgp/packet"
27 )
28
29
30
31 type Block struct {
32 Headers textproto.MIMEHeader
33 Plaintext []byte
34 Bytes []byte
35 ArmoredSignature *armor.Block
36 }
37
38
39 var start = []byte("\n-----BEGIN PGP SIGNED MESSAGE-----")
40
41
42
43 var dashEscape = []byte("- ")
44
45
46
47 var endText = []byte("-----BEGIN PGP SIGNATURE-----")
48
49
50 var end = []byte("\n-----END PGP SIGNATURE-----")
51
52 var crlf = []byte("\r\n")
53 var lf = byte('\n')
54
55
56
57
58
59 func getLine(data []byte) (line, rest []byte) {
60 i := bytes.Index(data, []byte{'\n'})
61 var j int
62 if i < 0 {
63 i = len(data)
64 j = i
65 } else {
66 j = i + 1
67 if i > 0 && data[i-1] == '\r' {
68 i--
69 }
70 }
71 return data[0:i], data[j:]
72 }
73
74
75
76
77
78
79
80
81 func Decode(data []byte) (b *Block, rest []byte) {
82
83
84 rest = data
85 if bytes.HasPrefix(data, start[1:]) {
86 rest = rest[len(start)-1:]
87 } else if i := bytes.Index(data, start); i >= 0 {
88 rest = rest[i+len(start):]
89 } else {
90 return nil, data
91 }
92
93
94 suffix, rest := getLine(rest)
95 if len(suffix) != 0 {
96 return nil, data
97 }
98
99 var line []byte
100 b = &Block{
101 Headers: make(textproto.MIMEHeader),
102 }
103
104
105 for {
106
107
108 if len(rest) == 0 {
109 return nil, data
110 }
111
112 if line, rest = getLine(rest); len(line) == 0 {
113 break
114 }
115
116
117 if i := bytes.IndexFunc(line, func(r rune) bool {
118 return r < 0x20 || r > 0x7e
119 }); i != -1 {
120 return nil, data
121 }
122
123 i := bytes.Index(line, []byte{':'})
124 if i == -1 {
125 return nil, data
126 }
127
128 key, val := string(line[0:i]), string(line[i+1:])
129 key = strings.TrimSpace(key)
130 if key != "Hash" {
131 return nil, data
132 }
133 for _, val := range strings.Split(val, ",") {
134 val = strings.TrimSpace(val)
135 b.Headers.Add(key, val)
136 }
137 }
138
139 firstLine := true
140 for {
141 start := rest
142
143 line, rest = getLine(rest)
144 if len(line) == 0 && len(rest) == 0 {
145
146 return nil, data
147 }
148 if bytes.Equal(line, endText) {
149
150
151 rest = start
152 break
153 }
154
155
156
157 if firstLine {
158 firstLine = false
159 } else {
160 b.Bytes = append(b.Bytes, crlf...)
161 }
162
163 if bytes.HasPrefix(line, dashEscape) {
164 line = line[2:]
165 }
166 line = bytes.TrimRight(line, " \t")
167 b.Bytes = append(b.Bytes, line...)
168
169 b.Plaintext = append(b.Plaintext, line...)
170 b.Plaintext = append(b.Plaintext, lf)
171 }
172
173
174
175 i := bytes.Index(rest, end)
176 if i == -1 {
177 return nil, data
178 }
179 i += len(end)
180 for i < len(rest) && (rest[i] == '\r' || rest[i] == '\n') {
181 i++
182 }
183 armored := rest[:i]
184 rest = rest[i:]
185
186 var err error
187 b.ArmoredSignature, err = armor.Decode(bytes.NewBuffer(armored))
188 if err != nil {
189 return nil, data
190 }
191
192 return b, rest
193 }
194
195
196
197
198
199
200
201 type dashEscaper struct {
202 buffered *bufio.Writer
203 hashers []hash.Hash
204 hashType crypto.Hash
205 toHash io.Writer
206
207 atBeginningOfLine bool
208 isFirstLine bool
209
210 whitespace []byte
211 byteBuf []byte
212
213 privateKeys []*packet.PrivateKey
214 config *packet.Config
215 }
216
217 func (d *dashEscaper) Write(data []byte) (n int, err error) {
218 for _, b := range data {
219 d.byteBuf[0] = b
220
221 if d.atBeginningOfLine {
222
223
224 if !d.isFirstLine {
225 d.toHash.Write(crlf)
226 }
227 d.isFirstLine = false
228 }
229
230
231
232 if b == ' ' || b == '\t' || b == '\r' {
233 d.whitespace = append(d.whitespace, b)
234 d.atBeginningOfLine = false
235 continue
236 }
237
238 if d.atBeginningOfLine {
239
240 if b == '-' {
241
242
243 if _, err = d.buffered.Write(dashEscape); err != nil {
244 return
245 }
246 d.toHash.Write(d.byteBuf)
247 d.atBeginningOfLine = false
248 } else if b == '\n' {
249
250 } else {
251 d.toHash.Write(d.byteBuf)
252 d.atBeginningOfLine = false
253 }
254 if err = d.buffered.WriteByte(b); err != nil {
255 return
256 }
257 } else {
258 if b == '\n' {
259
260
261 d.whitespace = d.whitespace[:0]
262
263
264 if err = d.buffered.WriteByte(b); err != nil {
265 return
266 }
267 d.atBeginningOfLine = true
268 } else {
269
270
271 if len(d.whitespace) > 0 {
272 d.toHash.Write(d.whitespace)
273 if _, err = d.buffered.Write(d.whitespace); err != nil {
274 return
275 }
276 d.whitespace = d.whitespace[:0]
277 }
278 d.toHash.Write(d.byteBuf)
279 if err = d.buffered.WriteByte(b); err != nil {
280 return
281 }
282 }
283 }
284 }
285
286 n = len(data)
287 return
288 }
289
290 func (d *dashEscaper) Close() (err error) {
291 if !d.atBeginningOfLine {
292 if err = d.buffered.WriteByte(lf); err != nil {
293 return
294 }
295 }
296
297 out, err := armor.Encode(d.buffered, "PGP SIGNATURE", nil)
298 if err != nil {
299 return
300 }
301
302 t := d.config.Now()
303 for i, k := range d.privateKeys {
304 sig := new(packet.Signature)
305 sig.SigType = packet.SigTypeText
306 sig.PubKeyAlgo = k.PubKeyAlgo
307 sig.Hash = d.hashType
308 sig.CreationTime = t
309 sig.IssuerKeyId = &k.KeyId
310 sig.IssuerFingerprint = k.Fingerprint
311 sig.Notations = d.config.Notations()
312 sigLifetimeSecs := d.config.SigLifetime()
313 sig.SigLifetimeSecs = &sigLifetimeSecs
314
315 if err = sig.Sign(d.hashers[i], k, d.config); err != nil {
316 return
317 }
318 if err = sig.Serialize(out); err != nil {
319 return
320 }
321 }
322
323 if err = out.Close(); err != nil {
324 return
325 }
326 if err = d.buffered.Flush(); err != nil {
327 return
328 }
329 return
330 }
331
332
333
334 func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (plaintext io.WriteCloser, err error) {
335 return EncodeMulti(w, []*packet.PrivateKey{privateKey}, config)
336 }
337
338
339
340
341 func EncodeMulti(w io.Writer, privateKeys []*packet.PrivateKey, config *packet.Config) (plaintext io.WriteCloser, err error) {
342 for _, k := range privateKeys {
343 if k.Encrypted {
344 return nil, errors.InvalidArgumentError(fmt.Sprintf("signing key %s is encrypted", k.KeyIdString()))
345 }
346 }
347
348 hashType := config.Hash()
349 name := nameOfHash(hashType)
350 if len(name) == 0 {
351 return nil, errors.UnsupportedError("unknown hash type: " + strconv.Itoa(int(hashType)))
352 }
353
354 if !hashType.Available() {
355 return nil, errors.UnsupportedError("unsupported hash type: " + strconv.Itoa(int(hashType)))
356 }
357 var hashers []hash.Hash
358 var ws []io.Writer
359 for range privateKeys {
360 h := hashType.New()
361 hashers = append(hashers, h)
362 ws = append(ws, h)
363 }
364 toHash := io.MultiWriter(ws...)
365
366 buffered := bufio.NewWriter(w)
367
368 if _, err = buffered.Write(start[1:]); err != nil {
369 return
370 }
371 if err = buffered.WriteByte(lf); err != nil {
372 return
373 }
374 if _, err = buffered.WriteString("Hash: "); err != nil {
375 return
376 }
377 if _, err = buffered.WriteString(name); err != nil {
378 return
379 }
380 if err = buffered.WriteByte(lf); err != nil {
381 return
382 }
383 if err = buffered.WriteByte(lf); err != nil {
384 return
385 }
386
387 plaintext = &dashEscaper{
388 buffered: buffered,
389 hashers: hashers,
390 hashType: hashType,
391 toHash: toHash,
392
393 atBeginningOfLine: true,
394 isFirstLine: true,
395
396 byteBuf: make([]byte, 1),
397
398 privateKeys: privateKeys,
399 config: config,
400 }
401
402 return
403 }
404
405
406
407 func (b *Block) VerifySignature(keyring openpgp.KeyRing, config *packet.Config) (signer *openpgp.Entity, err error) {
408 var expectedHashes []crypto.Hash
409 for _, v := range b.Headers {
410 for _, name := range v {
411 expectedHash := nameToHash(name)
412 if uint8(expectedHash) == 0 {
413 return nil, errors.StructuralError("unknown hash algorithm in cleartext message headers")
414 }
415 expectedHashes = append(expectedHashes, expectedHash)
416 }
417 }
418 if len(expectedHashes) == 0 {
419 expectedHashes = append(expectedHashes, crypto.MD5)
420 }
421 return openpgp.CheckDetachedSignatureAndHash(keyring, bytes.NewBuffer(b.Bytes), b.ArmoredSignature.Body, expectedHashes, config)
422 }
423
424
425
426 func nameOfHash(h crypto.Hash) string {
427 switch h {
428 case crypto.SHA224:
429 return "SHA224"
430 case crypto.SHA256:
431 return "SHA256"
432 case crypto.SHA384:
433 return "SHA384"
434 case crypto.SHA512:
435 return "SHA512"
436 case crypto.SHA3_256:
437 return "SHA3-256"
438 case crypto.SHA3_512:
439 return "SHA3-512"
440 }
441 return ""
442 }
443
444
445
446 func nameToHash(h string) crypto.Hash {
447 switch h {
448 case "SHA1":
449 return crypto.SHA1
450 case "SHA224":
451 return crypto.SHA224
452 case "SHA256":
453 return crypto.SHA256
454 case "SHA384":
455 return crypto.SHA384
456 case "SHA512":
457 return crypto.SHA512
458 case "SHA3-256":
459 return crypto.SHA3_256
460 case "SHA3-512":
461 return crypto.SHA3_512
462 }
463 return crypto.Hash(0)
464 }
465
View as plain text