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 len(line) == 0 && len(rest) == 0 {
122 // No armored data was found, so this isn't a complete message.
125 if bytes.Equal(line, endText) {
126 // Back up to the start of the line because armor expects to see the
132 // The final CRLF isn't included in the hash so we don't write it until
133 // we've seen the next line.
137 b.Bytes = append(b.Bytes, crlf...)
140 if bytes.HasPrefix(line, dashEscape) {
143 line = bytes.TrimRight(line, " \t")
144 b.Bytes = append(b.Bytes, line...)
146 b.Plaintext = append(b.Plaintext, line...)
147 b.Plaintext = append(b.Plaintext, lf)
150 // We want to find the extent of the armored data (including any newlines at
152 i := bytes.Index(rest, end)
157 for i < len(rest) && (rest[i] == '\r' || rest[i] == '\n') {
164 b.ArmoredSignature, err = armor.Decode(bytes.NewBuffer(armored))
172 // A dashEscaper is an io.WriteCloser which processes the body of a clear-signed
173 // message. The clear-signed message is written to buffered and a hash, suitable
174 // for signing, is maintained in h.
176 // When closed, an armored signature is created and written to complete the
178 type dashEscaper struct {
179 buffered *bufio.Writer
183 atBeginningOfLine bool
187 byteBuf []byte // a one byte buffer to save allocations
189 privateKey *packet.PrivateKey
190 config *packet.Config
193 func (d *dashEscaper) Write(data []byte) (n int, err error) {
194 for _, b := range data {
197 if d.atBeginningOfLine {
198 // The final CRLF isn't included in the hash so we have to wait
199 // until this point (the start of the next line) before writing it.
203 d.isFirstLine = false
206 // Any whitespace at the end of the line has to be removed so we
207 // buffer it until we find out whether there's more on this line.
208 if b == ' ' || b == '\t' || b == '\r' {
209 d.whitespace = append(d.whitespace, b)
210 d.atBeginningOfLine = false
214 if d.atBeginningOfLine {
215 // At the beginning of a line, hyphens have to be escaped.
217 // The signature isn't calculated over the dash-escaped text so
218 // the escape is only written to buffered.
219 if _, err = d.buffered.Write(dashEscape); err != nil {
223 d.atBeginningOfLine = false
224 } else if b == '\n' {
225 // Nothing to do because we delay writing CRLF to the hash.
228 d.atBeginningOfLine = false
230 if err = d.buffered.WriteByte(b); err != nil {
235 // We got a raw \n. Drop any trailing whitespace and write a
237 d.whitespace = d.whitespace[:0]
238 // We delay writing CRLF to the hash until the start of the
240 if err = d.buffered.WriteByte(b); err != nil {
243 d.atBeginningOfLine = true
245 // Any buffered whitespace wasn't at the end of the line so
246 // we need to write it out.
247 if len(d.whitespace) > 0 {
248 d.h.Write(d.whitespace)
249 if _, err = d.buffered.Write(d.whitespace); err != nil {
252 d.whitespace = d.whitespace[:0]
255 if err = d.buffered.WriteByte(b); err != nil {
266 func (d *dashEscaper) Close() (err error) {
267 if !d.atBeginningOfLine {
268 if err = d.buffered.WriteByte(lf); err != nil {
272 sig := new(packet.Signature)
273 sig.SigType = packet.SigTypeText
274 sig.PubKeyAlgo = d.privateKey.PubKeyAlgo
275 sig.Hash = d.hashType
276 sig.CreationTime = d.config.Now()
277 sig.IssuerKeyId = &d.privateKey.KeyId
279 if err = sig.Sign(d.h, d.privateKey, d.config); err != nil {
283 out, err := armor.Encode(d.buffered, "PGP SIGNATURE", nil)
288 if err = sig.Serialize(out); err != nil {
291 if err = out.Close(); err != nil {
294 if err = d.buffered.Flush(); err != nil {
300 // Encode returns a WriteCloser which will clear-sign a message with privateKey
301 // and write it to w. If config is nil, sensible defaults are used.
302 func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (plaintext io.WriteCloser, err error) {
303 if privateKey.Encrypted {
304 return nil, errors.InvalidArgumentError("signing key is encrypted")
307 hashType := config.Hash()
308 name := nameOfHash(hashType)
310 return nil, errors.UnsupportedError("unknown hash type: " + strconv.Itoa(int(hashType)))
313 if !hashType.Available() {
314 return nil, errors.UnsupportedError("unsupported hash type: " + strconv.Itoa(int(hashType)))
318 buffered := bufio.NewWriter(w)
319 // start has a \n at the beginning that we don't want here.
320 if _, err = buffered.Write(start[1:]); err != nil {
323 if err = buffered.WriteByte(lf); err != nil {
326 if _, err = buffered.WriteString("Hash: "); err != nil {
329 if _, err = buffered.WriteString(name); err != nil {
332 if err = buffered.WriteByte(lf); err != nil {
335 if err = buffered.WriteByte(lf); err != nil {
339 plaintext = &dashEscaper{
344 atBeginningOfLine: true,
347 byteBuf: make([]byte, 1),
349 privateKey: privateKey,
356 // nameOfHash returns the OpenPGP name for the given hash, or the empty string
357 // if the name isn't known. See RFC 4880, section 9.4.
358 func nameOfHash(h crypto.Hash) string {
364 case crypto.RIPEMD160: