]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/golang.org/x/crypto/openpgp/write.go
vendor: github.com/hashicorp/terraform/...@v0.10.0
[github/fretlink/terraform-provider-statuscake.git] / vendor / golang.org / x / crypto / openpgp / write.go
diff --git a/vendor/golang.org/x/crypto/openpgp/write.go b/vendor/golang.org/x/crypto/openpgp/write.go
new file mode 100644 (file)
index 0000000..65a304c
--- /dev/null
@@ -0,0 +1,378 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package openpgp
+
+import (
+       "crypto"
+       "hash"
+       "io"
+       "strconv"
+       "time"
+
+       "golang.org/x/crypto/openpgp/armor"
+       "golang.org/x/crypto/openpgp/errors"
+       "golang.org/x/crypto/openpgp/packet"
+       "golang.org/x/crypto/openpgp/s2k"
+)
+
+// DetachSign signs message with the private key from signer (which must
+// already have been decrypted) and writes the signature to w.
+// If config is nil, sensible defaults will be used.
+func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
+       return detachSign(w, signer, message, packet.SigTypeBinary, config)
+}
+
+// ArmoredDetachSign signs message with the private key from signer (which
+// must already have been decrypted) and writes an armored signature to w.
+// If config is nil, sensible defaults will be used.
+func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) {
+       return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config)
+}
+
+// DetachSignText signs message (after canonicalising the line endings) with
+// the private key from signer (which must already have been decrypted) and
+// writes the signature to w.
+// If config is nil, sensible defaults will be used.
+func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
+       return detachSign(w, signer, message, packet.SigTypeText, config)
+}
+
+// ArmoredDetachSignText signs message (after canonicalising the line endings)
+// with the private key from signer (which must already have been decrypted)
+// and writes an armored signature to w.
+// If config is nil, sensible defaults will be used.
+func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
+       return armoredDetachSign(w, signer, message, packet.SigTypeText, config)
+}
+
+func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
+       out, err := armor.Encode(w, SignatureType, nil)
+       if err != nil {
+               return
+       }
+       err = detachSign(out, signer, message, sigType, config)
+       if err != nil {
+               return
+       }
+       return out.Close()
+}
+
+func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
+       if signer.PrivateKey == nil {
+               return errors.InvalidArgumentError("signing key doesn't have a private key")
+       }
+       if signer.PrivateKey.Encrypted {
+               return errors.InvalidArgumentError("signing key is encrypted")
+       }
+
+       sig := new(packet.Signature)
+       sig.SigType = sigType
+       sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo
+       sig.Hash = config.Hash()
+       sig.CreationTime = config.Now()
+       sig.IssuerKeyId = &signer.PrivateKey.KeyId
+
+       h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
+       if err != nil {
+               return
+       }
+       io.Copy(wrappedHash, message)
+
+       err = sig.Sign(h, signer.PrivateKey, config)
+       if err != nil {
+               return
+       }
+
+       return sig.Serialize(w)
+}
+
+// FileHints contains metadata about encrypted files. This metadata is, itself,
+// encrypted.
+type FileHints struct {
+       // IsBinary can be set to hint that the contents are binary data.
+       IsBinary bool
+       // FileName hints at the name of the file that should be written. It's
+       // truncated to 255 bytes if longer. It may be empty to suggest that the
+       // file should not be written to disk. It may be equal to "_CONSOLE" to
+       // suggest the data should not be written to disk.
+       FileName string
+       // ModTime contains the modification time of the file, or the zero time if not applicable.
+       ModTime time.Time
+}
+
+// SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase.
+// The resulting WriteCloser must be closed after the contents of the file have
+// been written.
+// If config is nil, sensible defaults will be used.
+func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
+       if hints == nil {
+               hints = &FileHints{}
+       }
+
+       key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config)
+       if err != nil {
+               return
+       }
+       w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config)
+       if err != nil {
+               return
+       }
+
+       literaldata := w
+       if algo := config.Compression(); algo != packet.CompressionNone {
+               var compConfig *packet.CompressionConfig
+               if config != nil {
+                       compConfig = config.CompressionConfig
+               }
+               literaldata, err = packet.SerializeCompressed(w, algo, compConfig)
+               if err != nil {
+                       return
+               }
+       }
+
+       var epochSeconds uint32
+       if !hints.ModTime.IsZero() {
+               epochSeconds = uint32(hints.ModTime.Unix())
+       }
+       return packet.SerializeLiteral(literaldata, hints.IsBinary, hints.FileName, epochSeconds)
+}
+
+// intersectPreferences mutates and returns a prefix of a that contains only
+// the values in the intersection of a and b. The order of a is preserved.
+func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) {
+       var j int
+       for _, v := range a {
+               for _, v2 := range b {
+                       if v == v2 {
+                               a[j] = v
+                               j++
+                               break
+                       }
+               }
+       }
+
+       return a[:j]
+}
+
+func hashToHashId(h crypto.Hash) uint8 {
+       v, ok := s2k.HashToHashId(h)
+       if !ok {
+               panic("tried to convert unknown hash")
+       }
+       return v
+}
+
+// Encrypt encrypts a message to a number of recipients and, optionally, signs
+// it. hints contains optional information, that is also encrypted, that aids
+// the recipients in processing the message. The resulting WriteCloser must
+// be closed after the contents of the file have been written.
+// If config is nil, sensible defaults will be used.
+func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
+       var signer *packet.PrivateKey
+       if signed != nil {
+               signKey, ok := signed.signingKey(config.Now())
+               if !ok {
+                       return nil, errors.InvalidArgumentError("no valid signing keys")
+               }
+               signer = signKey.PrivateKey
+               if signer == nil {
+                       return nil, errors.InvalidArgumentError("no private key in signing key")
+               }
+               if signer.Encrypted {
+                       return nil, errors.InvalidArgumentError("signing key must be decrypted")
+               }
+       }
+
+       // These are the possible ciphers that we'll use for the message.
+       candidateCiphers := []uint8{
+               uint8(packet.CipherAES128),
+               uint8(packet.CipherAES256),
+               uint8(packet.CipherCAST5),
+       }
+       // These are the possible hash functions that we'll use for the signature.
+       candidateHashes := []uint8{
+               hashToHashId(crypto.SHA256),
+               hashToHashId(crypto.SHA512),
+               hashToHashId(crypto.SHA1),
+               hashToHashId(crypto.RIPEMD160),
+       }
+       // In the event that a recipient doesn't specify any supported ciphers
+       // or hash functions, these are the ones that we assume that every
+       // implementation supports.
+       defaultCiphers := candidateCiphers[len(candidateCiphers)-1:]
+       defaultHashes := candidateHashes[len(candidateHashes)-1:]
+
+       encryptKeys := make([]Key, len(to))
+       for i := range to {
+               var ok bool
+               encryptKeys[i], ok = to[i].encryptionKey(config.Now())
+               if !ok {
+                       return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys")
+               }
+
+               sig := to[i].primaryIdentity().SelfSignature
+
+               preferredSymmetric := sig.PreferredSymmetric
+               if len(preferredSymmetric) == 0 {
+                       preferredSymmetric = defaultCiphers
+               }
+               preferredHashes := sig.PreferredHash
+               if len(preferredHashes) == 0 {
+                       preferredHashes = defaultHashes
+               }
+               candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric)
+               candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
+       }
+
+       if len(candidateCiphers) == 0 || len(candidateHashes) == 0 {
+               return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms")
+       }
+
+       cipher := packet.CipherFunction(candidateCiphers[0])
+       // If the cipher specified by config is a candidate, we'll use that.
+       configuredCipher := config.Cipher()
+       for _, c := range candidateCiphers {
+               cipherFunc := packet.CipherFunction(c)
+               if cipherFunc == configuredCipher {
+                       cipher = cipherFunc
+                       break
+               }
+       }
+
+       var hash crypto.Hash
+       for _, hashId := range candidateHashes {
+               if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() {
+                       hash = h
+                       break
+               }
+       }
+
+       // If the hash specified by config is a candidate, we'll use that.
+       if configuredHash := config.Hash(); configuredHash.Available() {
+               for _, hashId := range candidateHashes {
+                       if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash {
+                               hash = h
+                               break
+                       }
+               }
+       }
+
+       if hash == 0 {
+               hashId := candidateHashes[0]
+               name, ok := s2k.HashIdToString(hashId)
+               if !ok {
+                       name = "#" + strconv.Itoa(int(hashId))
+               }
+               return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)")
+       }
+
+       symKey := make([]byte, cipher.KeySize())
+       if _, err := io.ReadFull(config.Random(), symKey); err != nil {
+               return nil, err
+       }
+
+       for _, key := range encryptKeys {
+               if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil {
+                       return nil, err
+               }
+       }
+
+       encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config)
+       if err != nil {
+               return
+       }
+
+       if signer != nil {
+               ops := &packet.OnePassSignature{
+                       SigType:    packet.SigTypeBinary,
+                       Hash:       hash,
+                       PubKeyAlgo: signer.PubKeyAlgo,
+                       KeyId:      signer.KeyId,
+                       IsLast:     true,
+               }
+               if err := ops.Serialize(encryptedData); err != nil {
+                       return nil, err
+               }
+       }
+
+       if hints == nil {
+               hints = &FileHints{}
+       }
+
+       w := encryptedData
+       if signer != nil {
+               // If we need to write a signature packet after the literal
+               // data then we need to stop literalData from closing
+               // encryptedData.
+               w = noOpCloser{encryptedData}
+
+       }
+       var epochSeconds uint32
+       if !hints.ModTime.IsZero() {
+               epochSeconds = uint32(hints.ModTime.Unix())
+       }
+       literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
+       if err != nil {
+               return nil, err
+       }
+
+       if signer != nil {
+               return signatureWriter{encryptedData, literalData, hash, hash.New(), signer, config}, nil
+       }
+       return literalData, nil
+}
+
+// signatureWriter hashes the contents of a message while passing it along to
+// literalData. When closed, it closes literalData, writes a signature packet
+// to encryptedData and then also closes encryptedData.
+type signatureWriter struct {
+       encryptedData io.WriteCloser
+       literalData   io.WriteCloser
+       hashType      crypto.Hash
+       h             hash.Hash
+       signer        *packet.PrivateKey
+       config        *packet.Config
+}
+
+func (s signatureWriter) Write(data []byte) (int, error) {
+       s.h.Write(data)
+       return s.literalData.Write(data)
+}
+
+func (s signatureWriter) Close() error {
+       sig := &packet.Signature{
+               SigType:      packet.SigTypeBinary,
+               PubKeyAlgo:   s.signer.PubKeyAlgo,
+               Hash:         s.hashType,
+               CreationTime: s.config.Now(),
+               IssuerKeyId:  &s.signer.KeyId,
+       }
+
+       if err := sig.Sign(s.h, s.signer, s.config); err != nil {
+               return err
+       }
+       if err := s.literalData.Close(); err != nil {
+               return err
+       }
+       if err := sig.Serialize(s.encryptedData); err != nil {
+               return err
+       }
+       return s.encryptedData.Close()
+}
+
+// noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
+// TODO: we have two of these in OpenPGP packages alone. This probably needs
+// to be promoted somewhere more common.
+type noOpCloser struct {
+       w io.Writer
+}
+
+func (c noOpCloser) Write(data []byte) (n int, err error) {
+       return c.w.Write(data)
+}
+
+func (c noOpCloser) Close() error {
+       return nil
+}