From 36ab52b428f6b87df5bdd85f253758967bf0a240 Mon Sep 17 00:00:00 2001 From: Mike Danese Date: Fri, 28 Sep 2018 23:02:42 -0700 Subject: [PATCH] refactor envelope to use cryptobytes --- .../src/k8s.io/apiserver/Godeps/Godeps.json | 8 +++ .../pkg/storage/value/encrypt/envelope/BUILD | 1 + .../value/encrypt/envelope/envelope.go | 32 ++++----- .../value/encrypt/envelope/envelope_test.go | 68 +++++++++++++++++++ 4 files changed, 92 insertions(+), 17 deletions(-) diff --git a/staging/src/k8s.io/apiserver/Godeps/Godeps.json b/staging/src/k8s.io/apiserver/Godeps/Godeps.json index 7ee99efd80..c7a233e637 100644 --- a/staging/src/k8s.io/apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/apiserver/Godeps/Godeps.json @@ -626,6 +626,14 @@ "ImportPath": "golang.org/x/crypto/blowfish", "Rev": "de0752318171da717af4ce24d0a2e8626afaeb11" }, + { + "ImportPath": "golang.org/x/crypto/cryptobyte", + "Rev": "de0752318171da717af4ce24d0a2e8626afaeb11" + }, + { + "ImportPath": "golang.org/x/crypto/cryptobyte/asn1", + "Rev": "de0752318171da717af4ce24d0a2e8626afaeb11" + }, { "ImportPath": "golang.org/x/crypto/ed25519", "Rev": "de0752318171da717af4ce24d0a2e8626afaeb11" diff --git a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/BUILD b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/BUILD index 81403bdc6b..8beca7163d 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/BUILD @@ -19,6 +19,7 @@ go_library( "//staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/hashicorp/golang-lru:go_default_library", + "//vendor/golang.org/x/crypto/cryptobyte:go_default_library", "//vendor/google.golang.org/grpc:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/envelope.go b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/envelope.go index e5a8989d97..7c4e2080a2 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/envelope.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/envelope.go @@ -22,13 +22,13 @@ import ( "crypto/cipher" "crypto/rand" "encoding/base64" - "encoding/binary" "fmt" "time" "k8s.io/apiserver/pkg/storage/value" lru "github.com/hashicorp/golang-lru" + "golang.org/x/crypto/cryptobyte" ) // defaultCacheSize is the number of decrypted DEKs which would be cached by the transformer. @@ -80,12 +80,13 @@ func (t *envelopeTransformer) TransformFromStorage(data []byte, context value.Co // 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 nil, false, fmt.Errorf("invalid data encountered by envelope transformer, length longer than available bytes: %q", data) + var encKey cryptobyte.String + s := cryptobyte.String(data) + if ok := s.ReadUint16LengthPrefixed(&encKey); !ok { + return nil, false, fmt.Errorf("invalid data encountered by envelope transformer: failed to read uint16 length prefixed data") } - encKey := data[2 : keyLen+2] - encData := data[2+keyLen:] + + encData := []byte(s) // Look up the decrypted DEK from cache or Envelope. transformer := t.getTransformer(encKey) @@ -120,21 +121,18 @@ func (t *envelopeTransformer) TransformToStorage(data []byte, context value.Cont return nil, err } - // Append the length of the encrypted DEK as the first 2 bytes. - encKeyLen := make([]byte, 2) - encKeyBytes := []byte(encKey) - binary.BigEndian.PutUint16(encKeyLen, uint16(len(encKeyBytes))) - - prefix := append(encKeyLen, encKeyBytes...) - - prefixedData := make([]byte, len(prefix), len(data)+len(prefix)) - copy(prefixedData, prefix) result, err := transformer.TransformToStorage(data, context) if err != nil { return nil, err } - prefixedData = append(prefixedData, result...) - return prefixedData, nil + // Append the length of the encrypted DEK as the first 2 bytes. + b := cryptobyte.NewBuilder(nil) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes([]byte(encKey)) + }) + b.AddBytes(result) + + return b.Bytes() } var _ value.Transformer = &envelopeTransformer{} diff --git a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/envelope_test.go b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/envelope_test.go index 3215a22f33..65c0063a29 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/envelope_test.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/envelope_test.go @@ -20,6 +20,7 @@ import ( "bytes" "crypto/aes" "encoding/base64" + "encoding/binary" "fmt" "strconv" "strings" @@ -201,3 +202,70 @@ func benchmarkRead(b *testing.B, transformer value.Transformer, valueLength int) } b.StopTimer() } + +// remove after 1.13 +func TestBackwardsCompatibility(t *testing.T) { + envelopeService := newTestEnvelopeService() + envelopeTransformerInst, err := NewEnvelopeTransformer(envelopeService, testEnvelopeCacheSize, aestransformer.NewCBCTransformer) + if err != nil { + t.Fatalf("failed to initialize envelope transformer: %v", err) + } + context := value.DefaultContext([]byte(testContextText)) + originalText := []byte(testText) + + transformedData, err := oldTransformToStorage(envelopeTransformerInst.(*envelopeTransformer), originalText, context) + if err != nil { + t.Fatalf("envelopeTransformer: error while transforming data to storage: %s", err) + } + untransformedData, _, err := envelopeTransformerInst.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 = envelopeTransformerInst.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) + } +} + +// remove after 1.13 +func oldTransformToStorage(t *envelopeTransformer, data []byte, context value.Context) ([]byte, error) { + newKey, err := generateKey(32) + if err != nil { + return nil, err + } + + encKey, err := t.envelopeService.Encrypt(newKey) + if err != nil { + return nil, err + } + + transformer, err := t.addTransformer(encKey, newKey) + if err != nil { + return nil, err + } + + // Append the length of the encrypted DEK as the first 2 bytes. + encKeyLen := make([]byte, 2) + encKeyBytes := []byte(encKey) + binary.BigEndian.PutUint16(encKeyLen, uint16(len(encKeyBytes))) + + prefix := append(encKeyLen, encKeyBytes...) + + prefixedData := make([]byte, len(prefix), len(data)+len(prefix)) + copy(prefixedData, prefix) + result, err := transformer.TransformToStorage(data, context) + if err != nil { + return nil, err + } + prefixedData = append(prefixedData, result...) + return prefixedData, nil +}