github.com/opentofu/opentofu@v1.7.1/internal/encryption/keyprovider/gcp_kms/provider.go (about) 1 package gcp_kms 2 3 import ( 4 "context" 5 "crypto/rand" 6 7 "cloud.google.com/go/kms/apiv1/kmspb" 8 "github.com/googleapis/gax-go/v2" 9 "github.com/opentofu/opentofu/internal/encryption/keyprovider" 10 ) 11 12 type keyMeta struct { 13 Ciphertext []byte `json:"ciphertext"` 14 } 15 16 func (m keyMeta) isPresent() bool { 17 return len(m.Ciphertext) != 0 18 } 19 20 type keyManagementClient interface { 21 Encrypt(ctx context.Context, req *kmspb.EncryptRequest, opts ...gax.CallOption) (*kmspb.EncryptResponse, error) 22 Decrypt(ctx context.Context, req *kmspb.DecryptRequest, opts ...gax.CallOption) (*kmspb.DecryptResponse, error) 23 } 24 25 type keyProvider struct { 26 svc keyManagementClient 27 ctx context.Context 28 keyName string 29 keyLength int 30 } 31 32 func (p keyProvider) Provide(rawMeta keyprovider.KeyMeta) (keyprovider.Output, keyprovider.KeyMeta, error) { 33 if rawMeta == nil { 34 return keyprovider.Output{}, nil, &keyprovider.ErrInvalidMetadata{Message: "bug: no metadata struct provided"} 35 } 36 inMeta, ok := rawMeta.(*keyMeta) 37 if !ok { 38 return keyprovider.Output{}, nil, &keyprovider.ErrInvalidMetadata{Message: "bug: invalid metadata struct type"} 39 } 40 41 outMeta := &keyMeta{} 42 out := keyprovider.Output{} 43 44 // Generate new key 45 out.EncryptionKey = make([]byte, p.keyLength) 46 _, err := rand.Read(out.EncryptionKey) 47 if err != nil { 48 return out, outMeta, &keyprovider.ErrKeyProviderFailure{ 49 Message: "failed to generate key", 50 Cause: err, 51 } 52 } 53 54 // Encrypt new encryption key using kms 55 encryptedKeyData, err := p.svc.Encrypt(p.ctx, &kmspb.EncryptRequest{ 56 Name: p.keyName, 57 Plaintext: out.EncryptionKey, 58 }) 59 if err != nil { 60 return out, outMeta, &keyprovider.ErrKeyProviderFailure{ 61 Message: "failed to encrypt key", 62 Cause: err, 63 } 64 } 65 66 outMeta.Ciphertext = encryptedKeyData.Ciphertext 67 68 // We do not set the DecryptionKey here as we should only be setting the decryption key if we are decrypting 69 // and that is handled below when we check if the inMeta has a CiphertextBlob 70 71 if inMeta.isPresent() { 72 // We have an existing decryption key to decrypt, so we should now populate the DecryptionKey 73 decryptedKeyData, decryptErr := p.svc.Decrypt(p.ctx, &kmspb.DecryptRequest{ 74 Name: p.keyName, 75 Ciphertext: inMeta.Ciphertext, 76 }) 77 78 if decryptErr != nil { 79 return out, outMeta, decryptErr 80 } 81 82 // Set decryption key on the output 83 out.DecryptionKey = decryptedKeyData.Plaintext 84 } 85 86 return out, outMeta, nil 87 }