Add configuration for AESCBC, Secretbox encryption

Add tests for new transformers
This commit is contained in:
Saksham Sharma 2017-06-06 14:45:19 -07:00 committed by Clayton Coleman
parent 395399ab3d
commit 1307340742
No known key found for this signature in database
GPG Key ID: 3D16906B4F1C5CB3
4 changed files with 220 additions and 53 deletions

View File

@ -21,6 +21,7 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/aes:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/aes:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/identity:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/identity:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/secretbox:go_default_library",
], ],
) )

View File

@ -18,6 +18,7 @@ package encryptionconfig
import ( import (
"crypto/aes" "crypto/aes"
"crypto/cipher"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io" "io"
@ -30,10 +31,13 @@ import (
"k8s.io/apiserver/pkg/storage/value" "k8s.io/apiserver/pkg/storage/value"
aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes" aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
"k8s.io/apiserver/pkg/storage/value/encrypt/identity" "k8s.io/apiserver/pkg/storage/value/encrypt/identity"
"k8s.io/apiserver/pkg/storage/value/encrypt/secretbox"
) )
const ( const (
aesTransformerPrefixV1 = "k8s:enc:aes:v1:" aesCBCTransformerPrefixV1 = "k8s:enc:aescbc:v1:"
aesGCMTransformerPrefixV1 = "k8s:enc:aesgcm:v1:"
secretboxTransformerPrefixV1 = "k8s:enc:secretbox:v1"
) )
// GetTransformerOverrides returns the transformer overrides by reading and parsing the encryption provider configuration file // GetTransformerOverrides returns the transformer overrides by reading and parsing the encryption provider configuration file
@ -102,25 +106,48 @@ func GetPrefixTransformers(config *ResourceConfig) ([]value.PrefixTransformer, e
for _, provider := range config.Providers { for _, provider := range config.Providers {
found := false found := false
if provider.AES != nil { var transformer value.PrefixTransformer
transformer, err := GetAESPrefixTransformer(provider.AES) var err error
found = true
if provider.AESGCM != nil {
transformer, err = GetAESPrefixTransformer(provider.AESGCM, aestransformer.NewGCMTransformer, aesGCMTransformerPrefixV1)
if err != nil { if err != nil {
return result, err return result, err
} }
result = append(result, transformer) found = true
}
if provider.AESCBC != nil {
if found == true {
return result, fmt.Errorf("more than one provider specified in a single element, should split into different list elements")
}
transformer, err = GetAESPrefixTransformer(provider.AESCBC, aestransformer.NewCBCTransformer, aesCBCTransformerPrefixV1)
found = true
}
if provider.Secretbox != nil {
if found == true {
return result, fmt.Errorf("more than one provider specified in a single element, should split into different list elements")
}
transformer, err = GetSecretboxPrefixTransformer(provider.Secretbox)
found = true
} }
if provider.Identity != nil { if provider.Identity != nil {
if found == true { if found == true {
return result, fmt.Errorf("more than one provider specified in a single element, should split into different list elements") return result, fmt.Errorf("more than one provider specified in a single element, should split into different list elements")
} }
found = true transformer = value.PrefixTransformer{
result = append(result, value.PrefixTransformer{
Transformer: identity.NewEncryptCheckTransformer(), Transformer: identity.NewEncryptCheckTransformer(),
Prefix: []byte{}, Prefix: []byte{},
})
} }
found = true
}
if err != nil {
return result, err
}
result = append(result, transformer)
if found == false { if found == false {
return result, fmt.Errorf("invalid provider configuration provided") return result, fmt.Errorf("invalid provider configuration provided")
@ -129,8 +156,12 @@ func GetPrefixTransformers(config *ResourceConfig) ([]value.PrefixTransformer, e
return result, nil return result, nil
} }
// GetAESPrefixTransformer returns a prefix transformer from the provided configuration // BlockTransformerFunc taske an AES cipher block and returns a value transformer.
func GetAESPrefixTransformer(config *AESConfig) (value.PrefixTransformer, error) { type BlockTransformerFunc func(cipher.Block) value.Transformer
// GetAESPrefixTransformer returns a prefix transformer from the provided configuration.
// Returns an AES transformer based on the provided prefix and block transformer.
func GetAESPrefixTransformer(config *AESConfig, fn BlockTransformerFunc, prefix string) (value.PrefixTransformer, error) {
var result value.PrefixTransformer var result value.PrefixTransformer
if len(config.Keys) == 0 { if len(config.Keys) == 0 {
@ -160,7 +191,7 @@ func GetAESPrefixTransformer(config *AESConfig) (value.PrefixTransformer, error)
// Create a new PrefixTransformer for this key // Create a new PrefixTransformer for this key
keyTransformers = append(keyTransformers, keyTransformers = append(keyTransformers,
value.PrefixTransformer{ value.PrefixTransformer{
Transformer: aestransformer.NewGCMTransformer(block), Transformer: fn(block),
Prefix: []byte(keyData.Name + ":"), Prefix: []byte(keyData.Name + ":"),
}) })
} }
@ -172,7 +203,58 @@ func GetAESPrefixTransformer(config *AESConfig) (value.PrefixTransformer, error)
// Create a PrefixTransformer which shall later be put in a list with other providers // Create a PrefixTransformer which shall later be put in a list with other providers
result = value.PrefixTransformer{ result = value.PrefixTransformer{
Transformer: keyTransformer, Transformer: keyTransformer,
Prefix: []byte(aesTransformerPrefixV1), Prefix: []byte(prefix),
}
return result, nil
}
// GetSecretboxPrefixTransformer returns a prefix transformer from the provided configuration
func GetSecretboxPrefixTransformer(config *SecretboxConfig) (value.PrefixTransformer, error) {
var result value.PrefixTransformer
if len(config.Keys) == 0 {
return result, fmt.Errorf("secretbox provider has no valid keys")
}
for _, key := range config.Keys {
if key.Name == "" {
return result, fmt.Errorf("key with invalid name provided")
}
if key.Secret == "" {
return result, fmt.Errorf("key %v has no provided secret", key.Name)
}
}
keyTransformers := []value.PrefixTransformer{}
for _, keyData := range config.Keys {
key, err := base64.StdEncoding.DecodeString(keyData.Secret)
if err != nil {
return result, fmt.Errorf("could not obtain secret for named key %s: %s", keyData.Name, err)
}
if len(key) != 32 {
return result, fmt.Errorf("expected key size 32 for aes-cbc provider, got %v", len(key))
}
keyArray := [32]byte{}
copy(keyArray[:], key)
// Create a new PrefixTransformer for this key
keyTransformers = append(keyTransformers,
value.PrefixTransformer{
Transformer: secretbox.NewSecretboxTransformer(keyArray),
Prefix: []byte(keyData.Name + ":"),
})
}
// Create a prefixTransformer which can choose between these keys
keyTransformer := value.NewPrefixTransformers(
fmt.Errorf("no matching key was found for the provided Secretbox transformer"), keyTransformers...)
// Create a PrefixTransformer which shall later be put in a list with other providers
result = value.PrefixTransformer{
Transformer: keyTransformer,
Prefix: []byte(secretboxTransformerPrefixV1),
} }
return result, nil return result, nil
} }

View File

@ -39,7 +39,69 @@ resources:
- namespaces - namespaces
providers: providers:
- identity: {} - identity: {}
- aes: - aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
`
correctConfigWithAesGcmFirst = `
kind: EncryptionConfig
apiVersion: v1
resources:
- resources:
- secrets
providers:
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- identity: {}
`
correctConfigWithAesCbcFirst = `
kind: EncryptionConfig
apiVersion: v1
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- identity: {}
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- aesgcm:
keys: keys:
- name: key1 - name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ== secret: c2VjcmV0IGlzIHNlY3VyZQ==
@ -47,20 +109,30 @@ resources:
secret: dGhpcyBpcyBwYXNzd29yZA== secret: dGhpcyBpcyBwYXNzd29yZA==
` `
correctConfigWithAesFirst = ` correctConfigWithSecretboxFirst = `
kind: EncryptionConfig kind: EncryptionConfig
apiVersion: v1 apiVersion: v1
resources: resources:
- resources: - resources:
- secrets - secrets
providers: providers:
- aes: - secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- aescbc:
keys: keys:
- name: key1 - name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ== secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2 - name: key2
secret: dGhpcyBpcyBwYXNzd29yZA== secret: dGhpcyBpcyBwYXNzd29yZA==
- identity: {} - identity: {}
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
` `
incorrectConfigNoSecretForKey = ` incorrectConfigNoSecretForKey = `
@ -71,7 +143,7 @@ resources:
- namespaces - namespaces
- secrets - secrets
providers: providers:
- aes: - aesgcm:
keys: keys:
- name: key1 - name: key1
` `
@ -84,7 +156,7 @@ resources:
- namespaces - namespaces
- secrets - secrets
providers: providers:
- aes: - aesgcm:
keys: keys:
- name: key1 - name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ== secret: c2VjcmV0IGlzIHNlY3VyZQ==
@ -103,58 +175,60 @@ func TestEncryptionProviderConfigCorrect(t *testing.T) {
t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithIdentityFirst) t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithIdentityFirst)
} }
aesFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithAesFirst)) aesGcmFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithAesGcmFirst))
if err != nil { if err != nil {
t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithAesFirst) t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithAesGcmFirst)
}
aesCbcFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithAesCbcFirst))
if err != nil {
t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithAesCbcFirst)
}
secretboxFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithSecretboxFirst))
if err != nil {
t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithSecretboxFirst)
} }
// Pick the transformer for any of the returned resources. // Pick the transformer for any of the returned resources.
identityFirstTransformer := identityFirstTransformerOverrides[schema.ParseGroupResource("secrets")] identityFirstTransformer := identityFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
aesFirstTransformer := aesFirstTransformerOverrides[schema.ParseGroupResource("secrets")] aesGcmFirstTransformer := aesGcmFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
aesCbcFirstTransformer := aesCbcFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
secretboxFirstTransformer := secretboxFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
context := value.DefaultContext([]byte(sampleContextText)) context := value.DefaultContext([]byte(sampleContextText))
originalText := []byte(sampleText) originalText := []byte(sampleText)
testCases := []struct { transformers := []struct {
WritingTransformer value.Transformer Transformer value.Transformer
Name string Name string
AesStale bool
IdentityStale bool
}{ }{
{aesFirstTransformer, "aesFirst", false, true}, {aesGcmFirstTransformer, "aesGcmFirst"},
{identityFirstTransformer, "identityFirst", true, false}, {aesCbcFirstTransformer, "aesCbcFirst"},
{secretboxFirstTransformer, "secretboxFirst"},
{identityFirstTransformer, "identityFirst"},
} }
for _, testCase := range testCases { for _, testCase := range transformers {
transformedData, err := testCase.WritingTransformer.TransformToStorage(originalText, context) transformedData, err := testCase.Transformer.TransformToStorage(originalText, context)
if err != nil { if err != nil {
t.Fatalf("%s: error while transforming data to storage: %s", testCase.Name, err) t.Fatalf("%s: error while transforming data to storage: %s", testCase.Name, err)
} }
aesUntransformedData, stale, err := aesFirstTransformer.TransformFromStorage(transformedData, context) for _, transformer := range transformers {
untransformedData, stale, err := transformer.Transformer.TransformFromStorage(transformedData, context)
if err != nil { if err != nil {
t.Fatalf("%s: error while reading using aesFirst transformer: %s", testCase.Name, err) t.Fatalf("%s: error while reading using %s transformer: %s", testCase.Name, transformer.Name, err)
}
if stale != (transformer.Name != testCase.Name) {
t.Fatalf("%s: wrong stale information on reading using %s transformer, should be %v", testCase.Name, transformer.Name, testCase.Name == transformer.Name)
}
if bytes.Compare(untransformedData, originalText) != 0 {
t.Fatalf("%s: %s transformer transformed data incorrectly. Expected: %v, got %v", testCase.Name, transformer.Name, originalText, untransformedData)
}
} }
if stale != testCase.AesStale {
t.Fatalf("%s: wrong stale information on reading using aesFirst transformer, should be %v", testCase.Name, testCase.AesStale)
} }
identityUntransformedData, stale, err := identityFirstTransformer.TransformFromStorage(transformedData, context)
if err != nil {
t.Fatalf("%s: error while reading using identityFirst transformer: %s", testCase.Name, err)
}
if stale != testCase.IdentityStale {
t.Fatalf("%s: wrong stale information on reading using identityFirst transformer, should be %v", testCase.Name, testCase.IdentityStale)
}
if bytes.Compare(aesUntransformedData, originalText) != 0 {
t.Fatalf("%s: aesFirst transformer transformed data incorrectly. Expected: %v, got %v", testCase.Name, originalText, aesUntransformedData)
}
if bytes.Compare(identityUntransformedData, originalText) != 0 {
t.Fatalf("%s: identityFirst transformer transformed data incorrectly. Expected: %v, got %v", testCase.Name, originalText, aesUntransformedData)
}
}
} }
// Throw error if key has no secret // Throw error if key has no secret

View File

@ -37,8 +37,12 @@ type ResourceConfig struct {
// ProviderConfig stores the provided configuration for an encryption provider. // ProviderConfig stores the provided configuration for an encryption provider.
type ProviderConfig struct { type ProviderConfig struct {
// aes is the configuration for the AEAD-GCM transformer. // aesgcm is the configuration for the AES-GCM transformer.
AES *AESConfig `json:"aes,omitempty"` AESGCM *AESConfig `json:"aesgcm,omitempty"`
// aescbc is the configuration for the AES-CBC transformer.
AESCBC *AESConfig `json:"aescbc,omitempty"`
// secretbox is the configuration for the Secretbox based transformer.
Secretbox *SecretboxConfig `json:"secretbox,omitempty"`
// identity is the (empty) configuration for the identity transformer. // identity is the (empty) configuration for the identity transformer.
Identity *IdentityConfig `json:"identity,omitempty"` Identity *IdentityConfig `json:"identity,omitempty"`
} }
@ -49,6 +53,12 @@ type AESConfig struct {
Keys []Key `json:"keys"` Keys []Key `json:"keys"`
} }
// SECRETBOXConfig contains the API configuration for an Secretbox transformer.
type SecretboxConfig struct {
// keys is a list of keys to be used for creating the Secretbox transformer.
Keys []Key `json:"keys"`
}
// Key contains name and secret of the provided key for AES transformer. // Key contains name and secret of the provided key for AES transformer.
type Key struct { type Key struct {
// name is the name of the key to be used while storing data to disk. // name is the name of the key to be used while storing data to disk.