1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
package s3crypto
import (
"fmt"
"io"
"github.com/aws/aws-sdk-go/aws"
)
const (
gcmKeySize = 32
gcmNonceSize = 12
)
// AESGCMContentCipherBuilder returns a new encryption only AES/GCM mode structure with a specific cipher data generator
// that will provide keys to be used for content encryption.
//
// Note: This uses the Go stdlib AEAD implementation for AES/GCM. Due to this objects to be encrypted or decrypted
// will be fully loaded into memory before encryption or decryption can occur. Caution must be taken to avoid memory
// allocation failures.
//
// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
func AESGCMContentCipherBuilder(generator CipherDataGenerator) ContentCipherBuilder {
return gcmContentCipherBuilder{generator}
}
// AESGCMContentCipherBuilderV2 returns a new encryption only AES/GCM mode structure with a specific cipher data generator
// that will provide keys to be used for content encryption. This type is compatible with the V2 encryption client.
//
// Note: This uses the Go stdlib AEAD implementation for AES/GCM. Due to this objects to be encrypted or decrypted
// will be fully loaded into memory before encryption or decryption can occur. Caution must be taken to avoid memory
// allocation failures.
func AESGCMContentCipherBuilderV2(generator CipherDataGeneratorWithCEKAlg) ContentCipherBuilder {
return gcmContentCipherBuilderV2{generator}
}
// RegisterAESGCMContentCipher registers the AES/GCM content cipher algorithm with the provided CryptoRegistry.
//
// Example:
//
// cr := s3crypto.NewCryptoRegistry()
// if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil {
// panic(err) // handle error
// }
func RegisterAESGCMContentCipher(registry *CryptoRegistry) error {
if registry == nil {
return errNilCryptoRegistry
}
err := registry.AddCEK(AESGCMNoPadding, newAESGCMContentCipher)
if err != nil {
return err
}
// NoPadder is generic but required by this algorithm, so if it is already registered and is the expected implementation
// don't error.
padderName := NoPadder.Name()
if v, ok := registry.GetPadder(padderName); !ok {
if err := registry.AddPadder(padderName, NoPadder); err != nil {
return err
}
} else if _, ok := v.(noPadder); !ok {
return fmt.Errorf("%s is already registred but does not match expected type %T", padderName, NoPadder)
}
return nil
}
// gcmContentCipherBuilder is a AES/GCM content cipher to be used with the V1 client CipherDataGenerator interface
type gcmContentCipherBuilder struct {
generator CipherDataGenerator
}
func (builder gcmContentCipherBuilder) ContentCipher() (ContentCipher, error) {
return builder.ContentCipherWithContext(aws.BackgroundContext())
}
func (builder gcmContentCipherBuilder) ContentCipherWithContext(ctx aws.Context) (ContentCipher, error) {
var cd CipherData
var err error
switch v := builder.generator.(type) {
case CipherDataGeneratorWithContext:
cd, err = v.GenerateCipherDataWithContext(ctx, gcmKeySize, gcmNonceSize)
default:
cd, err = builder.generator.GenerateCipherData(gcmKeySize, gcmNonceSize)
}
if err != nil {
return nil, err
}
return newAESGCMContentCipher(cd)
}
// isFixtureEncryptionCompatible will ensure that this type may only be used with the V1 client
func (builder gcmContentCipherBuilder) isEncryptionVersionCompatible(version clientVersion) error {
if version != v1ClientVersion {
return errDeprecatedIncompatibleCipherBuilder
}
return nil
}
func (builder gcmContentCipherBuilder) isAWSFixture() bool {
return true
}
// gcmContentCipherBuilderV2 return a new builder for encryption content using AES/GCM/NoPadding. This type is meant
// to be used with key wrapping implementations that allow the cek algorithm to be provided when calling the
// cipher data generator.
type gcmContentCipherBuilderV2 struct {
generator CipherDataGeneratorWithCEKAlg
}
func (builder gcmContentCipherBuilderV2) ContentCipher() (ContentCipher, error) {
return builder.ContentCipherWithContext(aws.BackgroundContext())
}
func (builder gcmContentCipherBuilderV2) ContentCipherWithContext(ctx aws.Context) (ContentCipher, error) {
cd, err := builder.generator.GenerateCipherDataWithCEKAlg(ctx, gcmKeySize, gcmNonceSize, AESGCMNoPadding)
if err != nil {
return nil, err
}
return newAESGCMContentCipher(cd)
}
// isFixtureEncryptionCompatible will ensure that this type may only be used with the V2 client
func (builder gcmContentCipherBuilderV2) isEncryptionVersionCompatible(version clientVersion) error {
if version != v2ClientVersion {
return errDeprecatedIncompatibleCipherBuilder
}
return nil
}
// isAWSFixture will return whether this type was constructed with an AWS provided CipherDataGenerator
func (builder gcmContentCipherBuilderV2) isAWSFixture() bool {
v, ok := builder.generator.(awsFixture)
return ok && v.isAWSFixture()
}
func newAESGCMContentCipher(cd CipherData) (ContentCipher, error) {
cd.CEKAlgorithm = AESGCMNoPadding
cd.TagLength = "128"
cipher, err := newAESGCM(cd)
if err != nil {
return nil, err
}
return &aesGCMContentCipher{
CipherData: cd,
Cipher: cipher,
}, nil
}
// AESGCMContentCipher will use AES GCM for the main cipher.
type aesGCMContentCipher struct {
CipherData CipherData
Cipher Cipher
}
// EncryptContents will generate a random key and iv and encrypt the data using cbc
func (cc *aesGCMContentCipher) EncryptContents(src io.Reader) (io.Reader, error) {
return cc.Cipher.Encrypt(src), nil
}
// DecryptContents will use the symmetric key provider to instantiate a new GCM cipher.
// We grab a decrypt reader from gcm and wrap it in a CryptoReadCloser. The only error
// expected here is when the key or iv is of invalid length.
func (cc *aesGCMContentCipher) DecryptContents(src io.ReadCloser) (io.ReadCloser, error) {
reader := cc.Cipher.Decrypt(src)
return &CryptoReadCloser{Body: src, Decrypter: reader}, nil
}
// GetCipherData returns cipher data
func (cc aesGCMContentCipher) GetCipherData() CipherData {
return cc.CipherData
}
// assert ContentCipherBuilder implementations
var (
_ ContentCipherBuilder = (*gcmContentCipherBuilder)(nil)
_ ContentCipherBuilder = (*gcmContentCipherBuilderV2)(nil)
)
// assert ContentCipherBuilderWithContext implementations
var (
_ ContentCipherBuilderWithContext = (*gcmContentCipherBuilder)(nil)
_ ContentCipherBuilderWithContext = (*gcmContentCipherBuilderV2)(nil)
)
// assert ContentCipher implementations
var (
_ ContentCipher = (*aesGCMContentCipher)(nil)
)
// assert awsFixture implementations
var (
_ awsFixture = (*gcmContentCipherBuilderV2)(nil)
)
|