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  }