github.com/opentofu/opentofu@v1.7.1/internal/encryption/keyprovider/aws_kms/provider.go (about) 1 package aws_kms 2 3 import ( 4 "context" 5 6 "github.com/aws/aws-sdk-go-v2/aws" 7 "github.com/aws/aws-sdk-go-v2/service/kms" 8 "github.com/aws/aws-sdk-go-v2/service/kms/types" 9 "github.com/opentofu/opentofu/internal/encryption/keyprovider" 10 ) 11 12 type keyMeta struct { 13 CiphertextBlob []byte `json:"ciphertext_blob"` 14 } 15 16 func (m keyMeta) isPresent() bool { 17 return len(m.CiphertextBlob) != 0 18 } 19 20 type kmsClient interface { 21 GenerateDataKey(ctx context.Context, params *kms.GenerateDataKeyInput, optFns ...func(*kms.Options)) (*kms.GenerateDataKeyOutput, error) 22 Decrypt(ctx context.Context, params *kms.DecryptInput, optFns ...func(*kms.Options)) (*kms.DecryptOutput, error) 23 } 24 25 type keyProvider struct { 26 Config 27 svc kmsClient 28 ctx context.Context 29 } 30 31 func (p keyProvider) Provide(rawMeta keyprovider.KeyMeta) (keyprovider.Output, keyprovider.KeyMeta, error) { 32 if rawMeta == nil { 33 return keyprovider.Output{}, nil, &keyprovider.ErrInvalidMetadata{Message: "bug: no metadata struct provided"} 34 } 35 inMeta, ok := rawMeta.(*keyMeta) 36 if !ok { 37 return keyprovider.Output{}, nil, &keyprovider.ErrInvalidMetadata{Message: "bug: metadata struct is not of the correct type"} 38 } 39 40 outMeta := &keyMeta{} 41 out := keyprovider.Output{} 42 43 // as validation has happened in the config, we can safely cast here and not worry about the cast failing 44 spec := types.DataKeySpec(p.KeySpec) 45 46 generatedKeyData, err := p.svc.GenerateDataKey(p.ctx, &kms.GenerateDataKeyInput{ 47 KeyId: aws.String(p.KMSKeyID), 48 KeySpec: spec, 49 }) 50 51 if err != nil { 52 return out, outMeta, &keyprovider.ErrKeyProviderFailure{ 53 Message: "failed to generate key", 54 Cause: err, 55 } 56 } 57 58 // Set initial outputs that are always set 59 out.EncryptionKey = generatedKeyData.Plaintext 60 outMeta.CiphertextBlob = generatedKeyData.CiphertextBlob 61 62 // We do not set the DecryptionKey here as we should only be setting the decryption key if we are decrypting 63 // and that is handled below when we check if the inMeta has a CiphertextBlob 64 65 if inMeta.isPresent() { 66 // We have an existing decryption key to decrypt, so we should now populate the DecryptionKey 67 decryptedKeyData, decryptErr := p.svc.Decrypt(p.ctx, &kms.DecryptInput{ 68 KeyId: aws.String(p.KMSKeyID), 69 CiphertextBlob: inMeta.CiphertextBlob, 70 }) 71 72 if decryptErr != nil { 73 return out, outMeta, &keyprovider.ErrKeyProviderFailure{Cause: decryptErr} 74 } 75 76 // Set decryption key on the output 77 out.DecryptionKey = decryptedKeyData.Plaintext 78 } 79 80 return out, outMeta, nil 81 }