81 lines
3.0 KiB
Go
81 lines
3.0 KiB
Go
/*
|
|
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 aes transforms values for storage at rest using AES-GCM.
|
|
package aes
|
|
|
|
import (
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"fmt"
|
|
|
|
"k8s.io/apiserver/pkg/storage/value"
|
|
)
|
|
|
|
// gcm implements AEAD encryption of the provided values given a cipher.Block algorithm.
|
|
// The authenticated data provided as part of the value.Context method must match when the same
|
|
// value is set to and loaded from storage. In order to ensure that values cannot be copied by
|
|
// an attacker from a location under their control, use characteristics of the storage location
|
|
// (such as the etcd key) as part of the authenticated data.
|
|
//
|
|
// Because this mode requires a generated IV and IV reuse is a known weakness of AES-GCM, keys
|
|
// must be rotated before a birthday attack becomes feasible. NIST SP 800-38D
|
|
// (http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf) recommends using the same
|
|
// key with random 96-bit nonces (the default nonce length) no more than 2^32 times, and
|
|
// therefore transformers using this implementation *must* ensure they allow for frequent key
|
|
// rotation. Future work should include investigation of AES-GCM-SIV as an alternative to
|
|
// random nonces.
|
|
type gcm struct {
|
|
block cipher.Block
|
|
}
|
|
|
|
// NewGCMTransformer takes the given block cipher and performs encryption and decryption on the given
|
|
// data.
|
|
func NewGCMTransformer(block cipher.Block) value.Transformer {
|
|
return &gcm{block: block}
|
|
}
|
|
|
|
func (t *gcm) TransformFromStorage(data []byte, context value.Context) ([]byte, bool, error) {
|
|
aead, err := cipher.NewGCM(t.block)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
nonceSize := aead.NonceSize()
|
|
if len(data) < nonceSize {
|
|
return nil, false, fmt.Errorf("the stored data was shorter than the required size")
|
|
}
|
|
result, err := aead.Open(nil, data[:nonceSize], data[nonceSize:], context.AuthenticatedData())
|
|
return result, false, err
|
|
}
|
|
|
|
func (t *gcm) TransformToStorage(data []byte, context value.Context) ([]byte, error) {
|
|
aead, err := cipher.NewGCM(t.block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
nonceSize := aead.NonceSize()
|
|
result := make([]byte, nonceSize+aead.Overhead()+len(data))
|
|
n, err := rand.Read(result[:nonceSize])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if n != nonceSize {
|
|
return nil, fmt.Errorf("unable to read sufficient random bytes")
|
|
}
|
|
cipherText := aead.Seal(result[nonceSize:nonceSize], result[:nonceSize], data, context.AuthenticatedData())
|
|
return result[:nonceSize+len(cipherText)], nil
|
|
}
|