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.
5
// Package clearsign generates and processes OpenPGP, clear-signed data. See
6
// RFC 4880, section 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"
21
"golang.org/x/crypto/openpgp/armor"
22
"golang.org/x/crypto/openpgp/errors"
23
"golang.org/x/crypto/openpgp/packet"
26
// A Block represents a clearsigned message. A signature on a Block can
27
// be checked by passing Bytes into openpgp.CheckDetachedSignature.
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
35
// start is the marker which denotes the beginning of a clearsigned message.
36
var start = []byte("\n-----BEGIN PGP SIGNED MESSAGE-----")
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("- ")
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-----")
46
// end is a marker which denotes the end of the armored signature.
47
var end = []byte("\n-----END PGP SIGNATURE-----")
49
var crlf = []byte("\r\n")
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'})
64
if i > 0 && data[i-1] == '\r' {
68
return data[0:i], data[j:]
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.
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):]
85
// Consume the start line.
86
_, rest = getLine(rest)
90
Headers: make(textproto.MIMEHeader),
93
// Next come a series of header lines.
95
// This loop terminates because getLine's second result is
96
// always smaller than its argument.
100
// An empty line marks the end of the headers.
101
if line, rest = getLine(rest); len(line) == 0 {
105
i := bytes.Index(line, []byte{':'})
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))
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
128
// The final CRLF isn't included in the hash so we don't write it until
129
// we've seen the next line.
133
b.Bytes = append(b.Bytes, crlf...)
136
if bytes.HasPrefix(line, dashEscape) {
139
line = bytes.TrimRight(line, " \t")
140
b.Bytes = append(b.Bytes, line...)
142
b.Plaintext = append(b.Plaintext, line...)
143
b.Plaintext = append(b.Plaintext, lf)
146
// We want to find the extent of the armored data (including any newlines at
148
i := bytes.Index(rest, end)
153
for i < len(rest) && (rest[i] == '\r' || rest[i] == '\n') {
160
b.ArmoredSignature, err = armor.Decode(bytes.NewBuffer(armored))
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.
172
// When closed, an armored signature is created and written to complete the
174
type dashEscaper struct {
175
buffered *bufio.Writer
179
atBeginningOfLine bool
183
byteBuf []byte // a one byte buffer to save allocations
185
privateKey *packet.PrivateKey
186
config *packet.Config
189
func (d *dashEscaper) Write(data []byte) (n int, err error) {
190
for _, b := range data {
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.
199
d.isFirstLine = false
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
210
if d.atBeginningOfLine {
211
// At the beginning of a line, hyphens have to be escaped.
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 {
219
d.atBeginningOfLine = false
220
} else if b == '\n' {
221
// Nothing to do because we delay writing CRLF to the hash.
224
d.atBeginningOfLine = false
226
if err = d.buffered.WriteByte(b); err != nil {
231
// We got a raw \n. Drop any trailing whitespace and write a
233
d.whitespace = d.whitespace[:0]
234
// We delay writing CRLF to the hash until the start of the
236
if err = d.buffered.WriteByte(b); err != nil {
239
d.atBeginningOfLine = true
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 {
248
d.whitespace = d.whitespace[:0]
251
if err = d.buffered.WriteByte(b); err != nil {
262
func (d *dashEscaper) Close() (err error) {
263
if !d.atBeginningOfLine {
264
if err = d.buffered.WriteByte(lf); err != nil {
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
275
if err = sig.Sign(d.h, d.privateKey, d.config); err != nil {
279
out, err := armor.Encode(d.buffered, "PGP SIGNATURE", nil)
284
if err = sig.Serialize(out); err != nil {
287
if err = out.Close(); err != nil {
290
if err = d.buffered.Flush(); err != nil {
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")
303
hashType := config.Hash()
304
name := nameOfHash(hashType)
306
return nil, errors.UnsupportedError("unknown hash type: " + strconv.Itoa(int(hashType)))
309
if !hashType.Available() {
310
return nil, errors.UnsupportedError("unsupported hash type: " + strconv.Itoa(int(hashType)))
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 {
319
if err = buffered.WriteByte(lf); err != nil {
322
if _, err = buffered.WriteString("Hash: "); err != nil {
325
if _, err = buffered.WriteString(name); err != nil {
328
if err = buffered.WriteByte(lf); err != nil {
331
if err = buffered.WriteByte(lf); err != nil {
335
plaintext = &dashEscaper{
340
atBeginningOfLine: true,
343
byteBuf: make([]byte, 1),
345
privateKey: privateKey,
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 {
360
case crypto.RIPEMD160: