Add unit tests for envelope transformer
Kubernetes-commit: d23a1f135d694ef315f23299f095fd4b85421670
This commit is contained in:
		
							parent
							
								
									03bcff8111
								
							
						
					
					
						commit
						8ca16e584a
					
				| 
						 | 
				
			
			@ -5,6 +5,7 @@ licenses(["notice"])
 | 
			
		|||
load(
 | 
			
		||||
    "@io_bazel_rules_go//go:def.bzl",
 | 
			
		||||
    "go_library",
 | 
			
		||||
    "go_test",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
go_library(
 | 
			
		||||
| 
						 | 
				
			
			@ -17,3 +18,11 @@ go_library(
 | 
			
		|||
        "//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/aes:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
go_test(
 | 
			
		||||
    name = "go_default_test",
 | 
			
		||||
    srcs = ["envelope_test.go"],
 | 
			
		||||
    library = ":go_default_library",
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    deps = ["//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library"],
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,10 +71,12 @@ func NewEnvelopeTransformer(envelopeService Service, cacheSize int) (value.Trans
 | 
			
		|||
 | 
			
		||||
// TransformFromStorage decrypts data encrypted by this transformer using envelope encryption.
 | 
			
		||||
func (t *envelopeTransformer) TransformFromStorage(data []byte, context value.Context) ([]byte, bool, error) {
 | 
			
		||||
	// Read the 16 bit length-of-DEK encoded at the start of the encrypted DEK.
 | 
			
		||||
	// Read the 16 bit length-of-DEK encoded at the start of the encrypted DEK. 16 bits can
 | 
			
		||||
	// represent a maximum key length of 65536 bytes. We are using a 256 bit key, whose
 | 
			
		||||
	// length cannot fit in 8 bits (1 byte). Thus, we use 16 bits (2 bytes) to store the length.
 | 
			
		||||
	keyLen := int(binary.BigEndian.Uint16(data[:2]))
 | 
			
		||||
	if keyLen+2 > len(data) {
 | 
			
		||||
		return []byte{}, false, fmt.Errorf("invalid data encountered by genvelope transformer, length longer than available bytes: %q", data)
 | 
			
		||||
		return nil, false, fmt.Errorf("invalid data encountered by genvelope transformer, length longer than available bytes: %q", data)
 | 
			
		||||
	}
 | 
			
		||||
	encKey := string(data[2 : keyLen+2])
 | 
			
		||||
	encData := data[2+keyLen:]
 | 
			
		||||
| 
						 | 
				
			
			@ -87,11 +89,11 @@ func (t *envelopeTransformer) TransformFromStorage(data []byte, context value.Co
 | 
			
		|||
	} else {
 | 
			
		||||
		key, err := t.envelopeService.Decrypt(encKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return []byte{}, false, fmt.Errorf("error while decrypting key: %q", err)
 | 
			
		||||
			return nil, false, fmt.Errorf("error while decrypting key: %q", err)
 | 
			
		||||
		}
 | 
			
		||||
		transformer, err = t.addTransformer(encKey, key)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return []byte{}, false, err
 | 
			
		||||
			return nil, false, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return transformer.TransformFromStorage(encData, context)
 | 
			
		||||
| 
						 | 
				
			
			@ -101,17 +103,17 @@ func (t *envelopeTransformer) TransformFromStorage(data []byte, context value.Co
 | 
			
		|||
func (t *envelopeTransformer) TransformToStorage(data []byte, context value.Context) ([]byte, error) {
 | 
			
		||||
	newKey, err := generateKey(32)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return []byte{}, err
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	encKey, err := t.envelopeService.Encrypt(newKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return []byte{}, err
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	transformer, err := t.addTransformer(encKey, newKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return []byte{}, err
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Append the length of the encrypted DEK as the first 2 bytes.
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +141,6 @@ func (t *envelopeTransformer) addTransformer(encKey string, key []byte) (value.T
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	transformer := aestransformer.NewCBCTransformer(block)
 | 
			
		||||
	t.transformers.Add(encKey, transformer)
 | 
			
		||||
	return transformer, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -150,7 +151,7 @@ func generateKey(length int) ([]byte, error) {
 | 
			
		|||
	key := make([]byte, length)
 | 
			
		||||
	_, err := rand.Read(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return []byte{}, err
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return key, nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,143 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2017 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package envelope
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apiserver/pkg/storage/value"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	testText              = "abcdefghijklmnopqrstuvwxyz"
 | 
			
		||||
	testContextText       = "0123456789"
 | 
			
		||||
	testEnvelopeCacheSize = 10
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// testEnvelopeService is a mock Envelope service which can be used to simulate remote Envelope services
 | 
			
		||||
// for testing of Envelope based encryption providers.
 | 
			
		||||
type testEnvelopeService struct {
 | 
			
		||||
	disabled   bool
 | 
			
		||||
	keyVersion string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *testEnvelopeService) Decrypt(data string) ([]byte, error) {
 | 
			
		||||
	if t.disabled {
 | 
			
		||||
		return nil, fmt.Errorf("Envelope service was disabled")
 | 
			
		||||
	}
 | 
			
		||||
	dataChunks := strings.SplitN(data, ":", 2)
 | 
			
		||||
	if len(dataChunks) != 2 {
 | 
			
		||||
		return nil, fmt.Errorf("invalid data encountered for decryption: %s. Missing key version", data)
 | 
			
		||||
	}
 | 
			
		||||
	return base64.StdEncoding.DecodeString(dataChunks[1])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *testEnvelopeService) Encrypt(data []byte) (string, error) {
 | 
			
		||||
	if t.disabled {
 | 
			
		||||
		return "", fmt.Errorf("Envelope service was disabled")
 | 
			
		||||
	}
 | 
			
		||||
	return t.keyVersion + ":" + base64.StdEncoding.EncodeToString(data), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *testEnvelopeService) SetDisabledStatus(status bool) {
 | 
			
		||||
	t.disabled = status
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *testEnvelopeService) Rotate() {
 | 
			
		||||
	i, _ := strconv.Atoi(t.keyVersion)
 | 
			
		||||
	t.keyVersion = strconv.FormatInt(int64(i+1), 10)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTestEnvelopeService() *testEnvelopeService {
 | 
			
		||||
	return &testEnvelopeService{
 | 
			
		||||
		keyVersion: "1",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Throw error if Envelope transformer tries to contact Envelope without hitting cache.
 | 
			
		||||
func TestEnvelopeCaching(t *testing.T) {
 | 
			
		||||
	envelopeService := newTestEnvelopeService()
 | 
			
		||||
	envelopeTransformer, err := NewEnvelopeTransformer(envelopeService, testEnvelopeCacheSize)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("failed to initialize envelope transformer: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	context := value.DefaultContext([]byte(testContextText))
 | 
			
		||||
	originalText := []byte(testText)
 | 
			
		||||
 | 
			
		||||
	transformedData, err := envelopeTransformer.TransformToStorage(originalText, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("envelopeTransformer: error while transforming data to storage: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	untransformedData, _, err := envelopeTransformer.TransformFromStorage(transformedData, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("could not decrypt Envelope transformer's encrypted data even once: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if bytes.Compare(untransformedData, originalText) != 0 {
 | 
			
		||||
		t.Fatalf("envelopeTransformer transformed data incorrectly. Expected: %v, got %v", originalText, untransformedData)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	envelopeService.SetDisabledStatus(true)
 | 
			
		||||
	// Subsequent read for the same data should work fine due to caching.
 | 
			
		||||
	untransformedData, _, err = envelopeTransformer.TransformFromStorage(transformedData, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("could not decrypt Envelope transformer's encrypted data using just cache: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if bytes.Compare(untransformedData, originalText) != 0 {
 | 
			
		||||
		t.Fatalf("envelopeTransformer transformed data incorrectly using cache. Expected: %v, got %v", originalText, untransformedData)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Makes Envelope transformer hit cache limit, throws error if it misbehaves.
 | 
			
		||||
func TestEnvelopeCacheLimit(t *testing.T) {
 | 
			
		||||
	envelopeTransformer, err := NewEnvelopeTransformer(newTestEnvelopeService(), testEnvelopeCacheSize)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("failed to initialize envelope transformer: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	context := value.DefaultContext([]byte(testContextText))
 | 
			
		||||
 | 
			
		||||
	transformedOutputs := map[int][]byte{}
 | 
			
		||||
 | 
			
		||||
	// Overwrite lots of entries in the map
 | 
			
		||||
	for i := 0; i < 2*testEnvelopeCacheSize; i++ {
 | 
			
		||||
		numberText := []byte(strconv.Itoa(i))
 | 
			
		||||
 | 
			
		||||
		res, err := envelopeTransformer.TransformToStorage(numberText, context)
 | 
			
		||||
		transformedOutputs[i] = res
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("envelopeTransformer: error while transforming data (%v) to storage: %s", numberText, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Try reading all the data now, ensuring cache misses don't cause a concern.
 | 
			
		||||
	for i := 0; i < 2*testEnvelopeCacheSize; i++ {
 | 
			
		||||
		numberText := []byte(strconv.Itoa(i))
 | 
			
		||||
 | 
			
		||||
		output, _, err := envelopeTransformer.TransformFromStorage(transformedOutputs[i], context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("envelopeTransformer: error while transforming data (%v) from storage: %s", transformedOutputs[i], err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if bytes.Compare(numberText, output) != 0 {
 | 
			
		||||
			t.Fatalf("envelopeTransformer transformed data incorrectly using cache. Expected: %v, got %v", numberText, output)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue