k8s.io/apiserver@v0.31.1/pkg/storage/value/encrypt/envelope/envelope_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package envelope 18 19 import ( 20 "bytes" 21 "context" 22 "crypto/aes" 23 "crypto/cipher" 24 "encoding/base64" 25 "encoding/binary" 26 "fmt" 27 "strconv" 28 "strings" 29 "testing" 30 31 "k8s.io/apiserver/pkg/storage/value" 32 aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes" 33 ) 34 35 const ( 36 testText = "abcdefghijklmnopqrstuvwxyz" 37 testContextText = "0123456789" 38 testEnvelopeCacheSize = 10 39 ) 40 41 // testEnvelopeService is a mock Envelope service which can be used to simulate remote Envelope services 42 // for testing of Envelope based encryption providers. 43 type testEnvelopeService struct { 44 disabled bool 45 keyVersion string 46 } 47 48 func (t *testEnvelopeService) Decrypt(data []byte) ([]byte, error) { 49 if t.disabled { 50 return nil, fmt.Errorf("Envelope service was disabled") 51 } 52 dataChunks := strings.SplitN(string(data), ":", 2) 53 if len(dataChunks) != 2 { 54 return nil, fmt.Errorf("invalid data encountered for decryption: %s. Missing key version", data) 55 } 56 return base64.StdEncoding.DecodeString(dataChunks[1]) 57 } 58 59 func (t *testEnvelopeService) Encrypt(data []byte) ([]byte, error) { 60 if t.disabled { 61 return nil, fmt.Errorf("Envelope service was disabled") 62 } 63 return []byte(t.keyVersion + ":" + base64.StdEncoding.EncodeToString(data)), nil 64 } 65 66 func (t *testEnvelopeService) SetDisabledStatus(status bool) { 67 t.disabled = status 68 } 69 70 func (t *testEnvelopeService) Rotate() { 71 i, _ := strconv.Atoi(t.keyVersion) 72 t.keyVersion = strconv.FormatInt(int64(i+1), 10) 73 } 74 75 func newTestEnvelopeService() *testEnvelopeService { 76 return &testEnvelopeService{ 77 keyVersion: "1", 78 } 79 } 80 81 // Throw error if Envelope transformer tries to contact Envelope without hitting cache. 82 func TestEnvelopeCaching(t *testing.T) { 83 testCases := []struct { 84 desc string 85 cacheSize int 86 simulateKMSPluginFailure bool 87 expectedError string 88 }{ 89 { 90 desc: "positive cache size should withstand plugin failure", 91 cacheSize: 1000, 92 simulateKMSPluginFailure: true, 93 }, 94 { 95 desc: "cache disabled size should not withstand plugin failure", 96 cacheSize: 0, 97 simulateKMSPluginFailure: true, 98 expectedError: "Envelope service was disabled", 99 }, 100 { 101 desc: "cache disabled, no plugin failure should succeed", 102 cacheSize: 0, 103 simulateKMSPluginFailure: false, 104 }, 105 } 106 107 for _, tt := range testCases { 108 t.Run(tt.desc, func(t *testing.T) { 109 envelopeService := newTestEnvelopeService() 110 cbcTransformer := func(block cipher.Block) (value.Transformer, error) { 111 return aestransformer.NewCBCTransformer(block), nil 112 } 113 envelopeTransformer := NewEnvelopeTransformer(envelopeService, tt.cacheSize, cbcTransformer) 114 ctx := context.Background() 115 dataCtx := value.DefaultContext(testContextText) 116 originalText := []byte(testText) 117 118 transformedData, err := envelopeTransformer.TransformToStorage(ctx, originalText, dataCtx) 119 if err != nil { 120 t.Fatalf("envelopeTransformer: error while transforming data to storage: %s", err) 121 } 122 untransformedData, _, err := envelopeTransformer.TransformFromStorage(ctx, transformedData, dataCtx) 123 if err != nil { 124 t.Fatalf("could not decrypt Envelope transformer's encrypted data even once: %v", err) 125 } 126 if !bytes.Equal(untransformedData, originalText) { 127 t.Fatalf("envelopeTransformer transformed data incorrectly. Expected: %v, got %v", originalText, untransformedData) 128 } 129 130 envelopeService.SetDisabledStatus(tt.simulateKMSPluginFailure) 131 untransformedData, _, err = envelopeTransformer.TransformFromStorage(ctx, transformedData, dataCtx) 132 if tt.expectedError != "" { 133 if err == nil { 134 t.Fatalf("expected error: %v, got nil", tt.expectedError) 135 } 136 if err.Error() != tt.expectedError { 137 t.Fatalf("expected error: %v, got: %v", tt.expectedError, err) 138 } 139 } else { 140 if err != nil { 141 t.Fatalf("unexpected error: %v", err) 142 } 143 if !bytes.Equal(untransformedData, originalText) { 144 t.Fatalf("envelopeTransformer transformed data incorrectly. Expected: %v, got %v", originalText, untransformedData) 145 } 146 } 147 }) 148 } 149 } 150 151 // Makes Envelope transformer hit cache limit, throws error if it misbehaves. 152 func TestEnvelopeCacheLimit(t *testing.T) { 153 cbcTransformer := func(block cipher.Block) (value.Transformer, error) { 154 return aestransformer.NewCBCTransformer(block), nil 155 } 156 envelopeTransformer := NewEnvelopeTransformer(newTestEnvelopeService(), testEnvelopeCacheSize, cbcTransformer) 157 ctx := context.Background() 158 dataCtx := value.DefaultContext(testContextText) 159 160 transformedOutputs := map[int][]byte{} 161 162 // Overwrite lots of entries in the map 163 for i := 0; i < 2*testEnvelopeCacheSize; i++ { 164 numberText := []byte(strconv.Itoa(i)) 165 166 res, err := envelopeTransformer.TransformToStorage(ctx, numberText, dataCtx) 167 transformedOutputs[i] = res 168 if err != nil { 169 t.Fatalf("envelopeTransformer: error while transforming data (%v) to storage: %s", numberText, err) 170 } 171 } 172 173 // Try reading all the data now, ensuring cache misses don't cause a concern. 174 for i := 0; i < 2*testEnvelopeCacheSize; i++ { 175 numberText := []byte(strconv.Itoa(i)) 176 177 output, _, err := envelopeTransformer.TransformFromStorage(ctx, transformedOutputs[i], dataCtx) 178 if err != nil { 179 t.Fatalf("envelopeTransformer: error while transforming data (%v) from storage: %s", transformedOutputs[i], err) 180 } 181 182 if !bytes.Equal(numberText, output) { 183 t.Fatalf("envelopeTransformer transformed data incorrectly using cache. Expected: %v, got %v", numberText, output) 184 } 185 } 186 } 187 188 func BenchmarkEnvelopeCBCRead(b *testing.B) { 189 cbcTransformer := func(block cipher.Block) (value.Transformer, error) { 190 return aestransformer.NewCBCTransformer(block), nil 191 } 192 envelopeTransformer := NewEnvelopeTransformer(newTestEnvelopeService(), testEnvelopeCacheSize, cbcTransformer) 193 benchmarkRead(b, envelopeTransformer, 1024) 194 } 195 196 func BenchmarkAESCBCRead(b *testing.B) { 197 block, err := aes.NewCipher(bytes.Repeat([]byte("a"), 32)) 198 if err != nil { 199 b.Fatal(err) 200 } 201 202 aesCBCTransformer := aestransformer.NewCBCTransformer(block) 203 benchmarkRead(b, aesCBCTransformer, 1024) 204 } 205 206 func BenchmarkEnvelopeGCMRead(b *testing.B) { 207 envelopeTransformer := NewEnvelopeTransformer(newTestEnvelopeService(), testEnvelopeCacheSize, aestransformer.NewGCMTransformer) 208 benchmarkRead(b, envelopeTransformer, 1024) 209 } 210 211 func BenchmarkAESGCMRead(b *testing.B) { 212 block, err := aes.NewCipher(bytes.Repeat([]byte("a"), 32)) 213 if err != nil { 214 b.Fatal(err) 215 } 216 217 aesGCMTransformer, err := aestransformer.NewGCMTransformer(block) 218 if err != nil { 219 b.Fatal(err) 220 } 221 222 benchmarkRead(b, aesGCMTransformer, 1024) 223 } 224 225 func benchmarkRead(b *testing.B, transformer value.Transformer, valueLength int) { 226 ctx := context.Background() 227 dataCtx := value.DefaultContext(testContextText) 228 v := bytes.Repeat([]byte("0123456789abcdef"), valueLength/16) 229 230 out, err := transformer.TransformToStorage(ctx, v, dataCtx) 231 if err != nil { 232 b.Fatal(err) 233 } 234 235 b.ResetTimer() 236 for i := 0; i < b.N; i++ { 237 from, stale, err := transformer.TransformFromStorage(ctx, out, dataCtx) 238 if err != nil { 239 b.Fatal(err) 240 } 241 if stale { 242 b.Fatalf("unexpected data: %t %q", stale, from) 243 } 244 } 245 b.StopTimer() 246 } 247 248 // remove after 1.13 249 func TestBackwardsCompatibility(t *testing.T) { 250 envelopeService := newTestEnvelopeService() 251 cbcTransformer := func(block cipher.Block) (value.Transformer, error) { 252 return aestransformer.NewCBCTransformer(block), nil 253 } 254 envelopeTransformerInst := NewEnvelopeTransformer(envelopeService, testEnvelopeCacheSize, cbcTransformer) 255 ctx := context.Background() 256 dataCtx := value.DefaultContext(testContextText) 257 originalText := []byte(testText) 258 259 transformedData, err := oldTransformToStorage(ctx, envelopeTransformerInst.(*envelopeTransformer), originalText, dataCtx) 260 if err != nil { 261 t.Fatalf("envelopeTransformer: error while transforming data to storage: %s", err) 262 } 263 untransformedData, _, err := envelopeTransformerInst.TransformFromStorage(ctx, transformedData, dataCtx) 264 if err != nil { 265 t.Fatalf("could not decrypt Envelope transformer's encrypted data even once: %v", err) 266 } 267 if !bytes.Equal(untransformedData, originalText) { 268 t.Fatalf("envelopeTransformer transformed data incorrectly. Expected: %v, got %v", originalText, untransformedData) 269 } 270 271 envelopeService.SetDisabledStatus(true) 272 // Subsequent read for the same data should work fine due to caching. 273 untransformedData, _, err = envelopeTransformerInst.TransformFromStorage(ctx, transformedData, dataCtx) 274 if err != nil { 275 t.Fatalf("could not decrypt Envelope transformer's encrypted data using just cache: %v", err) 276 } 277 if !bytes.Equal(untransformedData, originalText) { 278 t.Fatalf("envelopeTransformer transformed data incorrectly using cache. Expected: %v, got %v", originalText, untransformedData) 279 } 280 } 281 282 // remove after 1.13 283 func oldTransformToStorage(ctx context.Context, t *envelopeTransformer, data []byte, dataCtx value.Context) ([]byte, error) { 284 newKey, err := generateKey(32) 285 if err != nil { 286 return nil, err 287 } 288 289 encKey, err := t.envelopeService.Encrypt(newKey) 290 if err != nil { 291 return nil, err 292 } 293 294 transformer, err := t.addTransformer(encKey, newKey) 295 if err != nil { 296 return nil, err 297 } 298 299 // Append the length of the encrypted DEK as the first 2 bytes. 300 encKeyLen := make([]byte, 2) 301 encKeyBytes := []byte(encKey) 302 binary.BigEndian.PutUint16(encKeyLen, uint16(len(encKeyBytes))) 303 304 prefix := append(encKeyLen, encKeyBytes...) 305 306 prefixedData := make([]byte, len(prefix), len(data)+len(prefix)) 307 copy(prefixedData, prefix) 308 result, err := transformer.TransformToStorage(ctx, data, dataCtx) 309 if err != nil { 310 return nil, err 311 } 312 prefixedData = append(prefixedData, result...) 313 return prefixedData, nil 314 }