...

Source file src/golang.org/x/crypto/openpgp/clearsign/clearsign.go

Documentation: golang.org/x/crypto/openpgp/clearsign

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

View as plain text