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 }