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