github.com/maenmax/kairep@v0.0.0-20210218001208-55bf3df36788/src/golang.org/x/crypto/openpgp/clearsign/clearsign.go (about)

     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 len(line) == 0 && len(rest) == 0 {
   122  			// No armored data was found, so this isn't a complete message.
   123  			return nil, data
   124  		}
   125  		if bytes.Equal(line, endText) {
   126  			// Back up to the start of the line because armor expects to see the
   127  			// header line.
   128  			rest = start
   129  			break
   130  		}
   131  
   132  		// The final CRLF isn't included in the hash so we don't write it until
   133  		// we've seen the next line.
   134  		if firstLine {
   135  			firstLine = false
   136  		} else {
   137  			b.Bytes = append(b.Bytes, crlf...)
   138  		}
   139  
   140  		if bytes.HasPrefix(line, dashEscape) {
   141  			line = line[2:]
   142  		}
   143  		line = bytes.TrimRight(line, " \t")
   144  		b.Bytes = append(b.Bytes, line...)
   145  
   146  		b.Plaintext = append(b.Plaintext, line...)
   147  		b.Plaintext = append(b.Plaintext, lf)
   148  	}
   149  
   150  	// We want to find the extent of the armored data (including any newlines at
   151  	// the end).
   152  	i := bytes.Index(rest, end)
   153  	if i == -1 {
   154  		return nil, data
   155  	}
   156  	i += len(end)
   157  	for i < len(rest) && (rest[i] == '\r' || rest[i] == '\n') {
   158  		i++
   159  	}
   160  	armored := rest[:i]
   161  	rest = rest[i:]
   162  
   163  	var err error
   164  	b.ArmoredSignature, err = armor.Decode(bytes.NewBuffer(armored))
   165  	if err != nil {
   166  		return nil, data
   167  	}
   168  
   169  	return b, rest
   170  }
   171  
   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.
   175  //
   176  // When closed, an armored signature is created and written to complete the
   177  // message.
   178  type dashEscaper struct {
   179  	buffered *bufio.Writer
   180  	h        hash.Hash
   181  	hashType crypto.Hash
   182  
   183  	atBeginningOfLine bool
   184  	isFirstLine       bool
   185  
   186  	whitespace []byte
   187  	byteBuf    []byte // a one byte buffer to save allocations
   188  
   189  	privateKey *packet.PrivateKey
   190  	config     *packet.Config
   191  }
   192  
   193  func (d *dashEscaper) Write(data []byte) (n int, err error) {
   194  	for _, b := range data {
   195  		d.byteBuf[0] = b
   196  
   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.
   200  			if !d.isFirstLine {
   201  				d.h.Write(crlf)
   202  			}
   203  			d.isFirstLine = false
   204  		}
   205  
   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
   211  			continue
   212  		}
   213  
   214  		if d.atBeginningOfLine {
   215  			// At the beginning of a line, hyphens have to be escaped.
   216  			if b == '-' {
   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 {
   220  					return
   221  				}
   222  				d.h.Write(d.byteBuf)
   223  				d.atBeginningOfLine = false
   224  			} else if b == '\n' {
   225  				// Nothing to do because we delay writing CRLF to the hash.
   226  			} else {
   227  				d.h.Write(d.byteBuf)
   228  				d.atBeginningOfLine = false
   229  			}
   230  			if err = d.buffered.WriteByte(b); err != nil {
   231  				return
   232  			}
   233  		} else {
   234  			if b == '\n' {
   235  				// We got a raw \n. Drop any trailing whitespace and write a
   236  				// CRLF.
   237  				d.whitespace = d.whitespace[:0]
   238  				// We delay writing CRLF to the hash until the start of the
   239  				// next line.
   240  				if err = d.buffered.WriteByte(b); err != nil {
   241  					return
   242  				}
   243  				d.atBeginningOfLine = true
   244  			} else {
   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 {
   250  						return
   251  					}
   252  					d.whitespace = d.whitespace[:0]
   253  				}
   254  				d.h.Write(d.byteBuf)
   255  				if err = d.buffered.WriteByte(b); err != nil {
   256  					return
   257  				}
   258  			}
   259  		}
   260  	}
   261  
   262  	n = len(data)
   263  	return
   264  }
   265  
   266  func (d *dashEscaper) Close() (err error) {
   267  	if !d.atBeginningOfLine {
   268  		if err = d.buffered.WriteByte(lf); err != nil {
   269  			return
   270  		}
   271  	}
   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
   278  
   279  	if err = sig.Sign(d.h, d.privateKey, d.config); err != nil {
   280  		return
   281  	}
   282  
   283  	out, err := armor.Encode(d.buffered, "PGP SIGNATURE", nil)
   284  	if err != nil {
   285  		return
   286  	}
   287  
   288  	if err = sig.Serialize(out); err != nil {
   289  		return
   290  	}
   291  	if err = out.Close(); err != nil {
   292  		return
   293  	}
   294  	if err = d.buffered.Flush(); err != nil {
   295  		return
   296  	}
   297  	return
   298  }
   299  
   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")
   305  	}
   306  
   307  	hashType := config.Hash()
   308  	name := nameOfHash(hashType)
   309  	if len(name) == 0 {
   310  		return nil, errors.UnsupportedError("unknown hash type: " + strconv.Itoa(int(hashType)))
   311  	}
   312  
   313  	if !hashType.Available() {
   314  		return nil, errors.UnsupportedError("unsupported hash type: " + strconv.Itoa(int(hashType)))
   315  	}
   316  	h := hashType.New()
   317  
   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 {
   321  		return
   322  	}
   323  	if err = buffered.WriteByte(lf); err != nil {
   324  		return
   325  	}
   326  	if _, err = buffered.WriteString("Hash: "); err != nil {
   327  		return
   328  	}
   329  	if _, err = buffered.WriteString(name); err != nil {
   330  		return
   331  	}
   332  	if err = buffered.WriteByte(lf); err != nil {
   333  		return
   334  	}
   335  	if err = buffered.WriteByte(lf); err != nil {
   336  		return
   337  	}
   338  
   339  	plaintext = &dashEscaper{
   340  		buffered: buffered,
   341  		h:        h,
   342  		hashType: hashType,
   343  
   344  		atBeginningOfLine: true,
   345  		isFirstLine:       true,
   346  
   347  		byteBuf: make([]byte, 1),
   348  
   349  		privateKey: privateKey,
   350  		config:     config,
   351  	}
   352  
   353  	return
   354  }
   355  
   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 {
   359  	switch h {
   360  	case crypto.MD5:
   361  		return "MD5"
   362  	case crypto.SHA1:
   363  		return "SHA1"
   364  	case crypto.RIPEMD160:
   365  		return "RIPEMD160"
   366  	case crypto.SHA224:
   367  		return "SHA224"
   368  	case crypto.SHA256:
   369  		return "SHA256"
   370  	case crypto.SHA384:
   371  		return "SHA384"
   372  	case crypto.SHA512:
   373  		return "SHA512"
   374  	}
   375  	return ""
   376  }