...

Source file src/github.com/ProtonMail/go-crypto/openpgp/write.go

Documentation: github.com/ProtonMail/go-crypto/openpgp

     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  	"github.com/ProtonMail/go-crypto/openpgp/armor"
    15  	"github.com/ProtonMail/go-crypto/openpgp/errors"
    16  	"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
    17  	"github.com/ProtonMail/go-crypto/openpgp/packet"
    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  	signingKey, ok := signer.SigningKeyById(config.Now(), config.SigningKey())
    64  	if !ok {
    65  		return errors.InvalidArgumentError("no valid signing keys")
    66  	}
    67  	if signingKey.PrivateKey == nil {
    68  		return errors.InvalidArgumentError("signing key doesn't have a private key")
    69  	}
    70  	if signingKey.PrivateKey.Encrypted {
    71  		return errors.InvalidArgumentError("signing key is encrypted")
    72  	}
    73  	if _, ok := algorithm.HashToHashId(config.Hash()); !ok {
    74  		return errors.InvalidArgumentError("invalid hash function")
    75  	}
    76  
    77  	sig := createSignaturePacket(signingKey.PublicKey, sigType, config)
    78  
    79  	h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
    80  	if err != nil {
    81  		return
    82  	}
    83  	if _, err = io.Copy(wrappedHash, message); err != nil {
    84  		return err
    85  	}
    86  
    87  	err = sig.Sign(h, signingKey.PrivateKey, config)
    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  // If config is nil, sensible defaults will be used.
   113  func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
   114  	if hints == nil {
   115  		hints = &FileHints{}
   116  	}
   117  
   118  	key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config)
   119  	if err != nil {
   120  		return
   121  	}
   122  
   123  	var w io.WriteCloser
   124  	cipherSuite := packet.CipherSuite{
   125  		Cipher: config.Cipher(),
   126  		Mode:   config.AEAD().Mode(),
   127  	}
   128  	w, err = packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), config.AEAD() != nil, cipherSuite, key, config)
   129  	if err != nil {
   130  		return
   131  	}
   132  
   133  	literalData := w
   134  	if algo := config.Compression(); algo != packet.CompressionNone {
   135  		var compConfig *packet.CompressionConfig
   136  		if config != nil {
   137  			compConfig = config.CompressionConfig
   138  		}
   139  		literalData, err = packet.SerializeCompressed(w, algo, compConfig)
   140  		if err != nil {
   141  			return
   142  		}
   143  	}
   144  
   145  	var epochSeconds uint32
   146  	if !hints.ModTime.IsZero() {
   147  		epochSeconds = uint32(hints.ModTime.Unix())
   148  	}
   149  	return packet.SerializeLiteral(literalData, hints.IsBinary, hints.FileName, epochSeconds)
   150  }
   151  
   152  // intersectPreferences mutates and returns a prefix of a that contains only
   153  // the values in the intersection of a and b. The order of a is preserved.
   154  func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) {
   155  	var j int
   156  	for _, v := range a {
   157  		for _, v2 := range b {
   158  			if v == v2 {
   159  				a[j] = v
   160  				j++
   161  				break
   162  			}
   163  		}
   164  	}
   165  
   166  	return a[:j]
   167  }
   168  
   169  // intersectPreferences mutates and returns a prefix of a that contains only
   170  // the values in the intersection of a and b. The order of a is preserved.
   171  func intersectCipherSuites(a [][2]uint8, b [][2]uint8) (intersection [][2]uint8) {
   172  	var j int
   173  	for _, v := range a {
   174  		for _, v2 := range b {
   175  			if v[0] == v2[0] && v[1] == v2[1] {
   176  				a[j] = v
   177  				j++
   178  				break
   179  			}
   180  		}
   181  	}
   182  
   183  	return a[:j]
   184  }
   185  
   186  func hashToHashId(h crypto.Hash) uint8 {
   187  	v, ok := algorithm.HashToHashId(h)
   188  	if !ok {
   189  		panic("tried to convert unknown hash")
   190  	}
   191  	return v
   192  }
   193  
   194  // EncryptText encrypts a message to a number of recipients and, optionally,
   195  // signs it. Optional information is contained in 'hints', also encrypted, that
   196  // aids the recipients in processing the message. The resulting WriteCloser
   197  // must be closed after the contents of the file have been written. If config
   198  // is nil, sensible defaults will be used. The signing is done in text mode.
   199  func EncryptText(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
   200  	return encrypt(ciphertext, ciphertext, to, signed, hints, packet.SigTypeText, config)
   201  }
   202  
   203  // Encrypt encrypts a message to a number of recipients and, optionally, signs
   204  // it. hints contains optional information, that is also encrypted, that aids
   205  // the recipients in processing the message. The resulting WriteCloser must
   206  // be closed after the contents of the file have been written.
   207  // If config is nil, sensible defaults will be used.
   208  func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
   209  	return encrypt(ciphertext, ciphertext, to, signed, hints, packet.SigTypeBinary, config)
   210  }
   211  
   212  // EncryptSplit encrypts a message to a number of recipients and, optionally, signs
   213  // it. hints contains optional information, that is also encrypted, that aids
   214  // the recipients in processing the message. The resulting WriteCloser must
   215  // be closed after the contents of the file have been written.
   216  // If config is nil, sensible defaults will be used.
   217  func EncryptSplit(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
   218  	return encrypt(keyWriter, dataWriter, to, signed, hints, packet.SigTypeBinary, config)
   219  }
   220  
   221  // EncryptTextSplit encrypts a message to a number of recipients and, optionally, signs
   222  // it. hints contains optional information, that is also encrypted, that aids
   223  // the recipients in processing the message. The resulting WriteCloser must
   224  // be closed after the contents of the file have been written.
   225  // If config is nil, sensible defaults will be used.
   226  func EncryptTextSplit(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
   227  	return encrypt(keyWriter, dataWriter, to, signed, hints, packet.SigTypeText, config)
   228  }
   229  
   230  // writeAndSign writes the data as a payload package and, optionally, signs
   231  // it. hints contains optional information, that is also encrypted,
   232  // that aids the recipients in processing the message. The resulting
   233  // WriteCloser must be closed after the contents of the file have been
   234  // written. If config is nil, sensible defaults will be used.
   235  func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entity, hints *FileHints, sigType packet.SignatureType, config *packet.Config) (plaintext io.WriteCloser, err error) {
   236  	var signer *packet.PrivateKey
   237  	if signed != nil {
   238  		signKey, ok := signed.SigningKeyById(config.Now(), config.SigningKey())
   239  		if !ok {
   240  			return nil, errors.InvalidArgumentError("no valid signing keys")
   241  		}
   242  		signer = signKey.PrivateKey
   243  		if signer == nil {
   244  			return nil, errors.InvalidArgumentError("no private key in signing key")
   245  		}
   246  		if signer.Encrypted {
   247  			return nil, errors.InvalidArgumentError("signing key must be decrypted")
   248  		}
   249  	}
   250  
   251  	var hash crypto.Hash
   252  	for _, hashId := range candidateHashes {
   253  		if h, ok := algorithm.HashIdToHash(hashId); ok && h.Available() {
   254  			hash = h
   255  			break
   256  		}
   257  	}
   258  
   259  	// If the hash specified by config is a candidate, we'll use that.
   260  	if configuredHash := config.Hash(); configuredHash.Available() {
   261  		for _, hashId := range candidateHashes {
   262  			if h, ok := algorithm.HashIdToHash(hashId); ok && h == configuredHash {
   263  				hash = h
   264  				break
   265  			}
   266  		}
   267  	}
   268  
   269  	if hash == 0 {
   270  		hashId := candidateHashes[0]
   271  		name, ok := algorithm.HashIdToString(hashId)
   272  		if !ok {
   273  			name = "#" + strconv.Itoa(int(hashId))
   274  		}
   275  		return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)")
   276  	}
   277  
   278  	if signer != nil {
   279  		ops := &packet.OnePassSignature{
   280  			SigType:    sigType,
   281  			Hash:       hash,
   282  			PubKeyAlgo: signer.PubKeyAlgo,
   283  			KeyId:      signer.KeyId,
   284  			IsLast:     true,
   285  		}
   286  		if err := ops.Serialize(payload); err != nil {
   287  			return nil, err
   288  		}
   289  	}
   290  
   291  	if hints == nil {
   292  		hints = &FileHints{}
   293  	}
   294  
   295  	w := payload
   296  	if signer != nil {
   297  		// If we need to write a signature packet after the literal
   298  		// data then we need to stop literalData from closing
   299  		// encryptedData.
   300  		w = noOpCloser{w}
   301  
   302  	}
   303  	var epochSeconds uint32
   304  	if !hints.ModTime.IsZero() {
   305  		epochSeconds = uint32(hints.ModTime.Unix())
   306  	}
   307  	literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  
   312  	if signer != nil {
   313  		h, wrappedHash, err := hashForSignature(hash, sigType)
   314  		if err != nil {
   315  			return nil, err
   316  		}
   317  		metadata := &packet.LiteralData{
   318  			Format:   't',
   319  			FileName: hints.FileName,
   320  			Time:     epochSeconds,
   321  		}
   322  		if hints.IsBinary {
   323  			metadata.Format = 'b'
   324  		}
   325  		return signatureWriter{payload, literalData, hash, wrappedHash, h, signer, sigType, config, metadata}, nil
   326  	}
   327  	return literalData, nil
   328  }
   329  
   330  // encrypt encrypts a message to a number of recipients and, optionally, signs
   331  // it. hints contains optional information, that is also encrypted, that aids
   332  // the recipients in processing the message. The resulting WriteCloser must
   333  // be closed after the contents of the file have been written.
   334  // If config is nil, sensible defaults will be used.
   335  func encrypt(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, sigType packet.SignatureType, config *packet.Config) (plaintext io.WriteCloser, err error) {
   336  	if len(to) == 0 {
   337  		return nil, errors.InvalidArgumentError("no encryption recipient provided")
   338  	}
   339  
   340  	// These are the possible ciphers that we'll use for the message.
   341  	candidateCiphers := []uint8{
   342  		uint8(packet.CipherAES256),
   343  		uint8(packet.CipherAES128),
   344  	}
   345  
   346  	// These are the possible hash functions that we'll use for the signature.
   347  	candidateHashes := []uint8{
   348  		hashToHashId(crypto.SHA256),
   349  		hashToHashId(crypto.SHA384),
   350  		hashToHashId(crypto.SHA512),
   351  		hashToHashId(crypto.SHA3_256),
   352  		hashToHashId(crypto.SHA3_512),
   353  	}
   354  
   355  	// Prefer GCM if everyone supports it
   356  	candidateCipherSuites := [][2]uint8{
   357  		{uint8(packet.CipherAES256), uint8(packet.AEADModeGCM)},
   358  		{uint8(packet.CipherAES256), uint8(packet.AEADModeEAX)},
   359  		{uint8(packet.CipherAES256), uint8(packet.AEADModeOCB)},
   360  		{uint8(packet.CipherAES128), uint8(packet.AEADModeGCM)},
   361  		{uint8(packet.CipherAES128), uint8(packet.AEADModeEAX)},
   362  		{uint8(packet.CipherAES128), uint8(packet.AEADModeOCB)},
   363  	}
   364  
   365  	candidateCompression := []uint8{
   366  		uint8(packet.CompressionNone),
   367  		uint8(packet.CompressionZIP),
   368  		uint8(packet.CompressionZLIB),
   369  	}
   370  
   371  	encryptKeys := make([]Key, len(to))
   372  
   373  	// AEAD is used only if config enables it and every key supports it
   374  	aeadSupported := config.AEAD() != nil
   375  
   376  	for i := range to {
   377  		var ok bool
   378  		encryptKeys[i], ok = to[i].EncryptionKey(config.Now())
   379  		if !ok {
   380  			return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no valid encryption keys")
   381  		}
   382  
   383  		sig := to[i].PrimaryIdentity().SelfSignature
   384  		if !sig.SEIPDv2 {
   385  			aeadSupported = false
   386  		}
   387  
   388  		candidateCiphers = intersectPreferences(candidateCiphers, sig.PreferredSymmetric)
   389  		candidateHashes = intersectPreferences(candidateHashes, sig.PreferredHash)
   390  		candidateCipherSuites = intersectCipherSuites(candidateCipherSuites, sig.PreferredCipherSuites)
   391  		candidateCompression = intersectPreferences(candidateCompression, sig.PreferredCompression)
   392  	}
   393  
   394  	// In the event that the intersection of supported algorithms is empty we use the ones
   395  	// labelled as MUST that every implementation supports.
   396  	if len(candidateCiphers) == 0 {
   397  		// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-9.3
   398  		candidateCiphers = []uint8{uint8(packet.CipherAES128)}
   399  	}
   400  	if len(candidateHashes) == 0 {
   401  		// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#hash-algos
   402  		candidateHashes = []uint8{hashToHashId(crypto.SHA256)}
   403  	}
   404  	if len(candidateCipherSuites) == 0 {
   405  		// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-9.6
   406  		candidateCipherSuites = [][2]uint8{{uint8(packet.CipherAES128), uint8(packet.AEADModeOCB)}}
   407  	}
   408  
   409  	cipher := packet.CipherFunction(candidateCiphers[0])
   410  	aeadCipherSuite := packet.CipherSuite{
   411  		Cipher: packet.CipherFunction(candidateCipherSuites[0][0]),
   412  		Mode:   packet.AEADMode(candidateCipherSuites[0][1]),
   413  	}
   414  
   415  	// If the cipher specified by config is a candidate, we'll use that.
   416  	configuredCipher := config.Cipher()
   417  	for _, c := range candidateCiphers {
   418  		cipherFunc := packet.CipherFunction(c)
   419  		if cipherFunc == configuredCipher {
   420  			cipher = cipherFunc
   421  			break
   422  		}
   423  	}
   424  
   425  	symKey := make([]byte, cipher.KeySize())
   426  	if _, err := io.ReadFull(config.Random(), symKey); err != nil {
   427  		return nil, err
   428  	}
   429  
   430  	for _, key := range encryptKeys {
   431  		if err := packet.SerializeEncryptedKey(keyWriter, key.PublicKey, cipher, symKey, config); err != nil {
   432  			return nil, err
   433  		}
   434  	}
   435  
   436  	var payload io.WriteCloser
   437  	payload, err = packet.SerializeSymmetricallyEncrypted(dataWriter, cipher, aeadSupported, aeadCipherSuite, symKey, config)
   438  	if err != nil {
   439  		return
   440  	}
   441  
   442  	payload, err = handleCompression(payload, candidateCompression, config)
   443  	if err != nil {
   444  		return nil, err
   445  	}
   446  
   447  	return writeAndSign(payload, candidateHashes, signed, hints, sigType, config)
   448  }
   449  
   450  // Sign signs a message. The resulting WriteCloser must be closed after the
   451  // contents of the file have been written.  hints contains optional information
   452  // that aids the recipients in processing the message.
   453  // If config is nil, sensible defaults will be used.
   454  func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Config) (input io.WriteCloser, err error) {
   455  	if signed == nil {
   456  		return nil, errors.InvalidArgumentError("no signer provided")
   457  	}
   458  
   459  	// These are the possible hash functions that we'll use for the signature.
   460  	candidateHashes := []uint8{
   461  		hashToHashId(crypto.SHA256),
   462  		hashToHashId(crypto.SHA384),
   463  		hashToHashId(crypto.SHA512),
   464  		hashToHashId(crypto.SHA3_256),
   465  		hashToHashId(crypto.SHA3_512),
   466  	}
   467  	defaultHashes := candidateHashes[0:1]
   468  	preferredHashes := signed.PrimaryIdentity().SelfSignature.PreferredHash
   469  	if len(preferredHashes) == 0 {
   470  		preferredHashes = defaultHashes
   471  	}
   472  	candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
   473  	if len(candidateHashes) == 0 {
   474  		return nil, errors.InvalidArgumentError("cannot sign because signing key shares no common algorithms with candidate hashes")
   475  	}
   476  
   477  	return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, packet.SigTypeBinary, config)
   478  }
   479  
   480  // signatureWriter hashes the contents of a message while passing it along to
   481  // literalData. When closed, it closes literalData, writes a signature packet
   482  // to encryptedData and then also closes encryptedData.
   483  type signatureWriter struct {
   484  	encryptedData io.WriteCloser
   485  	literalData   io.WriteCloser
   486  	hashType      crypto.Hash
   487  	wrappedHash   hash.Hash
   488  	h             hash.Hash
   489  	signer        *packet.PrivateKey
   490  	sigType       packet.SignatureType
   491  	config        *packet.Config
   492  	metadata      *packet.LiteralData // V5 signatures protect document metadata
   493  }
   494  
   495  func (s signatureWriter) Write(data []byte) (int, error) {
   496  	s.wrappedHash.Write(data)
   497  	switch s.sigType {
   498  	case packet.SigTypeBinary:
   499  		return s.literalData.Write(data)
   500  	case packet.SigTypeText:
   501  		flag := 0
   502  		return writeCanonical(s.literalData, data, &flag)
   503  	}
   504  	return 0, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(s.sigType)))
   505  }
   506  
   507  func (s signatureWriter) Close() error {
   508  	sig := createSignaturePacket(&s.signer.PublicKey, s.sigType, s.config)
   509  	sig.Hash = s.hashType
   510  	sig.Metadata = s.metadata
   511  
   512  	if err := sig.Sign(s.h, s.signer, s.config); err != nil {
   513  		return err
   514  	}
   515  	if err := s.literalData.Close(); err != nil {
   516  		return err
   517  	}
   518  	if err := sig.Serialize(s.encryptedData); err != nil {
   519  		return err
   520  	}
   521  	return s.encryptedData.Close()
   522  }
   523  
   524  func createSignaturePacket(signer *packet.PublicKey, sigType packet.SignatureType, config *packet.Config) *packet.Signature {
   525  	sigLifetimeSecs := config.SigLifetime()
   526  	return &packet.Signature{
   527  		Version:           signer.Version,
   528  		SigType:           sigType,
   529  		PubKeyAlgo:        signer.PubKeyAlgo,
   530  		Hash:              config.Hash(),
   531  		CreationTime:      config.Now(),
   532  		IssuerKeyId:       &signer.KeyId,
   533  		IssuerFingerprint: signer.Fingerprint,
   534  		Notations:         config.Notations(),
   535  		SigLifetimeSecs:   &sigLifetimeSecs,
   536  	}
   537  }
   538  
   539  // noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
   540  // TODO: we have two of these in OpenPGP packages alone. This probably needs
   541  // to be promoted somewhere more common.
   542  type noOpCloser struct {
   543  	w io.Writer
   544  }
   545  
   546  func (c noOpCloser) Write(data []byte) (n int, err error) {
   547  	return c.w.Write(data)
   548  }
   549  
   550  func (c noOpCloser) Close() error {
   551  	return nil
   552  }
   553  
   554  func handleCompression(compressed io.WriteCloser, candidateCompression []uint8, config *packet.Config) (data io.WriteCloser, err error) {
   555  	data = compressed
   556  	confAlgo := config.Compression()
   557  	if confAlgo == packet.CompressionNone {
   558  		return
   559  	}
   560  
   561  	// Set algorithm labelled as MUST as fallback
   562  	// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-9.4
   563  	finalAlgo := packet.CompressionNone
   564  	// if compression specified by config available we will use it
   565  	for _, c := range candidateCompression {
   566  		if uint8(confAlgo) == c {
   567  			finalAlgo = confAlgo
   568  			break
   569  		}
   570  	}
   571  
   572  	if finalAlgo != packet.CompressionNone {
   573  		var compConfig *packet.CompressionConfig
   574  		if config != nil {
   575  			compConfig = config.CompressionConfig
   576  		}
   577  		data, err = packet.SerializeCompressed(compressed, finalAlgo, compConfig)
   578  		if err != nil {
   579  			return
   580  		}
   581  	}
   582  	return data, nil
   583  }
   584  

View as plain text