Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add naver cloud kms wrapper #262

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ as they may have been used for past encryption operations.
* * GCP CKMS (uses envelopes)
* * Huawei Cloud KMS (uses envelopes)
* * OCI KMS (uses envelopes)
* * Naver Cloud KMS (uses envelopes)
* * Tencent Cloud KMS (uses envelopes)
* * Vault Transit mount
* Transparently supports multiple decryption targets, allowing for key rotation
Expand Down
1 change: 1 addition & 0 deletions const.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
WrapperTypeHsmAuto WrapperType = "hsm-auto"
WrapperTypeHuaweiCloudKms WrapperType = "huaweicloudkms"
WrapperTypeOciKms WrapperType = "ocikms"
WrapperTypeNcloudKms WrapperType = "ncloudkms"
WrapperTypePkcs11 WrapperType = "pkcs11"
WrapperTypePooled WrapperType = "pooled"
WrapperTypeShamir WrapperType = "shamir"
Expand Down
20 changes: 20 additions & 0 deletions wrappers/ncloudkms/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module github.com/hashicorp/go-kms-wrapping/wrappers/ncloudkms/v2

go 1.21.4

toolchain go1.22.1

require (
github.com/hashicorp/go-hclog v1.6.3
github.com/hashicorp/go-kms-wrapping/v2 v2.0.16
github.com/jayground8/ncloud-sdk-go v0.0.3
)

require (
github.com/fatih/color v1.13.0 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
golang.org/x/sys v0.15.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)
43 changes: 43 additions & 0 deletions wrappers/ncloudkms/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
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-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.16 h1:WZeXfD26QMWYC35at25KgE021SF9L3u9UMHK8fJAdV0=
github.com/hashicorp/go-kms-wrapping/v2 v2.0.16/go.mod h1:ZiKZctjRTLEppuRwrttWkp71VYMbTTCkazK4xT7U/NQ=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/jayground8/ncloud-sdk-go v0.0.3 h1:OIXpwe1jzg2wPXsrvGWiw41WvHjKa7/z9sUKomDtPc8=
github.com/jayground8/ncloud-sdk-go v0.0.3/go.mod h1:quAEAOVI9vETCxf0kJtz3M4iPJyQr+XurLPMstVH4O0=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
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/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.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
138 changes: 138 additions & 0 deletions wrappers/ncloudkms/ncloudkms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package ncloudkms

import (
"context"
b64 "encoding/base64"
"errors"
"fmt"
"os"
"sync/atomic"

"github.com/hashicorp/go-hclog"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
sdk "github.com/jayground8/ncloud-sdk-go/client"
"github.com/jayground8/ncloud-sdk-go/ncloud/credentials"
)

const (
EnvNcloudKmsKeyTag = "NCLOUD_KMS_KEY_TAG"
)

type Wrapper struct {
keyId string
currentKeyId *atomic.Value

client *sdk.APIClient
logger hclog.Logger
}

var _ wrapping.Wrapper = (*Wrapper)(nil)

func NewWrapper() *Wrapper {
k := &Wrapper{
currentKeyId: new(atomic.Value),
}
k.currentKeyId.Store("")
return k
}

func (k *Wrapper) SetConfig(_ context.Context, opt ...wrapping.Option) (*wrapping.WrapperConfig, error) {
opts, err := getOpts(opt...)
if err != nil {
return nil, err
}

k.logger = opts.withLogger

switch {
case os.Getenv(EnvNcloudKmsKeyTag) != "":
k.keyId = os.Getenv(EnvNcloudKmsKeyTag)
case opts.WithKeyId != "":
k.keyId = opts.WithKeyId
default:
return nil, errors.New("key tag is required")
}

if k.client == nil {
credentials := credentials.NewValueProviderCreds("", "")
config := sdk.NewConfiguration(credentials)
client := sdk.NewAPIClient(config)
k.client = client
}

wrapConfig := new(wrapping.WrapperConfig)
return wrapConfig, nil
}

func (k *Wrapper) Type(_ context.Context) (wrapping.WrapperType, error) {
return wrapping.WrapperTypeNcloudKms, nil
}

func (k *Wrapper) KeyId(_ context.Context) (string, error) {
return k.currentKeyId.Load().(string), nil
}

func (k *Wrapper) Encrypt(ctx context.Context, plaintext []byte, opt ...wrapping.Option) (*wrapping.BlobInfo, error) {
if plaintext == nil {
return nil, fmt.Errorf("given plaintext for encryption is nil")
}

env, err := wrapping.EnvelopeEncrypt(plaintext, opt...)
if err != nil {
return nil, fmt.Errorf("error wrapping data: %w", err)
}
b64EncodedValue := b64.StdEncoding.EncodeToString(env.Key)
body := sdk.NewEncryptRequest(b64EncodedValue)
req := k.client.KmsAPI.Encrypt(context.Background(), k.keyId).EncryptRequest(*body)
value, _, err := k.client.KmsAPI.EncryptExecute(req)
if err != nil {
return nil, fmt.Errorf("error encrypting data: %w", err)
}
data := value.GetData()
ciphertext := data.GetCiphertext()
if ciphertext == "" {
return nil, fmt.Errorf("failed to get data from kms: %s", value.GetMsg())
}

k.currentKeyId.Store(k.keyId)

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: k.keyId,
WrappedKey: []byte(ciphertext),
},
}

return ret, nil
}

func (k *Wrapper) Decrypt(ctx context.Context, in *wrapping.BlobInfo, opt ...wrapping.Option) ([]byte, error) {
if in == nil {
return nil, fmt.Errorf("given input for decryption is nil")
}

body := sdk.NewDecryptRequest(string(in.KeyInfo.WrappedKey))
req := k.client.KmsAPI.Decrypt(context.Background(), k.keyId).DecryptRequest(*body)
value, _, err := k.client.KmsAPI.DecryptExecute(req)
if err != nil {
return nil, fmt.Errorf("error wrapping data: %w", err)
}
data := value.GetData()
b64EncodedPlaintext := data.GetPlaintext()
b64DecodedPlaintext, _ := b64.StdEncoding.DecodeString(b64EncodedPlaintext)

envInfo := &wrapping.EnvelopeInfo{
Key: b64DecodedPlaintext,
Iv: in.Iv,
Ciphertext: in.Ciphertext,
}
plaintext, err := wrapping.EnvelopeDecrypt(envInfo, opt...)
if err != nil {
return nil, fmt.Errorf("error decrypting data: %w", err)
}

return plaintext, nil
}
34 changes: 34 additions & 0 deletions wrappers/ncloudkms/ncloudkms_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ncloudkms

import (
"context"
"reflect"
"testing"
)

// To run this test, the following env variables need to be set:
// - NCLOUD_KMS_KEY_TAG
// - NCLOUD_ACCESS_KEY
// - NCLOUD_SECRET_KEY
func TestAccAwsKmsWrapper_Lifecycle(t *testing.T) {
s := NewWrapper()
testEncryptionRoundTrip(t, s)
}

func testEncryptionRoundTrip(t *testing.T, w *Wrapper) {
w.SetConfig(context.Background())
input := []byte("foo")
swi, err := w.Encrypt(context.Background(), input, nil)
if err != nil {
t.Fatalf("err: %s", err.Error())
}

pt, err := w.Decrypt(context.Background(), swi, nil)
if err != nil {
t.Fatalf("err: %s", err.Error())
}

if !reflect.DeepEqual(input, pt) {
t.Fatalf("expected %s, got %s", input, pt)
}
}
63 changes: 63 additions & 0 deletions wrappers/ncloudkms/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package ncloudkms

import (
"github.com/hashicorp/go-hclog"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
)

func getOpts(opt ...wrapping.Option) (*options, error) {
opts := getDefaultOptions()
var wrappingOptions []wrapping.Option
var localOptions []OptionFunc
for _, o := range opt {
if o == nil {
continue
}
iface := o()
switch to := iface.(type) {
case wrapping.OptionFunc:
wrappingOptions = append(wrappingOptions, o)
case OptionFunc:
localOptions = append(localOptions, to)
}
}

var err error
opts.Options, err = wrapping.GetOpts(wrappingOptions...)
if err != nil {
return nil, err
}

if opts.Options == nil {
opts.Options = new(wrapping.Options)
}

if opts.WithConfigMap != nil {
for k, _ := range opts.WithConfigMap {
switch k {
}
}
}

for _, o := range localOptions {
if o != nil {
if err := o(&opts); err != nil {
return nil, err
}
}
}

return &opts, nil
}

type OptionFunc func(*options) error

type options struct {
*wrapping.Options

withLogger hclog.Logger
}

func getDefaultOptions() options {
return options{}
}