~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/golang.org/x/crypto/openpgp/clearsign/clearsign.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2012 The Go Authors. All rights reserved.
 
2
// Use of this source code is governed by a BSD-style
 
3
// license that can be found in the LICENSE file.
 
4
 
 
5
// Package clearsign generates and processes OpenPGP, clear-signed data. See
 
6
// RFC 4880, section 7.
 
7
//
 
8
// Clearsigned messages are cryptographically signed, but the contents of the
 
9
// message are kept in plaintext so that it can be read without special tools.
 
10
package clearsign // import "golang.org/x/crypto/openpgp/clearsign"
 
11
 
 
12
import (
 
13
        "bufio"
 
14
        "bytes"
 
15
        "crypto"
 
16
        "hash"
 
17
        "io"
 
18
        "net/textproto"
 
19
        "strconv"
 
20
 
 
21
        "golang.org/x/crypto/openpgp/armor"
 
22
        "golang.org/x/crypto/openpgp/errors"
 
23
        "golang.org/x/crypto/openpgp/packet"
 
24
)
 
25
 
 
26
// A Block represents a clearsigned message. A signature on a Block can
 
27
// be checked by passing Bytes into openpgp.CheckDetachedSignature.
 
28
type Block struct {
 
29
        Headers          textproto.MIMEHeader // Optional message headers
 
30
        Plaintext        []byte               // The original message text
 
31
        Bytes            []byte               // The signed message
 
32
        ArmoredSignature *armor.Block         // The signature block
 
33
}
 
34
 
 
35
// start is the marker which denotes the beginning of a clearsigned message.
 
36
var start = []byte("\n-----BEGIN PGP SIGNED MESSAGE-----")
 
37
 
 
38
// dashEscape is prefixed to any lines that begin with a hyphen so that they
 
39
// can't be confused with endText.
 
40
var dashEscape = []byte("- ")
 
41
 
 
42
// endText is a marker which denotes the end of the message and the start of
 
43
// an armored signature.
 
44
var endText = []byte("-----BEGIN PGP SIGNATURE-----")
 
45
 
 
46
// end is a marker which denotes the end of the armored signature.
 
47
var end = []byte("\n-----END PGP SIGNATURE-----")
 
48
 
 
49
var crlf = []byte("\r\n")
 
50
var lf = byte('\n')
 
51
 
 
52
// getLine returns the first \r\n or \n delineated line from the given byte
 
53
// array. The line does not include the \r\n or \n. The remainder of the byte
 
54
// array (also not including the new line bytes) is also returned and this will
 
55
// always be smaller than the original argument.
 
56
func getLine(data []byte) (line, rest []byte) {
 
57
        i := bytes.Index(data, []byte{'\n'})
 
58
        var j int
 
59
        if i < 0 {
 
60
                i = len(data)
 
61
                j = i
 
62
        } else {
 
63
                j = i + 1
 
64
                if i > 0 && data[i-1] == '\r' {
 
65
                        i--
 
66
                }
 
67
        }
 
68
        return data[0:i], data[j:]
 
69
}
 
70
 
 
71
// Decode finds the first clearsigned message in data and returns it, as well
 
72
// as the suffix of data which remains after the message.
 
73
func Decode(data []byte) (b *Block, rest []byte) {
 
74
        // start begins with a newline. However, at the very beginning of
 
75
        // the byte array, we'll accept the start string without it.
 
76
        rest = data
 
77
        if bytes.HasPrefix(data, start[1:]) {
 
78
                rest = rest[len(start)-1:]
 
79
        } else if i := bytes.Index(data, start); i >= 0 {
 
80
                rest = rest[i+len(start):]
 
81
        } else {
 
82
                return nil, data
 
83
        }
 
84
 
 
85
        // Consume the start line.
 
86
        _, rest = getLine(rest)
 
87
 
 
88
        var line []byte
 
89
        b = &Block{
 
90
                Headers: make(textproto.MIMEHeader),
 
91
        }
 
92
 
 
93
        // Next come a series of header lines.
 
94
        for {
 
95
                // This loop terminates because getLine's second result is
 
96
                // always smaller than its argument.
 
97
                if len(rest) == 0 {
 
98
                        return nil, data
 
99
                }
 
100
                // An empty line marks the end of the headers.
 
101
                if line, rest = getLine(rest); len(line) == 0 {
 
102
                        break
 
103
                }
 
104
 
 
105
                i := bytes.Index(line, []byte{':'})
 
106
                if i == -1 {
 
107
                        return nil, data
 
108
                }
 
109
 
 
110
                key, val := line[0:i], line[i+1:]
 
111
                key = bytes.TrimSpace(key)
 
112
                val = bytes.TrimSpace(val)
 
113
                b.Headers.Add(string(key), string(val))
 
114
        }
 
115
 
 
116
        firstLine := true
 
117
        for {
 
118
                start := rest
 
119
 
 
120
                line, rest = getLine(rest)
 
121
                if bytes.Equal(line, endText) {
 
122
                        // Back up to the start of the line because armor expects to see the
 
123
                        // header line.
 
124
                        rest = start
 
125
                        break
 
126
                }
 
127
 
 
128
                // The final CRLF isn't included in the hash so we don't write it until
 
129
                // we've seen the next line.
 
130
                if firstLine {
 
131
                        firstLine = false
 
132
                } else {
 
133
                        b.Bytes = append(b.Bytes, crlf...)
 
134
                }
 
135
 
 
136
                if bytes.HasPrefix(line, dashEscape) {
 
137
                        line = line[2:]
 
138
                }
 
139
                line = bytes.TrimRight(line, " \t")
 
140
                b.Bytes = append(b.Bytes, line...)
 
141
 
 
142
                b.Plaintext = append(b.Plaintext, line...)
 
143
                b.Plaintext = append(b.Plaintext, lf)
 
144
        }
 
145
 
 
146
        // We want to find the extent of the armored data (including any newlines at
 
147
        // the end).
 
148
        i := bytes.Index(rest, end)
 
149
        if i == -1 {
 
150
                return nil, data
 
151
        }
 
152
        i += len(end)
 
153
        for i < len(rest) && (rest[i] == '\r' || rest[i] == '\n') {
 
154
                i++
 
155
        }
 
156
        armored := rest[:i]
 
157
        rest = rest[i:]
 
158
 
 
159
        var err error
 
160
        b.ArmoredSignature, err = armor.Decode(bytes.NewBuffer(armored))
 
161
        if err != nil {
 
162
                return nil, data
 
163
        }
 
164
 
 
165
        return b, rest
 
166
}
 
167
 
 
168
// A dashEscaper is an io.WriteCloser which processes the body of a clear-signed
 
169
// message. The clear-signed message is written to buffered and a hash, suitable
 
170
// for signing, is maintained in h.
 
171
//
 
172
// When closed, an armored signature is created and written to complete the
 
173
// message.
 
174
type dashEscaper struct {
 
175
        buffered *bufio.Writer
 
176
        h        hash.Hash
 
177
        hashType crypto.Hash
 
178
 
 
179
        atBeginningOfLine bool
 
180
        isFirstLine       bool
 
181
 
 
182
        whitespace []byte
 
183
        byteBuf    []byte // a one byte buffer to save allocations
 
184
 
 
185
        privateKey *packet.PrivateKey
 
186
        config     *packet.Config
 
187
}
 
188
 
 
189
func (d *dashEscaper) Write(data []byte) (n int, err error) {
 
190
        for _, b := range data {
 
191
                d.byteBuf[0] = b
 
192
 
 
193
                if d.atBeginningOfLine {
 
194
                        // The final CRLF isn't included in the hash so we have to wait
 
195
                        // until this point (the start of the next line) before writing it.
 
196
                        if !d.isFirstLine {
 
197
                                d.h.Write(crlf)
 
198
                        }
 
199
                        d.isFirstLine = false
 
200
                }
 
201
 
 
202
                // Any whitespace at the end of the line has to be removed so we
 
203
                // buffer it until we find out whether there's more on this line.
 
204
                if b == ' ' || b == '\t' || b == '\r' {
 
205
                        d.whitespace = append(d.whitespace, b)
 
206
                        d.atBeginningOfLine = false
 
207
                        continue
 
208
                }
 
209
 
 
210
                if d.atBeginningOfLine {
 
211
                        // At the beginning of a line, hyphens have to be escaped.
 
212
                        if b == '-' {
 
213
                                // The signature isn't calculated over the dash-escaped text so
 
214
                                // the escape is only written to buffered.
 
215
                                if _, err = d.buffered.Write(dashEscape); err != nil {
 
216
                                        return
 
217
                                }
 
218
                                d.h.Write(d.byteBuf)
 
219
                                d.atBeginningOfLine = false
 
220
                        } else if b == '\n' {
 
221
                                // Nothing to do because we delay writing CRLF to the hash.
 
222
                        } else {
 
223
                                d.h.Write(d.byteBuf)
 
224
                                d.atBeginningOfLine = false
 
225
                        }
 
226
                        if err = d.buffered.WriteByte(b); err != nil {
 
227
                                return
 
228
                        }
 
229
                } else {
 
230
                        if b == '\n' {
 
231
                                // We got a raw \n. Drop any trailing whitespace and write a
 
232
                                // CRLF.
 
233
                                d.whitespace = d.whitespace[:0]
 
234
                                // We delay writing CRLF to the hash until the start of the
 
235
                                // next line.
 
236
                                if err = d.buffered.WriteByte(b); err != nil {
 
237
                                        return
 
238
                                }
 
239
                                d.atBeginningOfLine = true
 
240
                        } else {
 
241
                                // Any buffered whitespace wasn't at the end of the line so
 
242
                                // we need to write it out.
 
243
                                if len(d.whitespace) > 0 {
 
244
                                        d.h.Write(d.whitespace)
 
245
                                        if _, err = d.buffered.Write(d.whitespace); err != nil {
 
246
                                                return
 
247
                                        }
 
248
                                        d.whitespace = d.whitespace[:0]
 
249
                                }
 
250
                                d.h.Write(d.byteBuf)
 
251
                                if err = d.buffered.WriteByte(b); err != nil {
 
252
                                        return
 
253
                                }
 
254
                        }
 
255
                }
 
256
        }
 
257
 
 
258
        n = len(data)
 
259
        return
 
260
}
 
261
 
 
262
func (d *dashEscaper) Close() (err error) {
 
263
        if !d.atBeginningOfLine {
 
264
                if err = d.buffered.WriteByte(lf); err != nil {
 
265
                        return
 
266
                }
 
267
        }
 
268
        sig := new(packet.Signature)
 
269
        sig.SigType = packet.SigTypeText
 
270
        sig.PubKeyAlgo = d.privateKey.PubKeyAlgo
 
271
        sig.Hash = d.hashType
 
272
        sig.CreationTime = d.config.Now()
 
273
        sig.IssuerKeyId = &d.privateKey.KeyId
 
274
 
 
275
        if err = sig.Sign(d.h, d.privateKey, d.config); err != nil {
 
276
                return
 
277
        }
 
278
 
 
279
        out, err := armor.Encode(d.buffered, "PGP SIGNATURE", nil)
 
280
        if err != nil {
 
281
                return
 
282
        }
 
283
 
 
284
        if err = sig.Serialize(out); err != nil {
 
285
                return
 
286
        }
 
287
        if err = out.Close(); err != nil {
 
288
                return
 
289
        }
 
290
        if err = d.buffered.Flush(); err != nil {
 
291
                return
 
292
        }
 
293
        return
 
294
}
 
295
 
 
296
// Encode returns a WriteCloser which will clear-sign a message with privateKey
 
297
// and write it to w. If config is nil, sensible defaults are used.
 
298
func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (plaintext io.WriteCloser, err error) {
 
299
        if privateKey.Encrypted {
 
300
                return nil, errors.InvalidArgumentError("signing key is encrypted")
 
301
        }
 
302
 
 
303
        hashType := config.Hash()
 
304
        name := nameOfHash(hashType)
 
305
        if len(name) == 0 {
 
306
                return nil, errors.UnsupportedError("unknown hash type: " + strconv.Itoa(int(hashType)))
 
307
        }
 
308
 
 
309
        if !hashType.Available() {
 
310
                return nil, errors.UnsupportedError("unsupported hash type: " + strconv.Itoa(int(hashType)))
 
311
        }
 
312
        h := hashType.New()
 
313
 
 
314
        buffered := bufio.NewWriter(w)
 
315
        // start has a \n at the beginning that we don't want here.
 
316
        if _, err = buffered.Write(start[1:]); err != nil {
 
317
                return
 
318
        }
 
319
        if err = buffered.WriteByte(lf); err != nil {
 
320
                return
 
321
        }
 
322
        if _, err = buffered.WriteString("Hash: "); err != nil {
 
323
                return
 
324
        }
 
325
        if _, err = buffered.WriteString(name); err != nil {
 
326
                return
 
327
        }
 
328
        if err = buffered.WriteByte(lf); err != nil {
 
329
                return
 
330
        }
 
331
        if err = buffered.WriteByte(lf); err != nil {
 
332
                return
 
333
        }
 
334
 
 
335
        plaintext = &dashEscaper{
 
336
                buffered: buffered,
 
337
                h:        h,
 
338
                hashType: hashType,
 
339
 
 
340
                atBeginningOfLine: true,
 
341
                isFirstLine:       true,
 
342
 
 
343
                byteBuf: make([]byte, 1),
 
344
 
 
345
                privateKey: privateKey,
 
346
                config:     config,
 
347
        }
 
348
 
 
349
        return
 
350
}
 
351
 
 
352
// nameOfHash returns the OpenPGP name for the given hash, or the empty string
 
353
// if the name isn't known. See RFC 4880, section 9.4.
 
354
func nameOfHash(h crypto.Hash) string {
 
355
        switch h {
 
356
        case crypto.MD5:
 
357
                return "MD5"
 
358
        case crypto.SHA1:
 
359
                return "SHA1"
 
360
        case crypto.RIPEMD160:
 
361
                return "RIPEMD160"
 
362
        case crypto.SHA224:
 
363
                return "SHA224"
 
364
        case crypto.SHA256:
 
365
                return "SHA256"
 
366
        case crypto.SHA384:
 
367
                return "SHA384"
 
368
        case crypto.SHA512:
 
369
                return "SHA512"
 
370
        }
 
371
        return ""
 
372
}