diff --git a/wrappers/alicloudkms/alicloudkms.go b/wrappers/alicloudkms/alicloudkms.go index 2d62b4dc..bb9000c3 100644 --- a/wrappers/alicloudkms/alicloudkms.go +++ b/wrappers/alicloudkms/alicloudkms.go @@ -23,6 +23,14 @@ const ( EnvVaultAliCloudKmsSealKeyId = "VAULT_ALICLOUDKMS_SEAL_KEY_ID" ) +const ( + // AliCloudKmsEnvelopeAesGcmEncrypt is when a data encryption key is generated and + // the data is encrypted with AES-GCM and the key is encrypted with KMS + AliCloudKmsEnvelopeAesGcmEncrypt = iota + // AliCloudKmsEncrypt is used to directly encrypt the data with KMS + AliCloudKmsEncrypt +) + // Wrapper is a Wrapper that uses AliCloud's KMS type Wrapper struct { client kmsClient @@ -164,32 +172,63 @@ func (k *Wrapper) Encrypt(_ context.Context, plaintext []byte, opt ...wrapping.O return nil, fmt.Errorf("given plaintext for encryption is nil") } - env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + opts, err := getOpts(opt...) if err != nil { - return nil, fmt.Errorf("error wrapping data: %w", err) + return nil, err } - input := kms.CreateEncryptRequest() - input.KeyId = k.keyId - input.Plaintext = base64.StdEncoding.EncodeToString(env.Key) - input.Domain = k.domain + var ret *wrapping.BlobInfo + if opts.WithoutEnvelope { + input := kms.CreateEncryptRequest() + input.KeyId = k.keyId + input.Plaintext = base64.StdEncoding.EncodeToString(plaintext) + input.Domain = k.domain - output, err := k.client.Encrypt(input) - if err != nil { - return nil, fmt.Errorf("error encrypting data: %w", err) - } + output, err := k.client.Encrypt(input) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } + + // Store the current key id. + keyId := output.KeyId + k.currentKeyId.Store(keyId) - // Store the current key id. - keyId := output.KeyId - k.currentKeyId.Store(keyId) - - ret := &wrapping.BlobInfo{ - Ciphertext: env.Ciphertext, - Iv: env.Iv, - KeyInfo: &wrapping.KeyInfo{ - KeyId: keyId, - WrappedKey: []byte(output.CiphertextBlob), - }, + ret = &wrapping.BlobInfo{ + Ciphertext: []byte(output.CiphertextBlob), + KeyInfo: &wrapping.KeyInfo{ + Mechanism: AliCloudKmsEncrypt, + KeyId: keyId, + }, + } + } else { + env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + if err != nil { + return nil, fmt.Errorf("error wrapping data: %w", err) + } + + input := kms.CreateEncryptRequest() + input.KeyId = k.keyId + input.Plaintext = base64.StdEncoding.EncodeToString(env.Key) + input.Domain = k.domain + + output, err := k.client.Encrypt(input) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } + + // Store the current key id. + keyId := output.KeyId + k.currentKeyId.Store(keyId) + + ret = &wrapping.BlobInfo{ + Ciphertext: env.Ciphertext, + Iv: env.Iv, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: AliCloudKmsEnvelopeAesGcmEncrypt, + KeyId: keyId, + WrappedKey: []byte(output.CiphertextBlob), + }, + } } return ret, nil @@ -201,30 +240,58 @@ func (k *Wrapper) Decrypt(_ context.Context, in *wrapping.BlobInfo, opt ...wrapp return nil, fmt.Errorf("given input for decryption is nil") } - // KeyId is not passed to this call because AliCloud handles this - // internally based on the metadata stored with the encrypted data - input := kms.CreateDecryptRequest() - input.CiphertextBlob = string(in.KeyInfo.WrappedKey) - input.Domain = k.domain - - output, err := k.client.Decrypt(input) - if err != nil { - return nil, fmt.Errorf("error decrypting data encryption key: %w", err) + if in.KeyInfo == nil { + return nil, errors.New("key info is nil") } - keyBytes, err := base64.StdEncoding.DecodeString(output.Plaintext) - if err != nil { - return nil, err - } + var plaintext []byte + switch in.KeyInfo.Mechanism { + case AliCloudKmsEncrypt: + // KeyId is not passed to this call because AliCloud handles this + // internally based on the metadata stored with the encrypted data + input := kms.CreateDecryptRequest() + input.CiphertextBlob = string(in.Ciphertext) + input.Domain = k.domain - envInfo := &wrapping.EnvelopeInfo{ - Key: keyBytes, - Iv: in.Iv, - Ciphertext: in.Ciphertext, - } - plaintext, err := wrapping.EnvelopeDecrypt(envInfo, opt...) - if err != nil { - return nil, fmt.Errorf("error decrypting data: %w", err) + output, err := k.client.Decrypt(input) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } + + plaintext, err = base64.StdEncoding.DecodeString(output.Plaintext) + if err != nil { + return nil, fmt.Errorf("error base64 decoding plaintext: %w", err) + } + + case AliCloudKmsEnvelopeAesGcmEncrypt: + // KeyId is not passed to this call because AliCloud handles this + // internally based on the metadata stored with the encrypted data + input := kms.CreateDecryptRequest() + input.CiphertextBlob = string(in.KeyInfo.WrappedKey) + input.Domain = k.domain + + output, err := k.client.Decrypt(input) + if err != nil { + return nil, fmt.Errorf("error decrypting data encryption key: %w", err) + } + + keyBytes, err := base64.StdEncoding.DecodeString(output.Plaintext) + if err != nil { + return nil, fmt.Errorf("error base64 decoding key: %w", err) + } + + envInfo := &wrapping.EnvelopeInfo{ + Key: keyBytes, + Iv: in.Iv, + Ciphertext: in.Ciphertext, + } + plaintext, err = wrapping.EnvelopeDecrypt(envInfo, opt...) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } + + default: + return nil, fmt.Errorf("invalid mechanism: %d", in.KeyInfo.Mechanism) } return plaintext, nil diff --git a/wrappers/alicloudkms/alicloudkms_test.go b/wrappers/alicloudkms/alicloudkms_test.go index e4bf8f3c..4a06724a 100644 --- a/wrappers/alicloudkms/alicloudkms_test.go +++ b/wrappers/alicloudkms/alicloudkms_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/aliyun/alibaba-cloud-sdk-go/services/kms" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" ) const aliCloudTestKeyId = "foo" @@ -73,6 +74,20 @@ func TestAliCloudKmsWrapper_Lifecycle(t *testing.T) { if !reflect.DeepEqual(input, pt) { t.Fatalf("expected %s, got %s", input, pt) } + + swi, err = s.Encrypt(context.Background(), input, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + pt, err = s.Decrypt(context.Background(), swi, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + if !reflect.DeepEqual(input, pt) { + t.Fatalf("expected %s, got %s", input, pt) + } } type mockAliCloudKmsWrapperClient struct { diff --git a/wrappers/alicloudkms/go.mod b/wrappers/alicloudkms/go.mod index 7d9cd82f..73dbe3c6 100644 --- a/wrappers/alicloudkms/go.mod +++ b/wrappers/alicloudkms/go.mod @@ -1,10 +1,10 @@ module github.com/hashicorp/go-kms-wrapping/wrappers/alicloudkms/v2 -go 1.20 +go 1.24.0 require ( github.com/aliyun/alibaba-cloud-sdk-go v1.61.1499 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 ) require ( diff --git a/wrappers/alicloudkms/go.sum b/wrappers/alicloudkms/go.sum index 7a24ac76..28ac3720 100644 --- a/wrappers/alicloudkms/go.sum +++ b/wrappers/alicloudkms/go.sum @@ -5,9 +5,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 h1:UjjwDABbv2Usc+ESZTC4DXimCRIyWUDBJe/LOUFxe/M= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17/go.mod h1:M79wTAvbhiVLo1WmglCPBgI5CEETtZWh18B3eXipmFc= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 h1:7mL3k701Go+prNM7xf7JAz6HAFM2iaAUEiPnjAEu0dA= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21/go.mod h1:NeK2Ul15t1zutp/dZzt28XQrGZHosbxE/QLNNfaWObM= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= @@ -42,7 +43,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -53,3 +56,4 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/wrappers/awskms/awskms.go b/wrappers/awskms/awskms.go index 0188b8aa..dc647a79 100644 --- a/wrappers/awskms/awskms.go +++ b/wrappers/awskms/awskms.go @@ -190,46 +190,81 @@ func (k *Wrapper) Encrypt(ctx context.Context, plaintext []byte, opt ...wrapping return nil, fmt.Errorf("given plaintext for encryption is nil") } - env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + opts, err := getOpts(opt...) if err != nil { - return nil, fmt.Errorf("error wrapping data: %w", err) + return nil, err } + var ret *wrapping.BlobInfo if k.client == nil { return nil, fmt.Errorf("nil client") } + if opts.WithoutEnvelope { + input := &kms.EncryptInput{ + KeyId: &k.keyId, + Plaintext: plaintext, + } + output, err := k.client.Encrypt(ctx, input) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } - input := &kms.EncryptInput{ - KeyId: &k.keyId, - Plaintext: env.Key, - } - output, err := k.client.Encrypt(ctx, input) - if err != nil { - return nil, fmt.Errorf("error encrypting data: %w", err) - } + // Store the current key id + // + // When using a key alias, this will return the actual underlying key id + // used for encryption. This is helpful if you are looking to reencyrpt + // your data when it is not using the latest key id. See these docs relating + // to key rotation https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html + keyId := output.KeyId + k.currentKeyId.Store(*keyId) + + ret = &wrapping.BlobInfo{ + Ciphertext: output.CiphertextBlob, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: AwsKmsEncrypt, + // Even though we do not use the key id during decryption, store it + // to know exactly the specific key used in encryption in case we + // want to rewrap older entries + KeyId: *keyId, + }, + } + } else { + env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + if err != nil { + return nil, fmt.Errorf("error wrapping data: %w", err) + } - // Store the current key id - // - // When using a key alias, this will return the actual underlying key id - // used for encryption. This is helpful if you are looking to reencyrpt - // your data when it is not using the latest key id. See these docs relating - // to key rotation https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html - keyId := output.KeyId - k.currentKeyId.Store(*keyId) - - ret := &wrapping.BlobInfo{ - Ciphertext: env.Ciphertext, - Iv: env.Iv, - KeyInfo: &wrapping.KeyInfo{ - Mechanism: AwsKmsEnvelopeAesGcmEncrypt, - // Even though we do not use the key id during decryption, store it - // to know exactly the specific key used in encryption in case we - // want to rewrap older entries - KeyId: *keyId, - WrappedKey: output.CiphertextBlob, - }, - } + input := &kms.EncryptInput{ + KeyId: &k.keyId, + Plaintext: env.Key, + } + output, err := k.client.Encrypt(ctx, input) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } + // Store the current key id + // + // When using a key alias, this will return the actual underlying key id + // used for encryption. This is helpful if you are looking to reencyrpt + // your data when it is not using the latest key id. See these docs relating + // to key rotation https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html + keyId := output.KeyId + k.currentKeyId.Store(*keyId) + + ret = &wrapping.BlobInfo{ + Ciphertext: env.Ciphertext, + Iv: env.Iv, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: AwsKmsEnvelopeAesGcmEncrypt, + // Even though we do not use the key id during decryption, store it + // to know exactly the specific key used in encryption in case we + // want to rewrap older entries + KeyId: *keyId, + WrappedKey: output.CiphertextBlob, + }, + } + } return ret, nil } @@ -239,11 +274,8 @@ func (k *Wrapper) Decrypt(ctx context.Context, in *wrapping.BlobInfo, opt ...wra return nil, fmt.Errorf("given input for decryption is nil") } - // Default to mechanism used before key info was stored if in.KeyInfo == nil { - in.KeyInfo = &wrapping.KeyInfo{ - Mechanism: AwsKmsEncrypt, - } + return nil, errors.New("key info is nil") } var plaintext []byte @@ -265,13 +297,13 @@ func (k *Wrapper) Decrypt(ctx context.Context, in *wrapping.BlobInfo, opt ...wra input := &kms.DecryptInput{ CiphertextBlob: in.KeyInfo.WrappedKey, } - output, err := k.client.Decrypt(ctx, input) + resp, err := k.client.Decrypt(ctx, input) if err != nil { return nil, fmt.Errorf("error decrypting data encryption key: %w", err) } envInfo := &wrapping.EnvelopeInfo{ - Key: output.Plaintext, + Key: resp.Plaintext, Iv: in.Iv, Ciphertext: in.Ciphertext, } diff --git a/wrappers/awskms/awskms_test.go b/wrappers/awskms/awskms_test.go index 128bf9fc..8866cc13 100644 --- a/wrappers/awskms/awskms_test.go +++ b/wrappers/awskms/awskms_test.go @@ -310,4 +310,12 @@ func encryptionRoundTrip(t *testing.T, w *Wrapper) { output, err := w.Decrypt(context.Background(), swi, nil) require.NoError(t, err) assert.Equal(t, expected, output) + + swi, err = w.Encrypt(context.Background(), expected, wrapping.WithoutEnvelope(true)) + require.NoError(t, err) + + output, err = w.Decrypt(context.Background(), swi, wrapping.WithoutEnvelope(true)) + require.NoError(t, err) + assert.Equal(t, expected, output) + } diff --git a/wrappers/awskms/go.mod b/wrappers/awskms/go.mod index 0a6bebff..8ecea464 100644 --- a/wrappers/awskms/go.mod +++ b/wrappers/awskms/go.mod @@ -8,10 +8,9 @@ require ( github.com/aws/aws-sdk-go-v2/service/kms v1.45.6 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-hclog v1.6.3 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.18 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.1 github.com/stretchr/testify v1.11.1 - go.uber.org/mock v0.6.0 ) require ( @@ -42,7 +41,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect - golang.org/x/sys v0.36.0 // indirect + golang.org/x/sys v0.38.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/wrappers/awskms/go.sum b/wrappers/awskms/go.sum index b586aaff..54f2f68e 100644 --- a/wrappers/awskms/go.sum +++ b/wrappers/awskms/go.sum @@ -1,63 +1,33 @@ github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= -github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= -github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= github.com/aws/aws-sdk-go-v2/config v1.31.12 h1:pYM1Qgy0dKZLHX2cXslNacbcEFMkDMl+Bcj5ROuS6p8= github.com/aws/aws-sdk-go-v2/config v1.31.12/go.mod h1:/MM0dyD7KSDPR+39p9ZNVKaHDLb9qnfDurvVS2KAhN8= -github.com/aws/aws-sdk-go-v2/config v1.31.17 h1:QFl8lL6RgakNK86vusim14P2k8BFSxjvUkcWLDjgz9Y= -github.com/aws/aws-sdk-go-v2/config v1.31.17/go.mod h1:V8P7ILjp/Uef/aX8TjGk6OHZN6IKPM5YW6S78QnRD5c= github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= -github.com/aws/aws-sdk-go-v2/credentials v1.18.21 h1:56HGpsgnmD+2/KpG0ikvvR8+3v3COCwaF4r+oWwOeNA= -github.com/aws/aws-sdk-go-v2/credentials v1.18.21/go.mod h1:3YELwedmQbw7cXNaII2Wywd+YY58AmLPwX4LzARgmmA= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 h1:Mv4Bc0mWmv6oDuSWTKnk+wgeqPL5DRFu5bQL9BGPQ8Y= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9/go.mod h1:IKlKfRppK2a1y0gy1yH6zD+yX5uplJ6UuPlgd48dJiQ= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/service/iam v1.47.7 h1:0EDAdmMTzsgXl++8a0JZ+Yx0/dOqT8o/EONknxlQK94= github.com/aws/aws-sdk-go-v2/service/iam v1.47.7/go.mod h1:NkNbn/8/mFrPUq0Kg6EM6c0+GaTLG+aPzXxwB7RF5xo= -github.com/aws/aws-sdk-go-v2/service/iam v1.49.2 h1:XeF6yEMX4/FxoSHCE1VNMOZ0t+mGnf/onqVe9dDVAlQ= -github.com/aws/aws-sdk-go-v2/service/iam v1.49.2/go.mod h1:cuEMbL1mNtO1sUyT+DYDNIA8Y7aJG1oIdgHqUk29Uzk= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 h1:5r34CgVOD4WZudeEKZ9/iKpiT6cM1JyEROpXjOcdWv8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9/go.mod h1:dB12CEbNWPbzO2uC6QSWHteqOg4JfBVJOojbAoAUb5I= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg= github.com/aws/aws-sdk-go-v2/service/kms v1.45.6 h1:Br3kil4j7RPW+7LoLVkYt8SuhIWlg6ylmbmzXJ7PgXY= github.com/aws/aws-sdk-go-v2/service/kms v1.45.6/go.mod h1:FKXkHzw1fJZtg1P1qoAIiwen5thz/cDRTTDCIu8ljxc= -github.com/aws/aws-sdk-go-v2/service/kms v1.47.1 h1:6+C0RoGF4HJQALrsecOXN7cm/l5rgNHCw2xbcvFgpH4= -github.com/aws/aws-sdk-go-v2/service/kms v1.47.1/go.mod h1:VJcNH6BLr+3VJwinRKdotLOMglHO8mIKlD3ea5c7hbw= github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 h1:A1oRkiSQOWstGh61y4Wc/yQ04sqrQZr1Si/oAXj20/s= github.com/aws/aws-sdk-go-v2/service/sso v1.29.6/go.mod h1:5PfYspyCU5Vw1wNPsxi15LZovOnULudOQuVxphSflQA= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 h1:5fm5RTONng73/QA73LhCNR7UT9RpFH3hR6HWL6bIgVY= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1/go.mod h1:xBEjWD13h+6nq+z4AkqSfSvqRKFgDIQeaMguAJndOWo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47hZb5HUQ0tn6Q9kA= github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= -github.com/aws/aws-sdk-go-v2/service/sts v1.39.1 h1:mLlUgHn02ue8whiR4BmxxGJLR2gwU6s6ZzJ5wDamBUs= -github.com/aws/aws-sdk-go-v2/service/sts v1.39.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= -github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= -github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -74,14 +44,9 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.18 h1:DLfC677GfKEpSAFpEWvl1vXsGpEcSHmbhBaPLrdDQHc= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.18/go.mod h1:t/eaR/mi2mw3klfl1WEAuiLKrlZ/Q8cosmsT+RIPLu0= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.19 h1:FX7HrkfkYomf4SlMrwzOP32FXuFltq34Qy/gXk1Tp5Y= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.19/go.mod h1:wpZygQlPUUGt4Klgg+RlCaq/KRe8XinEzqTf7QmvrNo= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21/go.mod h1:NeK2Ul15t1zutp/dZzt28XQrGZHosbxE/QLNNfaWObM= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.0 h1:V3TJFolOHYSDqQLbTUBygXtbX4jKXyBcDoU+KNZE1Ak= -github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.0/go.mod h1:OeRwM2eWNW62L1Z+8GvoZM5nQJMRWBewHSoo77qmb4Y= github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.1 h1:rXE5JmHT14VYLVm+hHSqBOojPl0rlBqJx6YLikUuCgM= github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.1/go.mod h1:6+rVulOPNCQbL3Xv2iLCqM0JmU2WO2wRzP1C6hBKeB8= github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM= @@ -116,18 +81,14 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= -go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/wrappers/azurekeyvault/azurekeyvault.go b/wrappers/azurekeyvault/azurekeyvault.go index 2015ec7e..80b018eb 100644 --- a/wrappers/azurekeyvault/azurekeyvault.go +++ b/wrappers/azurekeyvault/azurekeyvault.go @@ -38,6 +38,14 @@ const ( EnvAzureClientId = "AZURE_CLIENT_ID" ) +const ( + // AzureKeyVaultEnvelopeAesGcmEncrypt is when a data encryption key is generated and + // the data is encrypted with AES-GCM and the key is encrypted with KMS + AzureKeyVaultEnvelopeAesGcmEncrypt = iota + // AzureKeyVaultEncrypt is used to directly encrypt the data with KMS + AzureKeyVaultEncrypt +) + // Wrapper is an Wrapper that uses Azure Key Vault // for crypto operations. Azure Key Vault currently does not support // keys that can encrypt long data (RSA keys). Due to this fact, we generate @@ -217,35 +225,67 @@ func (v *Wrapper) Encrypt(ctx context.Context, plaintext []byte, opt ...wrapping return nil, errors.New("given plaintext for encryption is nil") } - env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) - if err != nil { - return nil, fmt.Errorf("error wrapping dat: %w", err) - } - // Encrypt the DEK using Key Vault - algo := azkeys.JSONWebKeyEncryptionAlgorithmRSAOAEP256 - params := azkeys.KeyOperationsParameters{ - Algorithm: &algo, - Value: env.Key, - } - // Wrap key with the latest version for the key name - resp, err := v.client.WrapKey(ctx, v.keyName, "", params, nil) + opts, err := getOpts(opt...) if err != nil { return nil, err } - // Store the current key version - keyVersion := ParseKeyVersion(resp.KID.Version()) - v.currentKeyId.Store(keyVersion) + var ret *wrapping.BlobInfo + if opts.WithoutEnvelope { + // Encrypt the plaintext directly using Key Vault + algo := azkeys.JSONWebKeyEncryptionAlgorithmRSAOAEP256 + params := azkeys.KeyOperationsParameters{ + Algorithm: &algo, + Value: plaintext, + } + // Wrap key with the latest version for the key name + resp, err := v.client.WrapKey(ctx, v.keyName, "", params, nil) + if err != nil { + return nil, err + } - ret := &wrapping.BlobInfo{ - Ciphertext: env.Ciphertext, - Iv: env.Iv, - KeyInfo: &wrapping.KeyInfo{ - KeyId: keyVersion, - WrappedKey: resp.Result, - }, - } + // Store the current key version + keyVersion := ParseKeyVersion(resp.KID.Version()) + v.currentKeyId.Store(keyVersion) + ret = &wrapping.BlobInfo{ + Ciphertext: resp.Result, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: AzureKeyVaultEncrypt, + KeyId: keyVersion, + }, + } + } else { + env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + if err != nil { + return nil, fmt.Errorf("error wrapping dat: %w", err) + } + // Encrypt the DEK using Key Vault + algo := azkeys.JSONWebKeyEncryptionAlgorithmRSAOAEP256 + params := azkeys.KeyOperationsParameters{ + Algorithm: &algo, + Value: env.Key, + } + // Wrap key with the latest version for the key name + resp, err := v.client.WrapKey(ctx, v.keyName, "", params, nil) + if err != nil { + return nil, err + } + + // Store the current key version + keyVersion := ParseKeyVersion(resp.KID.Version()) + v.currentKeyId.Store(keyVersion) + + ret = &wrapping.BlobInfo{ + Ciphertext: env.Ciphertext, + Iv: env.Iv, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: AzureKeyVaultEnvelopeAesGcmEncrypt, + KeyId: keyVersion, + WrappedKey: resp.Result, + }, + } + } return ret, nil } @@ -259,30 +299,55 @@ func (v *Wrapper) Decrypt(ctx context.Context, in *wrapping.BlobInfo, opt ...wra return nil, errors.New("key info is nil") } - // Unwrap the key - wrappedBytes, err := base64.RawURLEncoding.DecodeString(string(in.KeyInfo.WrappedKey)) - if err != nil { - // legacy unwrap as the key used to be stored base64 encoded and this is now handled in the json marshalling - // if it fails, the key is not encoded and can be used directly - wrappedBytes = in.KeyInfo.WrappedKey - } - algo := azkeys.JSONWebKeyEncryptionAlgorithmRSAOAEP256 - params := azkeys.KeyOperationsParameters{ - Algorithm: &algo, - Value: wrappedBytes, - } + var plaintext []byte + switch in.KeyInfo.Mechanism { + case AzureKeyVaultEncrypt: + algo := azkeys.JSONWebKeyEncryptionAlgorithmRSAOAEP256 + params := azkeys.KeyOperationsParameters{ + Algorithm: &algo, + Value: in.Ciphertext, + } - resp, err := v.client.UnwrapKey(ctx, v.keyName, in.KeyInfo.KeyId, params, nil) - if err != nil { - return nil, err - } + resp, err := v.client.UnwrapKey(ctx, v.keyName, in.KeyInfo.KeyId, params, nil) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } + plaintext = resp.Result - envInfo := &wrapping.EnvelopeInfo{ - Key: resp.Result, - Iv: in.Iv, - Ciphertext: in.Ciphertext, + case AzureKeyVaultEnvelopeAesGcmEncrypt: + wrappedKeyBytes, err := base64.RawURLEncoding.DecodeString(string(in.KeyInfo.WrappedKey)) + if err != nil { + // legacy unwrap as the key used to be stored base64 encoded and this is now handled in the json marshalling + // if it fails, the key is not encoded and can be used directly + wrappedKeyBytes = in.KeyInfo.WrappedKey + } + algo := azkeys.JSONWebKeyEncryptionAlgorithmRSAOAEP256 + params := azkeys.KeyOperationsParameters{ + Algorithm: &algo, + Value: wrappedKeyBytes, + } + + // Unwrap the key + resp, err := v.client.UnwrapKey(ctx, v.keyName, in.KeyInfo.KeyId, params, nil) + if err != nil { + return nil, fmt.Errorf("error decrypting data encryption key: %w", err) + } + + envInfo := &wrapping.EnvelopeInfo{ + Key: resp.Result, + Iv: in.Iv, + Ciphertext: in.Ciphertext, + } + plaintext, err = wrapping.EnvelopeDecrypt(envInfo, opt...) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } + + default: + return nil, fmt.Errorf("invalid mechanism: %d", in.KeyInfo.Mechanism) } - return wrapping.EnvelopeDecrypt(envInfo, opt...) + + return plaintext, nil } func (v *Wrapper) buildBaseURL() string { diff --git a/wrappers/azurekeyvault/azurekeyvault_acc_test.go b/wrappers/azurekeyvault/azurekeyvault_acc_test.go index da9be845..f9b377ae 100644 --- a/wrappers/azurekeyvault/azurekeyvault_acc_test.go +++ b/wrappers/azurekeyvault/azurekeyvault_acc_test.go @@ -103,4 +103,18 @@ func TestAzureKeyVault_Lifecycle(t *testing.T) { if !reflect.DeepEqual(input, pt) { t.Fatalf("expected %s, got %s", input, pt) } + + swi, err = s.Encrypt(context.Background(), input, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + pt, err = s.Decrypt(context.Background(), swi, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + if !reflect.DeepEqual(input, pt) { + t.Fatalf("expected %s, got %s", input, pt) + } } diff --git a/wrappers/azurekeyvault/go.mod b/wrappers/azurekeyvault/go.mod index 675b03f4..f14124e4 100644 --- a/wrappers/azurekeyvault/go.mod +++ b/wrappers/azurekeyvault/go.mod @@ -9,7 +9,7 @@ require ( github.com/Azure/go-autorest/autorest v0.11.29 github.com/Azure/go-autorest/autorest/to v0.4.0 github.com/hashicorp/go-hclog v1.5.0 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 github.com/stretchr/testify v1.10.0 golang.org/x/net v0.47.0 ) diff --git a/wrappers/azurekeyvault/go.sum b/wrappers/azurekeyvault/go.sum index f7856124..9ab33c34 100644 --- a/wrappers/azurekeyvault/go.sum +++ b/wrappers/azurekeyvault/go.sum @@ -53,8 +53,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 h1:UjjwDABbv2Usc+ESZTC4DXimCRIyWUDBJe/LOUFxe/M= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17/go.mod h1:M79wTAvbhiVLo1WmglCPBgI5CEETtZWh18B3eXipmFc= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 h1:7mL3k701Go+prNM7xf7JAz6HAFM2iaAUEiPnjAEu0dA= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21/go.mod h1:NeK2Ul15t1zutp/dZzt28XQrGZHosbxE/QLNNfaWObM= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= diff --git a/wrappers/gcpckms/gcpckms.go b/wrappers/gcpckms/gcpckms.go index e063f89a..6df7e169 100644 --- a/wrappers/gcpckms/gcpckms.go +++ b/wrappers/gcpckms/gcpckms.go @@ -220,35 +220,64 @@ func (s *Wrapper) Encrypt(ctx context.Context, plaintext []byte, opt ...wrapping return nil, errors.New("given plaintext for encryption is nil") } - env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) - if err != nil { - return nil, fmt.Errorf("error wrapping data: %w", err) - } - - resp, err := s.client.Encrypt(ctx, &kmspb.EncryptRequest{ - Name: s.parentName, - Plaintext: env.Key, - }) + opts, err := getOpts(opt...) if err != nil { return nil, err } - // Store current key id value - s.currentKeyId.Store(resp.Name) - - ret := &wrapping.BlobInfo{ - Ciphertext: env.Ciphertext, - Iv: env.Iv, - KeyInfo: &wrapping.KeyInfo{ - Mechanism: GcpCkmsEnvelopeAesGcmEncrypt, - // Even though we do not use the key id during decryption, store it - // to know exactly what version was used in encryption in case we - // want to rewrap older entries - KeyId: resp.Name, - WrappedKey: resp.Ciphertext, - }, - } + var ret *wrapping.BlobInfo + if opts.WithoutEnvelope { + resp, err := s.client.Encrypt(ctx, &kmspb.EncryptRequest{ + Name: s.parentName, + Plaintext: plaintext, + }) + if err != nil { + return nil, err + } + // Store current key id value + s.currentKeyId.Store(resp.Name) + + ret = &wrapping.BlobInfo{ + Ciphertext: resp.Ciphertext, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: GcpCkmsEncrypt, + // Even though we do not use the key id during decryption, store it + // to know exactly what version was used in encryption in case we + // want to rewrap older entries + KeyId: resp.Name, + }, + } + } else { + env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + if err != nil { + return nil, fmt.Errorf("error wrapping data: %w", err) + } + + resp, err := s.client.Encrypt(ctx, &kmspb.EncryptRequest{ + Name: s.parentName, + Plaintext: env.Key, + }) + if err != nil { + return nil, err + } + + // Store current key id value + s.currentKeyId.Store(resp.Name) + + ret = &wrapping.BlobInfo{ + Ciphertext: env.Ciphertext, + Iv: env.Iv, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: GcpCkmsEnvelopeAesGcmEncrypt, + // Even though we do not use the key id during decryption, store it + // to know exactly what version was used in encryption in case we + // want to rewrap older entries + KeyId: resp.Name, + WrappedKey: resp.Ciphertext, + }, + } + } return ret, nil } @@ -258,11 +287,8 @@ func (s *Wrapper) Decrypt(ctx context.Context, in *wrapping.BlobInfo, opt ...wra return nil, fmt.Errorf("given ciphertext for decryption is nil") } - // Default to mechanism used before key info was stored if in.KeyInfo == nil { - in.KeyInfo = &wrapping.KeyInfo{ - Mechanism: GcpCkmsEncrypt, - } + return nil, errors.New("key info is nil") } var plaintext []byte diff --git a/wrappers/gcpckms/gcpckms_acc_test.go b/wrappers/gcpckms/gcpckms_acc_test.go index fbe3695f..27bec852 100644 --- a/wrappers/gcpckms/gcpckms_acc_test.go +++ b/wrappers/gcpckms/gcpckms_acc_test.go @@ -200,6 +200,20 @@ func TestGcpCkmsSeal_Lifecycle(t *testing.T) { if !reflect.DeepEqual(input, pt) { t.Fatalf("expected %s, got %s", input, pt) } + + swi, err = s.Encrypt(context.Background(), input, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + pt, err = s.Decrypt(context.Background(), swi, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + if !reflect.DeepEqual(input, pt) { + t.Fatalf("expected %s, got %s", input, pt) + } } // checkAndSetEnvVars check and sets the required env vars. It will skip tests that are diff --git a/wrappers/gcpckms/go.mod b/wrappers/gcpckms/go.mod index c406feee..e8279696 100644 --- a/wrappers/gcpckms/go.mod +++ b/wrappers/gcpckms/go.mod @@ -1,10 +1,10 @@ module github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 -go 1.23.0 +go 1.24.0 require ( cloud.google.com/go/kms v1.10.1 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 golang.org/x/net v0.38.0 google.golang.org/api v0.114.0 ) @@ -26,7 +26,7 @@ require ( github.com/ryanuber/go-glob v1.0.0 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.31.0 // indirect + golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.23.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect diff --git a/wrappers/gcpckms/go.sum b/wrappers/gcpckms/go.sum index 7fa0a84f..cb656ea9 100644 --- a/wrappers/gcpckms/go.sum +++ b/wrappers/gcpckms/go.sum @@ -54,8 +54,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9 github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 h1:UjjwDABbv2Usc+ESZTC4DXimCRIyWUDBJe/LOUFxe/M= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17/go.mod h1:M79wTAvbhiVLo1WmglCPBgI5CEETtZWh18B3eXipmFc= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 h1:7mL3k701Go+prNM7xf7JAz6HAFM2iaAUEiPnjAEu0dA= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21/go.mod h1:NeK2Ul15t1zutp/dZzt28XQrGZHosbxE/QLNNfaWObM= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= @@ -106,8 +106,8 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/wrappers/huaweicloudkms/go.mod b/wrappers/huaweicloudkms/go.mod index 59321a61..32963c70 100644 --- a/wrappers/huaweicloudkms/go.mod +++ b/wrappers/huaweicloudkms/go.mod @@ -1,10 +1,10 @@ module github.com/hashicorp/go-kms-wrapping/wrappers/huaweicloudkms/v2 -go 1.20 +go 1.24.0 require ( github.com/hashicorp/go-cleanhttp v0.5.2 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 github.com/huaweicloud/golangsdk v0.0.0-20210831081626-d823fe11ceba ) diff --git a/wrappers/huaweicloudkms/go.sum b/wrappers/huaweicloudkms/go.sum index c6f4a551..81fc6690 100644 --- a/wrappers/huaweicloudkms/go.sum +++ b/wrappers/huaweicloudkms/go.sum @@ -1,9 +1,11 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 h1:UjjwDABbv2Usc+ESZTC4DXimCRIyWUDBJe/LOUFxe/M= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17/go.mod h1:M79wTAvbhiVLo1WmglCPBgI5CEETtZWh18B3eXipmFc= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 h1:7mL3k701Go+prNM7xf7JAz6HAFM2iaAUEiPnjAEu0dA= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21/go.mod h1:NeK2Ul15t1zutp/dZzt28XQrGZHosbxE/QLNNfaWObM= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= @@ -17,9 +19,11 @@ github.com/huaweicloud/golangsdk v0.0.0-20210831081626-d823fe11ceba/go.mod h1:fc github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -27,8 +31,10 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/wrappers/huaweicloudkms/huaweicloudkms.go b/wrappers/huaweicloudkms/huaweicloudkms.go index f96b4595..cfd888f5 100644 --- a/wrappers/huaweicloudkms/huaweicloudkms.go +++ b/wrappers/huaweicloudkms/huaweicloudkms.go @@ -7,6 +7,7 @@ import ( "context" "crypto/tls" "encoding/base64" + "errors" "fmt" "os" "sync/atomic" @@ -23,6 +24,14 @@ const ( EnvHuaweiCloudKmsWrapperKeyId = "HUAWEICLOUDKMS_WRAPPER_KEY_ID" ) +const ( + // HuaweiCloudKmsEnvelopeAesGcmEncrypt is when a data encryption key is generated and + // the data is encrypted with AES-GCM and the key is encrypted with KMS + HuaweiCloudKmsEnvelopeAesGcmEncrypt = iota + // HuaweiCloudKmsEncrypt is used to directly encrypt the data with KMS + HuaweiCloudKmsEncrypt +) + // Wrapper is a Wrapper that uses HuaweiCloud's KMS type Wrapper struct { client kmsClient @@ -111,28 +120,55 @@ func (k *Wrapper) Encrypt(_ context.Context, plaintext []byte, opt ...wrapping.O return nil, fmt.Errorf("given plaintext for encryption is nil") } - env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + opts, err := getOpts(opt...) if err != nil { - return nil, fmt.Errorf("error wrapping data: %w", err) + return nil, err } - output, err := k.client.encrypt(k.keyId, base64.StdEncoding.EncodeToString(env.Key)) - if err != nil { - return nil, fmt.Errorf("error encrypting data: %w", err) - } + var ret *wrapping.BlobInfo + if opts.WithoutEnvelope { + output, err := k.client.encrypt(k.keyId, base64.StdEncoding.EncodeToString(plaintext)) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } - // Store the current key id. - keyID := output.KeyId - k.currentKeyId.Store(keyID) - - return &wrapping.BlobInfo{ - Ciphertext: env.Ciphertext, - Iv: env.Iv, - KeyInfo: &wrapping.KeyInfo{ - KeyId: keyID, - WrappedKey: []byte(output.Ciphertext), - }, - }, nil + // Store the current key id. + keyID := output.KeyId + k.currentKeyId.Store(keyID) + + ret = &wrapping.BlobInfo{ + Ciphertext: []byte(output.Ciphertext), + KeyInfo: &wrapping.KeyInfo{ + Mechanism: HuaweiCloudKmsEncrypt, + KeyId: keyID, + }, + } + } else { + env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + if err != nil { + return nil, fmt.Errorf("error wrapping data: %w", err) + } + + output, err := k.client.encrypt(k.keyId, base64.StdEncoding.EncodeToString(env.Key)) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } + + // Store the current key id. + keyID := output.KeyId + k.currentKeyId.Store(keyID) + + ret = &wrapping.BlobInfo{ + Ciphertext: env.Ciphertext, + Iv: env.Iv, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: HuaweiCloudKmsEnvelopeAesGcmEncrypt, + KeyId: keyID, + WrappedKey: []byte(output.Ciphertext), + }, + } + } + return ret, nil } // Decrypt is used to decrypt the ciphertext. This should be called after Init. @@ -141,29 +177,50 @@ func (k *Wrapper) Decrypt(_ context.Context, in *wrapping.BlobInfo, opt ...wrapp return nil, fmt.Errorf("given input for decryption is nil") } - // KeyId is not passed to this call because HuaweiCloud handles this - // internally based on the metadata stored with the encrypted data - plainText, err := k.client.decrypt(string(in.KeyInfo.WrappedKey)) - if err != nil { - return nil, fmt.Errorf("error decrypting data encryption key: %w", err) + if in.KeyInfo == nil { + return nil, errors.New("key info is nil") } - keyBytes, err := base64.StdEncoding.DecodeString(plainText) - if err != nil { - return nil, err - } + var plaintext []byte + switch in.KeyInfo.Mechanism { + case HuaweiCloudKmsEncrypt: + plainText, err := k.client.decrypt(string(in.Ciphertext)) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } + plaintext, err = base64.StdEncoding.DecodeString(plainText) + if err != nil { + return nil, err + } - envInfo := &wrapping.EnvelopeInfo{ - Key: keyBytes, - Iv: in.Iv, - Ciphertext: in.Ciphertext, - } - pt, err := wrapping.EnvelopeDecrypt(envInfo, opt...) - if err != nil { - return nil, fmt.Errorf("error decrypting data: %w", err) + case HuaweiCloudKmsEnvelopeAesGcmEncrypt: + // KeyId is not passed to this call because HuaweiCloud handles this + // internally based on the metadata stored with the encrypted data + envelopeKeyStr, err := k.client.decrypt(string(in.KeyInfo.WrappedKey)) + if err != nil { + return nil, fmt.Errorf("error decrypting data encryption key: %w", err) + } + + envelopeKey, err := base64.StdEncoding.DecodeString(envelopeKeyStr) + if err != nil { + return nil, err + } + + envInfo := &wrapping.EnvelopeInfo{ + Key: envelopeKey, + Iv: in.Iv, + Ciphertext: in.Ciphertext, + } + plaintext, err = wrapping.EnvelopeDecrypt(envInfo, opt...) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } + + default: + return nil, fmt.Errorf("invalid mechanism: %d", in.KeyInfo.Mechanism) } - return pt, nil + return plaintext, nil } func getConfig(name string, values ...string) (string, error) { diff --git a/wrappers/huaweicloudkms/huaweicloudkms_test.go b/wrappers/huaweicloudkms/huaweicloudkms_test.go index 549863a9..cd86af8b 100644 --- a/wrappers/huaweicloudkms/huaweicloudkms_test.go +++ b/wrappers/huaweicloudkms/huaweicloudkms_test.go @@ -6,6 +6,7 @@ package huaweicloudkms import ( "context" "encoding/base64" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" "os" "reflect" "testing" @@ -68,6 +69,20 @@ func TestHuaweiCloudKmsWrapper_Lifecycle(t *testing.T) { if !reflect.DeepEqual(input, pt) { t.Fatalf("expected %s, got %s", input, pt) } + + swi, err = s.Encrypt(context.Background(), input, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + pt, err = s.Decrypt(context.Background(), swi, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + if !reflect.DeepEqual(input, pt) { + t.Fatalf("expected %s, got %s", input, pt) + } } type mockHuaweiCloudKmsWrapperClient struct{} diff --git a/wrappers/ibmkp/go.mod b/wrappers/ibmkp/go.mod index 56c9ad60..f7b9b78a 100644 --- a/wrappers/ibmkp/go.mod +++ b/wrappers/ibmkp/go.mod @@ -7,7 +7,7 @@ replace github.com/hashicorp/go-kms-wrapping/v2 => ../.. require ( github.com/IBM/keyprotect-go-client v0.15.1 github.com/hashicorp/go-hclog v1.6.3 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.18 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 github.com/stretchr/testify v1.11.1 ) diff --git a/wrappers/ibmkp/ibmkp.go b/wrappers/ibmkp/ibmkp.go index c11aeb00..282a6b3b 100644 --- a/wrappers/ibmkp/ibmkp.go +++ b/wrappers/ibmkp/ibmkp.go @@ -21,6 +21,14 @@ const ( EnvIbmKpKeyId = "IBMCLOUD_KP_KEY_ID" ) +const ( + // IbmKpEnvelopeAesGcmEncrypt is when a data encryption key is generated and + // the data is encrypted with AES-GCM and the key is encrypted with KMS + IbmKpEnvelopeAesGcmEncrypt = iota + // IbmKpEncrypt is used to directly encrypt the data with KMS + IbmKpEncrypt +) + // Wrapper represents credentials and Key information for the KMS Key used to // encryption and decryption type Wrapper struct { @@ -159,32 +167,55 @@ func (k *Wrapper) Encrypt(ctx context.Context, plaintext []byte, opt ...wrapping return nil, fmt.Errorf("given plaintext for encryption is nil") } - env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + opts, err := getOpts(opt...) if err != nil { - return nil, fmt.Errorf("error wrapping data: %w", err) + return nil, err } + var ret *wrapping.BlobInfo if k.client == nil { return nil, fmt.Errorf("nil client") } + if opts.WithoutEnvelope { + plaintextBase64 := []byte(base64.StdEncoding.EncodeToString(plaintext)) + ciphertext, err := k.client.Wrap(ctx, k.keyId, plaintextBase64, nil) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } - envelopeKeyBase64 := []byte(base64.StdEncoding.EncodeToString(env.Key)) - ciphertext, err := k.client.Wrap(ctx, k.keyId, envelopeKeyBase64, nil) - if err != nil { - return nil, fmt.Errorf("error encrypting data: %w", err) - } + k.currentkeyId.Store(k.keyId) + + ret = &wrapping.BlobInfo{ + Ciphertext: ciphertext, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: IbmKpEncrypt, + KeyId: k.keyId, + }, + } + } else { + env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + if err != nil { + return nil, fmt.Errorf("error wrapping data: %w", err) + } + + envelopeKeyBase64 := []byte(base64.StdEncoding.EncodeToString(env.Key)) + ciphertext, err := k.client.Wrap(ctx, k.keyId, envelopeKeyBase64, nil) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } - k.currentkeyId.Store(k.keyId) + k.currentkeyId.Store(k.keyId) - ret := &wrapping.BlobInfo{ - Ciphertext: env.Ciphertext, - Iv: env.Iv, - KeyInfo: &wrapping.KeyInfo{ - KeyId: k.keyId, - WrappedKey: ciphertext, - }, + ret = &wrapping.BlobInfo{ + Ciphertext: env.Ciphertext, + Iv: env.Iv, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: IbmKpEnvelopeAesGcmEncrypt, + KeyId: k.keyId, + WrappedKey: ciphertext, + }, + } } - return ret, nil } @@ -198,25 +229,42 @@ func (k *Wrapper) Decrypt(ctx context.Context, in *wrapping.BlobInfo, opt ...wra return nil, errors.New("key info is nil") } - envelopeKeyBase64, err := k.client.Unwrap(ctx, in.KeyInfo.KeyId, in.KeyInfo.WrappedKey, nil) - if err != nil { - return nil, err - } + var plaintext []byte + switch in.KeyInfo.Mechanism { + case IbmKpEncrypt: + plaintextBase64, err := k.client.Unwrap(ctx, in.KeyInfo.KeyId, in.Ciphertext, nil) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } + plaintext, err = base64.StdEncoding.DecodeString(string(plaintextBase64)) + if err != nil { + return nil, err + } - envelopeKey, err := base64.StdEncoding.DecodeString(string(envelopeKeyBase64)) - if err != nil { - return nil, err - } + case IbmKpEnvelopeAesGcmEncrypt: + envelopeKeyBase64, err := k.client.Unwrap(ctx, in.KeyInfo.KeyId, in.KeyInfo.WrappedKey, nil) + if err != nil { + return nil, fmt.Errorf("error decrypting data encryption key: %w", err) + } - envInfo := &wrapping.EnvelopeInfo{ - Key: envelopeKey, - Iv: in.Iv, - Ciphertext: in.Ciphertext, - } + envelopeKey, err := base64.StdEncoding.DecodeString(string(envelopeKeyBase64)) + if err != nil { + return nil, err + } - plaintext, err := wrapping.EnvelopeDecrypt(envInfo, opt...) - if err != nil { - return nil, fmt.Errorf("error decrypting data with envelope: %w", err) + envInfo := &wrapping.EnvelopeInfo{ + Key: envelopeKey, + Iv: in.Iv, + Ciphertext: in.Ciphertext, + } + + plaintext, err = wrapping.EnvelopeDecrypt(envInfo, opt...) + if err != nil { + return nil, fmt.Errorf("error decrypting data with envelope: %w", err) + } + + default: + return nil, fmt.Errorf("invalid mechanism: %d", in.KeyInfo.Mechanism) } return plaintext, nil diff --git a/wrappers/ibmkp/ibmkp_acc_test.go b/wrappers/ibmkp/ibmkp_acc_test.go index d4a77e30..bc8fd0cc 100644 --- a/wrappers/ibmkp/ibmkp_acc_test.go +++ b/wrappers/ibmkp/ibmkp_acc_test.go @@ -115,6 +115,20 @@ func TestIbmKp_Lifecycle(t *testing.T) { if subtle.ConstantTimeCompare(input, pt) == 0 { t.Fatalf("expected %s, got %s", input, pt) } + + swi, err = s.Encrypt(context.Background(), input, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("error encrypting: %s", err.Error()) + } + + pt, err = s.Decrypt(context.Background(), swi, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("error decrypting: %s", err.Error()) + } + + if subtle.ConstantTimeCompare(input, pt) == 0 { + t.Fatalf("expected %s, got %s", input, pt) + } } // checkAndSetEnvVars check and sets the required env vars. It will skip tests that are diff --git a/wrappers/ocikms/go.mod b/wrappers/ocikms/go.mod index 5db590c4..31800736 100644 --- a/wrappers/ocikms/go.mod +++ b/wrappers/ocikms/go.mod @@ -1,9 +1,9 @@ module github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 -go 1.23.0 +go 1.24.0 require ( - github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 github.com/oracle/oci-go-sdk/v60 v60.0.0 golang.org/x/net v0.38.0 ) diff --git a/wrappers/ocikms/go.sum b/wrappers/ocikms/go.sum index 58a89d2b..f846c100 100644 --- a/wrappers/ocikms/go.sum +++ b/wrappers/ocikms/go.sum @@ -3,8 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 h1:UjjwDABbv2Usc+ESZTC4DXimCRIyWUDBJe/LOUFxe/M= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17/go.mod h1:M79wTAvbhiVLo1WmglCPBgI5CEETtZWh18B3eXipmFc= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 h1:7mL3k701Go+prNM7xf7JAz6HAFM2iaAUEiPnjAEu0dA= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21/go.mod h1:NeK2Ul15t1zutp/dZzt28XQrGZHosbxE/QLNNfaWObM= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= diff --git a/wrappers/ocikms/ocikms.go b/wrappers/ocikms/ocikms.go index 93eb3774..574f6b82 100644 --- a/wrappers/ocikms/ocikms.go +++ b/wrappers/ocikms/ocikms.go @@ -31,6 +31,14 @@ const ( KMSMaximumNumberOfRetries = 5 ) +const ( + // OciKmsEnvelopeAesGcmEncrypt is when a data encryption key is generated and + // the data is encrypted with AES-GCM and the key is encrypted with KMS + OciKmsEnvelopeAesGcmEncrypt = iota + // OciKmsEncrypt is used to directly encrypt the data with KMS + OciKmsEncrypt +) + type Wrapper struct { authTypeAPIKey bool // true for user principal, false for instance principal, default is false keyId string // OCI KMS keyId @@ -150,53 +158,98 @@ func (k *Wrapper) Encrypt(ctx context.Context, plaintext []byte, opt ...wrapping return nil, errors.New("given plaintext for encryption is nil") } - env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + opts, err := getOpts(opt...) if err != nil { - return nil, fmt.Errorf("error wrapping data: %w", err) + return nil, err } + var ret *wrapping.BlobInfo if k.cryptoClient == nil { return nil, errors.New("nil client") } - // OCI KMS required base64 encrypted plain text before sending to the service - encodedKey := base64.StdEncoding.EncodeToString(env.Key) + if opts.WithoutEnvelope { + // OCI KMS required base64 encrypted plain text before sending to the service + encodedPlaintext := base64.StdEncoding.EncodeToString(plaintext) - // Build Encrypt Request - requestMetadata := k.getRequestMetadata() - encryptedDataDetails := keymanagement.EncryptDataDetails{ - KeyId: &k.keyId, - Plaintext: &encodedKey, - } + // Build Encrypt Request + requestMetadata := k.getRequestMetadata() + encryptedDataDetails := keymanagement.EncryptDataDetails{ + KeyId: &k.keyId, + Plaintext: &encodedPlaintext, + } - input := keymanagement.EncryptRequest{ - EncryptDataDetails: encryptedDataDetails, - RequestMetadata: requestMetadata, - } - output, err := k.cryptoClient.Encrypt(ctx, input) - if err != nil { - return nil, fmt.Errorf("error encrypting data: %w", err) - } + input := keymanagement.EncryptRequest{ + EncryptDataDetails: encryptedDataDetails, + RequestMetadata: requestMetadata, + } + output, err := k.cryptoClient.Encrypt(ctx, input) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } - // Note: It is potential a timing issue if the key gets rotated between this - // getCurrentKeyVersion operation and above Encrypt operation - keyVersion, err := k.getCurrentKeyVersion() - if err != nil { - return nil, fmt.Errorf("error getting current key version: %w", err) - } - // Update key version - k.currentKeyId.Store(keyVersion) - - ret := &wrapping.BlobInfo{ - Ciphertext: env.Ciphertext, - Iv: env.Iv, - KeyInfo: &wrapping.KeyInfo{ - // Storing current key version in case we want to re-wrap older entries - KeyId: keyVersion, - WrappedKey: []byte(*output.Ciphertext), - }, - } + // Note: It is potential a timing issue if the key gets rotated between this + // getCurrentKeyVersion operation and above Encrypt operation + keyVersion, err := k.getCurrentKeyVersion() + if err != nil { + return nil, fmt.Errorf("error getting current key version: %w", err) + } + // Update key version + k.currentKeyId.Store(keyVersion) + + ret = &wrapping.BlobInfo{ + Ciphertext: []byte(*output.Ciphertext), + KeyInfo: &wrapping.KeyInfo{ + Mechanism: OciKmsEncrypt, + // Storing current key version in case we want to re-wrap older entries + KeyId: keyVersion, + }, + } + } else { + env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + if err != nil { + return nil, fmt.Errorf("error wrapping data: %w", err) + } + + // OCI KMS required base64 encrypted plain text before sending to the service + encodedKey := base64.StdEncoding.EncodeToString(env.Key) + // Build Encrypt Request + requestMetadata := k.getRequestMetadata() + encryptedDataDetails := keymanagement.EncryptDataDetails{ + KeyId: &k.keyId, + Plaintext: &encodedKey, + } + + input := keymanagement.EncryptRequest{ + EncryptDataDetails: encryptedDataDetails, + RequestMetadata: requestMetadata, + } + output, err := k.cryptoClient.Encrypt(ctx, input) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } + + // Note: It is potential a timing issue if the key gets rotated between this + // getCurrentKeyVersion operation and above Encrypt operation + keyVersion, err := k.getCurrentKeyVersion() + if err != nil { + return nil, fmt.Errorf("error getting current key version: %w", err) + } + // Update key version + k.currentKeyId.Store(keyVersion) + + ret = &wrapping.BlobInfo{ + Ciphertext: env.Ciphertext, + Iv: env.Iv, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: OciKmsEnvelopeAesGcmEncrypt, + // Storing current key version in case we want to re-wrap older entries + KeyId: keyVersion, + WrappedKey: []byte(*output.Ciphertext), + }, + } + } return ret, nil } @@ -205,33 +258,64 @@ func (k *Wrapper) Decrypt(ctx context.Context, in *wrapping.BlobInfo, opt ...wra return nil, fmt.Errorf("given input for decryption is nil") } - requestMetadata := k.getRequestMetadata() - cipherTextBlob := string(in.KeyInfo.WrappedKey) - decryptedDataDetails := keymanagement.DecryptDataDetails{ - KeyId: &k.keyId, - Ciphertext: &cipherTextBlob, - } - input := keymanagement.DecryptRequest{ - DecryptDataDetails: decryptedDataDetails, - RequestMetadata: requestMetadata, - } - output, err := k.cryptoClient.Decrypt(ctx, input) - if err != nil { - return nil, fmt.Errorf("error decrypting data: %w", err) - } - envelopeKey, err := base64.StdEncoding.DecodeString(*output.Plaintext) - if err != nil { - return nil, fmt.Errorf("error base64 decrypting data: %w", err) - } - envInfo := &wrapping.EnvelopeInfo{ - Key: envelopeKey, - Iv: in.Iv, - Ciphertext: in.Ciphertext, + if in.KeyInfo == nil { + return nil, errors.New("key info is nil") } - plaintext, err := wrapping.EnvelopeDecrypt(envInfo, opt...) - if err != nil { - return nil, fmt.Errorf("error decrypting data: %w", err) + var plaintext []byte + switch in.KeyInfo.Mechanism { + case OciKmsEncrypt: + requestMetadata := k.getRequestMetadata() + cipherTextBlob := string(in.Ciphertext) + decryptedDataDetails := keymanagement.DecryptDataDetails{ + KeyId: &k.keyId, + Ciphertext: &cipherTextBlob, + } + input := keymanagement.DecryptRequest{ + DecryptDataDetails: decryptedDataDetails, + RequestMetadata: requestMetadata, + } + output, err := k.cryptoClient.Decrypt(ctx, input) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } + plaintext, err = base64.StdEncoding.DecodeString(*output.Plaintext) + if err != nil { + return nil, fmt.Errorf("error base64 decrypting data: %w", err) + } + + case OciKmsEnvelopeAesGcmEncrypt: + requestMetadata := k.getRequestMetadata() + cipherTextBlob := string(in.KeyInfo.WrappedKey) + decryptedDataDetails := keymanagement.DecryptDataDetails{ + KeyId: &k.keyId, + Ciphertext: &cipherTextBlob, + } + input := keymanagement.DecryptRequest{ + DecryptDataDetails: decryptedDataDetails, + RequestMetadata: requestMetadata, + } + output, err := k.cryptoClient.Decrypt(ctx, input) + if err != nil { + return nil, fmt.Errorf("error decrypting data encryption key: %w", err) + } + envelopeKey, err := base64.StdEncoding.DecodeString(*output.Plaintext) + if err != nil { + return nil, fmt.Errorf("error base64 decrypting data: %w", err) + } + envInfo := &wrapping.EnvelopeInfo{ + Key: envelopeKey, + Iv: in.Iv, + Ciphertext: in.Ciphertext, + } + + plaintext, err = wrapping.EnvelopeDecrypt(envInfo, opt...) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } + + default: + return nil, fmt.Errorf("invalid mechanism: %d", in.KeyInfo.Mechanism) } return plaintext, nil diff --git a/wrappers/ocikms/ocikms_test.go b/wrappers/ocikms/ocikms_test.go index fbccf9d5..9178dcb8 100644 --- a/wrappers/ocikms/ocikms_test.go +++ b/wrappers/ocikms/ocikms_test.go @@ -40,6 +40,20 @@ func TestWrapper_LifeCycle(t *testing.T) { if !reflect.DeepEqual(input, pt) { t.Fatalf("expected %s, got %s", input, pt) } + + swi, err = s.Encrypt(context.Background(), input, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + pt, err = s.Decrypt(context.Background(), swi, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + if !reflect.DeepEqual(input, pt) { + t.Fatalf("expected %s, got %s", input, pt) + } } func initSeal(t *testing.T) *Wrapper { diff --git a/wrappers/tencentcloudkms/go.mod b/wrappers/tencentcloudkms/go.mod index 5806aace..9f144cce 100644 --- a/wrappers/tencentcloudkms/go.mod +++ b/wrappers/tencentcloudkms/go.mod @@ -1,9 +1,9 @@ module github.com/hashicorp/go-kms-wrapping/wrappers/tencentcloudkms/v2 -go 1.20 +go 1.24.0 require ( - github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.604 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.604 ) diff --git a/wrappers/tencentcloudkms/go.sum b/wrappers/tencentcloudkms/go.sum index 8903405a..edf3a383 100644 --- a/wrappers/tencentcloudkms/go.sum +++ b/wrappers/tencentcloudkms/go.sum @@ -1,7 +1,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17 h1:UjjwDABbv2Usc+ESZTC4DXimCRIyWUDBJe/LOUFxe/M= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.17/go.mod h1:M79wTAvbhiVLo1WmglCPBgI5CEETtZWh18B3eXipmFc= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21 h1:7mL3k701Go+prNM7xf7JAz6HAFM2iaAUEiPnjAEu0dA= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.21/go.mod h1:NeK2Ul15t1zutp/dZzt28XQrGZHosbxE/QLNNfaWObM= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= @@ -13,14 +15,18 @@ github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.604 h1:BiuYcs8fINjZT9omCf8tV+rZkrZdf+Hq4TMWUNqYNgY= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.604/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.604 h1:LItsuG1cD+8e/9Z5MBNci2WJpYeNI0j00FN8UU5A3xw= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.604/go.mod h1:0NPGaAgj1qcvy3w0wEVhe3hMmDB0ptM+CqzXDu9r658= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/wrappers/tencentcloudkms/tencentcloudkms.go b/wrappers/tencentcloudkms/tencentcloudkms.go index c2e1a7a8..8fb0981c 100644 --- a/wrappers/tencentcloudkms/tencentcloudkms.go +++ b/wrappers/tencentcloudkms/tencentcloudkms.go @@ -6,6 +6,7 @@ package tencentcloudkms import ( "context" "encoding/base64" + "errors" "fmt" "os" "sync/atomic" @@ -25,6 +26,14 @@ const ( PROVIDER_KMS_KEY_ID = "TENCENTCLOUD_KMS_KEY_ID" ) +const ( + // TencentCloudKmsEnvelopeAesGcmEncrypt is when a data encryption key is generated and + // the data is encrypted with AES-GCM and the key is encrypted with KMS + TencentCloudKmsEnvelopeAesGcmEncrypt = iota + // TencentCloudKmsEncrypt is used to directly encrypt the data with KMS + TencentCloudKmsEncrypt +) + // Wrapper is a wrapper that uses TencentCloud KMS type Wrapper struct { accessKey string @@ -148,7 +157,7 @@ func (k *Wrapper) KeyId(_ context.Context) (string, error) { return k.currentKeyId.Load().(string), nil } -// Encrypt is used to encrypt the master key using the the TencentCloud KMS. +// Encrypt is used to encrypt the master key using the TencentCloud KMS. // This returns the ciphertext, and/or any errors from this call. // This should be called after the KMS client has been instantiated. func (k *Wrapper) Encrypt(_ context.Context, plaintext []byte, opt ...wrapping.Option) (*wrapping.BlobInfo, error) { @@ -156,64 +165,117 @@ func (k *Wrapper) Encrypt(_ context.Context, plaintext []byte, opt ...wrapping.O return nil, fmt.Errorf("given plaintext for encryption is nil") } - env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + opts, err := getOpts(opt...) if err != nil { - return nil, fmt.Errorf("error wrapping data: %w", err) + return nil, err } - input := kms.NewEncryptRequest() - input.KeyId = &k.keyId - input.Plaintext = common.StringPtr(base64.StdEncoding.EncodeToString(env.Key)) + var ret *wrapping.BlobInfo + if opts.WithoutEnvelope { + input := kms.NewEncryptRequest() + input.KeyId = &k.keyId + input.Plaintext = common.StringPtr(base64.StdEncoding.EncodeToString(plaintext)) - output, err := k.client.Encrypt(input) - if err != nil { - return nil, fmt.Errorf("error encrypting data: %w", err) - } + output, err := k.client.Encrypt(input) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } - keyId := *output.Response.KeyId - k.currentKeyId.Store(keyId) + keyId := *output.Response.KeyId + k.currentKeyId.Store(keyId) - ret := &wrapping.BlobInfo{ - Ciphertext: env.Ciphertext, - Iv: env.Iv, - KeyInfo: &wrapping.KeyInfo{ - KeyId: keyId, - WrappedKey: []byte(*output.Response.CiphertextBlob), - }, - } + ret = &wrapping.BlobInfo{ + Ciphertext: []byte(*output.Response.CiphertextBlob), + KeyInfo: &wrapping.KeyInfo{ + Mechanism: TencentCloudKmsEncrypt, + KeyId: keyId, + }, + } + } else { + env, err := wrapping.EnvelopeEncrypt(plaintext, opt...) + if err != nil { + return nil, fmt.Errorf("error wrapping data: %w", err) + } + + input := kms.NewEncryptRequest() + input.KeyId = &k.keyId + input.Plaintext = common.StringPtr(base64.StdEncoding.EncodeToString(env.Key)) + output, err := k.client.Encrypt(input) + if err != nil { + return nil, fmt.Errorf("error encrypting data: %w", err) + } + + keyId := *output.Response.KeyId + k.currentKeyId.Store(keyId) + + ret = &wrapping.BlobInfo{ + Ciphertext: env.Ciphertext, + Iv: env.Iv, + KeyInfo: &wrapping.KeyInfo{ + Mechanism: TencentCloudKmsEnvelopeAesGcmEncrypt, + KeyId: keyId, + WrappedKey: []byte(*output.Response.CiphertextBlob), + }, + } + } return ret, nil } -// Decrypt is used to decrypt the ciphertext using the the TencentCloud KMS. +// Decrypt is used to decrypt the ciphertext using the TencentCloud KMS. // This should be called after the KMS client has been instantiated. func (k *Wrapper) Decrypt(_ context.Context, in *wrapping.BlobInfo, opt ...wrapping.Option) ([]byte, error) { if in == nil { return nil, fmt.Errorf("given input for decryption is nil") } - input := kms.NewDecryptRequest() - input.CiphertextBlob = common.StringPtr(string(in.KeyInfo.WrappedKey)) - - output, err := k.client.Decrypt(input) - if err != nil { - return nil, fmt.Errorf("error decrypting data encryption key: %w", err) + if in.KeyInfo == nil { + return nil, errors.New("key info is nil") } - keyBytes, err := base64.StdEncoding.DecodeString(*output.Response.Plaintext) - if err != nil { - return nil, err - } + var plaintext []byte + switch in.KeyInfo.Mechanism { + case TencentCloudKmsEncrypt: + input := kms.NewDecryptRequest() + input.CiphertextBlob = common.StringPtr(string(in.Ciphertext)) - envInfo := &wrapping.EnvelopeInfo{ - Key: keyBytes, - Iv: in.Iv, - Ciphertext: in.Ciphertext, - } + output, err := k.client.Decrypt(input) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } - plaintext, err := wrapping.EnvelopeDecrypt(envInfo, opt...) - if err != nil { - return nil, fmt.Errorf("error decrypting data: %w", err) + plaintext, err = base64.StdEncoding.DecodeString(*output.Response.Plaintext) + if err != nil { + return nil, err + } + + case TencentCloudKmsEnvelopeAesGcmEncrypt: + input := kms.NewDecryptRequest() + input.CiphertextBlob = common.StringPtr(string(in.KeyInfo.WrappedKey)) + + output, err := k.client.Decrypt(input) + if err != nil { + return nil, fmt.Errorf("error decrypting data encryption key: %w", err) + } + + keyBytes, err := base64.StdEncoding.DecodeString(*output.Response.Plaintext) + if err != nil { + return nil, err + } + + envInfo := &wrapping.EnvelopeInfo{ + Key: keyBytes, + Iv: in.Iv, + Ciphertext: in.Ciphertext, + } + + plaintext, err = wrapping.EnvelopeDecrypt(envInfo, opt...) + if err != nil { + return nil, fmt.Errorf("error decrypting data: %w", err) + } + + default: + return nil, fmt.Errorf("invalid mechanism: %d", in.KeyInfo.Mechanism) } return plaintext, nil diff --git a/wrappers/tencentcloudkms/tencentcloudkms_test.go b/wrappers/tencentcloudkms/tencentcloudkms_test.go index 23f06a39..26c7ae7e 100644 --- a/wrappers/tencentcloudkms/tencentcloudkms_test.go +++ b/wrappers/tencentcloudkms/tencentcloudkms_test.go @@ -7,6 +7,7 @@ import ( "context" "encoding/base64" "errors" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" "os" "reflect" "testing" @@ -89,6 +90,20 @@ func TestTencentCloudKmsWrapper_Lifecycle(t *testing.T) { if !reflect.DeepEqual(input, pt) { t.Fatalf("expected %s, got %s", input, pt) } + + swi, err = s.Encrypt(context.Background(), input, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + pt, err = s.Decrypt(context.Background(), swi, wrapping.WithoutEnvelope(true)) + if err != nil { + t.Fatalf("err: %s", err.Error()) + } + + if !reflect.DeepEqual(input, pt) { + t.Fatalf("expected %s, got %s", input, pt) + } } // mockTencentCloudKmsWrapperClient is a mock client for testing