github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/third_party/code.google.com/p/go.crypto/openpgp/write.go (about)

     1  // Copyright 2011 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 openpgp
     6  
     7  import (
     8  	"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/armor"
     9  	"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors"
    10  	"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/packet"
    11  	"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/s2k"
    12  	"crypto"
    13  	"crypto/rand"
    14  	_ "crypto/sha256"
    15  	"hash"
    16  	"io"
    17  	"strconv"
    18  	"time"
    19  )
    20  
    21  // DetachSign signs message with the private key from signer (which must
    22  // already have been decrypted) and writes the signature to w.
    23  func DetachSign(w io.Writer, signer *Entity, message io.Reader) error {
    24  	return detachSign(w, signer, message, time.Time{}, packet.SigTypeBinary)
    25  }
    26  
    27  // ArmoredDetachSign signs message with the private key from signer (which
    28  // must already have been decrypted) and writes an armored signature to w.
    29  func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader) (err error) {
    30  	return armoredDetachSign(w, signer, message, time.Time{}, packet.SigTypeBinary)
    31  }
    32  
    33  func ArmoredDetachSignAt(w io.Writer, signer *Entity, sigTime time.Time, message io.Reader) (err error) {
    34  	return armoredDetachSign(w, signer, message, sigTime, packet.SigTypeBinary)
    35  }
    36  
    37  // DetachSignText signs message (after canonicalising the line endings) with
    38  // the private key from signer (which must already have been decrypted) and
    39  // writes the signature to w.
    40  func DetachSignText(w io.Writer, signer *Entity, message io.Reader) error {
    41  	return detachSign(w, signer, message, time.Time{}, packet.SigTypeText)
    42  }
    43  
    44  // ArmoredDetachSignText signs message (after canonicalising the line endings)
    45  // with the private key from signer (which must already have been decrypted)
    46  // and writes an armored signature to w.
    47  func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader) error {
    48  	return armoredDetachSign(w, signer, message, time.Time{}, packet.SigTypeText)
    49  }
    50  
    51  func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigTime time.Time, sigType packet.SignatureType) (err error) {
    52  	out, err := armor.Encode(w, SignatureType, nil)
    53  	if err != nil {
    54  		return
    55  	}
    56  	err = detachSign(out, signer, message, sigTime, sigType)
    57  	if err != nil {
    58  		return
    59  	}
    60  	return out.Close()
    61  }
    62  
    63  func detachSign(w io.Writer, signer *Entity, message io.Reader, sigTime time.Time, sigType packet.SignatureType) (err error) {
    64  	if signer.PrivateKey == nil {
    65  		return errors.InvalidArgumentError("signing key doesn't have a private key")
    66  	}
    67  	if signer.PrivateKey.Encrypted {
    68  		return errors.InvalidArgumentError("signing key is encrypted")
    69  	}
    70  	if sigTime.IsZero() {
    71  		sigTime = time.Now()
    72  	}
    73  
    74  	sig := new(packet.Signature)
    75  	sig.SigType = sigType
    76  	sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo
    77  	sig.Hash = crypto.SHA256
    78  	sig.CreationTime = sigTime
    79  	sig.IssuerKeyId = &signer.PrivateKey.KeyId
    80  
    81  	h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
    82  	if err != nil {
    83  		return
    84  	}
    85  	io.Copy(wrappedHash, message)
    86  
    87  	err = sig.Sign(rand.Reader, h, signer.PrivateKey)
    88  	if err != nil {
    89  		return
    90  	}
    91  
    92  	return sig.Serialize(w)
    93  }
    94  
    95  // FileHints contains metadata about encrypted files. This metadata is, itself,
    96  // encrypted.
    97  type FileHints struct {
    98  	// IsBinary can be set to hint that the contents are binary data.
    99  	IsBinary bool
   100  	// FileName hints at the name of the file that should be written. It's
   101  	// truncated to 255 bytes if longer. It may be empty to suggest that the
   102  	// file should not be written to disk. It may be equal to "_CONSOLE" to
   103  	// suggest the data should not be written to disk.
   104  	FileName string
   105  	// ModTime contains the modification time of the file, or the zero time if not applicable.
   106  	ModTime time.Time
   107  }
   108  
   109  // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase.
   110  // The resulting WriteCloser must be closed after the contents of the file have
   111  // been written.
   112  func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints) (plaintext io.WriteCloser, err error) {
   113  	if hints == nil {
   114  		hints = &FileHints{}
   115  	}
   116  
   117  	key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, rand.Reader, passphrase, packet.CipherAES128)
   118  	if err != nil {
   119  		return
   120  	}
   121  	w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, rand.Reader, packet.CipherAES128, key)
   122  	if err != nil {
   123  		return
   124  	}
   125  	var epochSeconds uint32
   126  	if !hints.ModTime.IsZero() {
   127  		epochSeconds = uint32(hints.ModTime.Unix())
   128  	}
   129  	return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
   130  }
   131  
   132  // intersectPreferences mutates and returns a prefix of a that contains only
   133  // the values in the intersection of a and b. The order of a is preserved.
   134  func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) {
   135  	var j int
   136  	for _, v := range a {
   137  		for _, v2 := range b {
   138  			if v == v2 {
   139  				a[j] = v
   140  				j++
   141  				break
   142  			}
   143  		}
   144  	}
   145  
   146  	return a[:j]
   147  }
   148  
   149  func hashToHashId(h crypto.Hash) uint8 {
   150  	v, ok := s2k.HashToHashId(h)
   151  	if !ok {
   152  		panic("tried to convert unknown hash")
   153  	}
   154  	return v
   155  }
   156  
   157  // Encrypt encrypts a message to a number of recipients and, optionally, signs
   158  // it. hints contains optional information, that is also encrypted, that aids
   159  // the recipients in processing the message. The resulting WriteCloser must
   160  // be closed after the contents of the file have been written.
   161  func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints) (plaintext io.WriteCloser, err error) {
   162  	var signer *packet.PrivateKey
   163  	if signed != nil {
   164  		signer = signed.signingKey().PrivateKey
   165  		if signer == nil || signer.Encrypted {
   166  			return nil, errors.InvalidArgumentError("signing key must be decrypted")
   167  		}
   168  	}
   169  
   170  	// These are the possible ciphers that we'll use for the message.
   171  	candidateCiphers := []uint8{
   172  		uint8(packet.CipherAES128),
   173  		uint8(packet.CipherAES256),
   174  		uint8(packet.CipherCAST5),
   175  	}
   176  	// These are the possible hash functions that we'll use for the signature.
   177  	candidateHashes := []uint8{
   178  		hashToHashId(crypto.SHA256),
   179  		hashToHashId(crypto.SHA512),
   180  		hashToHashId(crypto.SHA1),
   181  		hashToHashId(crypto.RIPEMD160),
   182  	}
   183  	// In the event that a recipient doesn't specify any supported ciphers
   184  	// or hash functions, these are the ones that we assume that every
   185  	// implementation supports.
   186  	defaultCiphers := candidateCiphers[len(candidateCiphers)-1:]
   187  	defaultHashes := candidateHashes[len(candidateHashes)-1:]
   188  
   189  	encryptKeys := make([]Key, len(to))
   190  	for i := range to {
   191  		encryptKeys[i] = to[i].encryptionKey()
   192  		if encryptKeys[i].PublicKey == nil {
   193  			return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys")
   194  		}
   195  
   196  		sig := to[i].primaryIdentity().SelfSignature
   197  
   198  		preferredSymmetric := sig.PreferredSymmetric
   199  		if len(preferredSymmetric) == 0 {
   200  			preferredSymmetric = defaultCiphers
   201  		}
   202  		preferredHashes := sig.PreferredHash
   203  		if len(preferredHashes) == 0 {
   204  			preferredHashes = defaultHashes
   205  		}
   206  		candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric)
   207  		candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
   208  	}
   209  
   210  	if len(candidateCiphers) == 0 || len(candidateHashes) == 0 {
   211  		return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms")
   212  	}
   213  
   214  	cipher := packet.CipherFunction(candidateCiphers[0])
   215  	hash, _ := s2k.HashIdToHash(candidateHashes[0])
   216  	symKey := make([]byte, cipher.KeySize())
   217  	if _, err := io.ReadFull(rand.Reader, symKey); err != nil {
   218  		return nil, err
   219  	}
   220  
   221  	for _, key := range encryptKeys {
   222  		if err := packet.SerializeEncryptedKey(ciphertext, rand.Reader, key.PublicKey, cipher, symKey); err != nil {
   223  			return nil, err
   224  		}
   225  	}
   226  
   227  	encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, rand.Reader, cipher, symKey)
   228  	if err != nil {
   229  		return
   230  	}
   231  
   232  	if signer != nil {
   233  		ops := &packet.OnePassSignature{
   234  			SigType:    packet.SigTypeBinary,
   235  			Hash:       hash,
   236  			PubKeyAlgo: signer.PubKeyAlgo,
   237  			KeyId:      signer.KeyId,
   238  			IsLast:     true,
   239  		}
   240  		if err := ops.Serialize(encryptedData); err != nil {
   241  			return nil, err
   242  		}
   243  	}
   244  
   245  	if hints == nil {
   246  		hints = &FileHints{}
   247  	}
   248  
   249  	w := encryptedData
   250  	if signer != nil {
   251  		// If we need to write a signature packet after the literal
   252  		// data then we need to stop literalData from closing
   253  		// encryptedData.
   254  		w = noOpCloser{encryptedData}
   255  
   256  	}
   257  	var epochSeconds uint32
   258  	if !hints.ModTime.IsZero() {
   259  		epochSeconds = uint32(hints.ModTime.Unix())
   260  	}
   261  	literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  
   266  	if signer != nil {
   267  		return signatureWriter{encryptedData, literalData, hash, hash.New(), signer}, nil
   268  	}
   269  	return literalData, nil
   270  }
   271  
   272  // signatureWriter hashes the contents of a message while passing it along to
   273  // literalData. When closed, it closes literalData, writes a signature packet
   274  // to encryptedData and then also closes encryptedData.
   275  type signatureWriter struct {
   276  	encryptedData io.WriteCloser
   277  	literalData   io.WriteCloser
   278  	hashType      crypto.Hash
   279  	h             hash.Hash
   280  	signer        *packet.PrivateKey
   281  }
   282  
   283  func (s signatureWriter) Write(data []byte) (int, error) {
   284  	s.h.Write(data)
   285  	return s.literalData.Write(data)
   286  }
   287  
   288  func (s signatureWriter) Close() error {
   289  	sig := &packet.Signature{
   290  		SigType:      packet.SigTypeBinary,
   291  		PubKeyAlgo:   s.signer.PubKeyAlgo,
   292  		Hash:         s.hashType,
   293  		CreationTime: time.Now(),
   294  		IssuerKeyId:  &s.signer.KeyId,
   295  	}
   296  
   297  	if err := sig.Sign(rand.Reader, s.h, s.signer); err != nil {
   298  		return err
   299  	}
   300  	if err := s.literalData.Close(); err != nil {
   301  		return err
   302  	}
   303  	if err := sig.Serialize(s.encryptedData); err != nil {
   304  		return err
   305  	}
   306  	return s.encryptedData.Close()
   307  }
   308  
   309  // noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
   310  // TODO: we have two of these in OpenPGP packages alone. This probably needs
   311  // to be promoted somewhere more common.
   312  type noOpCloser struct {
   313  	w io.Writer
   314  }
   315  
   316  func (c noOpCloser) Write(data []byte) (n int, err error) {
   317  	return c.w.Write(data)
   318  }
   319  
   320  func (c noOpCloser) Close() error {
   321  	return nil
   322  }