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