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