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