github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/encryption/encryption.go (about)

     1  package encryption
     2  
     3  import (
     4  	cryptorand "crypto/rand"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"io"
     8  	"strings"
     9  
    10  	"github.com/docker/swarmkit/api"
    11  	"github.com/gogo/protobuf/proto"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // This package defines the interfaces and encryption package
    16  
    17  const humanReadablePrefix = "SWMKEY-1-"
    18  
    19  // ErrCannotDecrypt is the type of error returned when some data cannot be decryptd as plaintext
    20  type ErrCannotDecrypt struct {
    21  	msg string
    22  }
    23  
    24  func (e ErrCannotDecrypt) Error() string {
    25  	return e.msg
    26  }
    27  
    28  // A Decrypter can decrypt an encrypted record
    29  type Decrypter interface {
    30  	Decrypt(api.MaybeEncryptedRecord) ([]byte, error)
    31  }
    32  
    33  // A Encrypter can encrypt some bytes into an encrypted record
    34  type Encrypter interface {
    35  	Encrypt(data []byte) (*api.MaybeEncryptedRecord, error)
    36  }
    37  
    38  type noopCrypter struct{}
    39  
    40  func (n noopCrypter) Decrypt(e api.MaybeEncryptedRecord) ([]byte, error) {
    41  	if e.Algorithm != n.Algorithm() {
    42  		return nil, fmt.Errorf("record is encrypted")
    43  	}
    44  	return e.Data, nil
    45  }
    46  
    47  func (n noopCrypter) Encrypt(data []byte) (*api.MaybeEncryptedRecord, error) {
    48  	return &api.MaybeEncryptedRecord{
    49  		Algorithm: n.Algorithm(),
    50  		Data:      data,
    51  	}, nil
    52  }
    53  
    54  func (n noopCrypter) Algorithm() api.MaybeEncryptedRecord_Algorithm {
    55  	return api.MaybeEncryptedRecord_NotEncrypted
    56  }
    57  
    58  // NoopCrypter is just a pass-through crypter - it does not actually encrypt or
    59  // decrypt any data
    60  var NoopCrypter = noopCrypter{}
    61  
    62  // specificDecryptor represents a specific type of Decrypter, like NaclSecretbox or Fernet.
    63  // It does not apply to a more general decrypter like MultiDecrypter.
    64  type specificDecrypter interface {
    65  	Decrypter
    66  	Algorithm() api.MaybeEncryptedRecord_Algorithm
    67  }
    68  
    69  // MultiDecrypter is a decrypter that will attempt to decrypt with multiple decrypters.  It
    70  // references them by algorithm, so that only the relevant decrypters are checked instead of
    71  // every single one. The reason for multiple decrypters per algorithm is to support hitless
    72  // encryption key rotation.
    73  //
    74  // For raft encryption for instance, during an encryption key rotation, it's possible to have
    75  // some raft logs encrypted with the old key and some encrypted with the new key, so we need a
    76  // decrypter that can decrypt both.
    77  type MultiDecrypter struct {
    78  	decrypters map[api.MaybeEncryptedRecord_Algorithm][]Decrypter
    79  }
    80  
    81  // Decrypt tries to decrypt using any decrypters that match the given algorithm.
    82  func (m MultiDecrypter) Decrypt(r api.MaybeEncryptedRecord) ([]byte, error) {
    83  	decrypters, ok := m.decrypters[r.Algorithm]
    84  	if !ok {
    85  		return nil, fmt.Errorf("cannot decrypt record encrypted using %s",
    86  			api.MaybeEncryptedRecord_Algorithm_name[int32(r.Algorithm)])
    87  	}
    88  	var rerr error
    89  	for _, d := range decrypters {
    90  		result, err := d.Decrypt(r)
    91  		if err == nil {
    92  			return result, nil
    93  		}
    94  		rerr = err
    95  	}
    96  	return nil, rerr
    97  }
    98  
    99  // NewMultiDecrypter returns a new MultiDecrypter given multiple Decrypters.  If any of
   100  // the Decrypters are also MultiDecrypters, they are flattened into a single map, but
   101  // it does not deduplicate any decrypters.
   102  // Note that if something is neither a MultiDecrypter nor a specificDecrypter, it is
   103  // ignored.
   104  func NewMultiDecrypter(decrypters ...Decrypter) MultiDecrypter {
   105  	m := MultiDecrypter{decrypters: make(map[api.MaybeEncryptedRecord_Algorithm][]Decrypter)}
   106  	for _, d := range decrypters {
   107  		if md, ok := d.(MultiDecrypter); ok {
   108  			for algo, dec := range md.decrypters {
   109  				m.decrypters[algo] = append(m.decrypters[algo], dec...)
   110  			}
   111  		} else if sd, ok := d.(specificDecrypter); ok {
   112  			m.decrypters[sd.Algorithm()] = append(m.decrypters[sd.Algorithm()], sd)
   113  		}
   114  	}
   115  	return m
   116  }
   117  
   118  // Decrypt turns a slice of bytes serialized as an MaybeEncryptedRecord into a slice of plaintext bytes
   119  func Decrypt(encryptd []byte, decrypter Decrypter) ([]byte, error) {
   120  	if decrypter == nil {
   121  		return nil, ErrCannotDecrypt{msg: "no decrypter specified"}
   122  	}
   123  	r := api.MaybeEncryptedRecord{}
   124  	if err := proto.Unmarshal(encryptd, &r); err != nil {
   125  		// nope, this wasn't marshalled as a MaybeEncryptedRecord
   126  		return nil, ErrCannotDecrypt{msg: "unable to unmarshal as MaybeEncryptedRecord"}
   127  	}
   128  	plaintext, err := decrypter.Decrypt(r)
   129  	if err != nil {
   130  		return nil, ErrCannotDecrypt{msg: err.Error()}
   131  	}
   132  	return plaintext, nil
   133  }
   134  
   135  // Encrypt turns a slice of bytes into a serialized MaybeEncryptedRecord slice of bytes
   136  func Encrypt(plaintext []byte, encrypter Encrypter) ([]byte, error) {
   137  	if encrypter == nil {
   138  		return nil, fmt.Errorf("no encrypter specified")
   139  	}
   140  
   141  	encryptedRecord, err := encrypter.Encrypt(plaintext)
   142  	if err != nil {
   143  		return nil, errors.Wrap(err, "unable to encrypt data")
   144  	}
   145  
   146  	data, err := proto.Marshal(encryptedRecord)
   147  	if err != nil {
   148  		return nil, errors.Wrap(err, "unable to marshal as MaybeEncryptedRecord")
   149  	}
   150  
   151  	return data, nil
   152  }
   153  
   154  // Defaults returns a default encrypter and decrypter.  If the FIPS parameter is set to
   155  // true, the only algorithm supported on both the encrypter and decrypter will be fernet.
   156  func Defaults(key []byte, fips bool) (Encrypter, Decrypter) {
   157  	f := NewFernet(key)
   158  	if fips {
   159  		return f, f
   160  	}
   161  	n := NewNACLSecretbox(key)
   162  	return n, NewMultiDecrypter(n, f)
   163  }
   164  
   165  // GenerateSecretKey generates a secret key that can be used for encrypting data
   166  // using this package
   167  func GenerateSecretKey() []byte {
   168  	secretData := make([]byte, naclSecretboxKeySize)
   169  	if _, err := io.ReadFull(cryptorand.Reader, secretData); err != nil {
   170  		// panic if we can't read random data
   171  		panic(errors.Wrap(err, "failed to read random bytes"))
   172  	}
   173  	return secretData
   174  }
   175  
   176  // HumanReadableKey displays a secret key in a human readable way
   177  func HumanReadableKey(key []byte) string {
   178  	// base64-encode the key
   179  	return humanReadablePrefix + base64.RawStdEncoding.EncodeToString(key)
   180  }
   181  
   182  // ParseHumanReadableKey returns a key as bytes from recognized serializations of
   183  // said keys
   184  func ParseHumanReadableKey(key string) ([]byte, error) {
   185  	if !strings.HasPrefix(key, humanReadablePrefix) {
   186  		return nil, fmt.Errorf("invalid key string")
   187  	}
   188  	keyBytes, err := base64.RawStdEncoding.DecodeString(strings.TrimPrefix(key, humanReadablePrefix))
   189  	if err != nil {
   190  		return nil, fmt.Errorf("invalid key string")
   191  	}
   192  	return keyBytes, nil
   193  }