diff --git a/.gitignore b/.gitignore index 5eb4a860c2..eb11be7ad5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ bin/ dist/ functional-tests/sops +functional-tests/target vendor/ profile.out diff --git a/README.rst b/README.rst index bde72ff8d6..1808ebe6c0 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ SOPS: Secrets OPerationS ======================== **SOPS** is an editor of encrypted files that supports YAML, JSON, ENV, INI and BINARY -formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, HuaweiCloud KMS, age, and PGP. +formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, HuaweiCloud KMS, Oracle Cloud KMS, age, and PGP. (`demo `_) .. image:: https://i.imgur.com/X0TM5NI.gif diff --git a/cmd/sops/main.go b/cmd/sops/main.go index 8092a3f400..ddf54ac40a 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -40,6 +40,7 @@ import ( "github.com/getsops/sops/v3/keyservice" "github.com/getsops/sops/v3/kms" "github.com/getsops/sops/v3/logging" + "github.com/getsops/sops/v3/ocikms" "github.com/getsops/sops/v3/pgp" "github.com/getsops/sops/v3/stores" "github.com/getsops/sops/v3/stores/dotenv" @@ -1217,8 +1218,8 @@ func main() { return toExitError(err) } if _, err := os.Stat(fileName); os.IsNotExist(err) { - if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hckms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" || - c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hckms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" { + if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hckms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" || c.String("add-oci-kms") != "" || + c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hckms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" || c.String("rm-oci-kms") != "" { return common.NewExitError(fmt.Sprintf("Error: cannot add or remove keys on non-existent file %q, use the `edit` subcommand instead.", fileName), codes.CannotChangeKeysFromNonExistentFile) } } @@ -1742,6 +1743,11 @@ func main() { Usage: "comma separated list of age recipients", EnvVar: "SOPS_AGE_RECIPIENTS", }, + cli.StringFlag{ + Name: "oci-kms", + Usage: "comma separated list of OCI KMS OCIDs", + EnvVar: "SOPS_OCI_KMS_OCIDS", + }, cli.BoolFlag{ Name: "in-place, i", Usage: "write output back to the same file instead of stdout", @@ -1810,6 +1816,14 @@ func main() { Name: "rm-age", Usage: "remove the provided comma-separated list of age recipients from the list of master keys on the given file", }, + cli.StringFlag{ + Name: "add-oci-kms", + Usage: "add the provided comma-separated list of OCI KMS keys OCIDs to the list of master keys on the given file", + }, + cli.StringFlag{ + Name: "rm-oci-kms", + Usage: "remove the provided comma-separated list of OCI KMS keys OCIDs from the list of master keys on the given file", + }, cli.StringFlag{ Name: "add-pgp", Usage: "add the provided comma-separated list of PGP fingerprints to the list of master keys on the given file", @@ -2243,7 +2257,7 @@ func getEncryptConfig(c *cli.Context, fileName string, inputStore common.Store, }, nil } -func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsOptionName string, pgpOptionName string, gcpKmsOptionName string, hckmsOptionName string, azureKvOptionName string, hcVaultTransitOptionName string, ageOptionName string) ([]keys.MasterKey, error) { +func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsOptionName string, pgpOptionName string, gcpKmsOptionName string, hckmsOptionName string, azureKvOptionName string, hcVaultTransitOptionName string, ageOptionName string, ociOptionName string) ([]keys.MasterKey, error) { var masterKeys []keys.MasterKey for _, k := range kms.MasterKeysFromArnString(c.String(kmsOptionName), kmsEncryptionContext, c.String("aws-profile")) { masterKeys = append(masterKeys, k) @@ -2282,16 +2296,19 @@ func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsO for _, k := range ageKeys { masterKeys = append(masterKeys, k) } + for _, k := range ocikms.MasterKeysFromOCIDString(c.String(ociOptionName)) { + masterKeys = append(masterKeys, k) + } return masterKeys, nil } func getRotateOpts(c *cli.Context, fileName string, inputStore common.Store, outputStore common.Store, svcs []keyservice.KeyServiceClient, decryptionOrder []string) (rotateOpts, error) { kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context")) - addMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "add-kms", "add-pgp", "add-gcp-kms", "add-hckms", "add-azure-kv", "add-hc-vault-transit", "add-age") + addMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "add-kms", "add-pgp", "add-gcp-kms", "add-hckms", "add-azure-kv", "add-hc-vault-transit", "add-age", "add-oci-kms") if err != nil { return rotateOpts{}, err } - rmMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "rm-kms", "rm-pgp", "rm-gcp-kms", "rm-hckms", "rm-azure-kv", "rm-hc-vault-transit", "rm-age") + rmMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "rm-kms", "rm-pgp", "rm-gcp-kms", "rm-hckms", "rm-azure-kv", "rm-hc-vault-transit", "rm-age", "rm-oci-kms") if err != nil { return rotateOpts{}, err } @@ -2441,6 +2458,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so var hcVaultMkKeys []keys.MasterKey var hckmsMkKeys []keys.MasterKey var ageMasterKeys []keys.MasterKey + var ociMasterKeys []keys.MasterKey kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context")) if c.String("encryption-context") != "" && kmsEncryptionContext == nil { return nil, common.NewExitError("Invalid KMS encryption context format", codes.ErrorInvalidKMSEncryptionContextFormat) @@ -2496,7 +2514,12 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so ageMasterKeys = append(ageMasterKeys, k) } } - if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" && c.String("hckms") == "" && c.String("azure-kv") == "" && c.String("hc-vault-transit") == "" && c.String("age") == "" { + if c.String("oci-kms") != "" { + for _, k := range ocikms.MasterKeysFromOCIDString(c.String("oci-kms")) { + ociMasterKeys = append(ociMasterKeys, k) + } + } + if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" && c.String("hckms") == "" && c.String("azure-kv") == "" && c.String("hc-vault-transit") == "" && c.String("age") == "" && c.String("oci-kms") == "" { conf := optionalConfig var err error if conf == nil { @@ -2520,6 +2543,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so group = append(group, pgpKeys...) group = append(group, hcVaultMkKeys...) group = append(group, ageMasterKeys...) + group = append(group, ociMasterKeys...) log.Debugf("Master keys available: %+v", group) return []sops.KeyGroup{group}, nil } diff --git a/config/config.go b/config/config.go index 511df1bc15..1030dd827e 100644 --- a/config/config.go +++ b/config/config.go @@ -18,6 +18,7 @@ import ( "github.com/getsops/sops/v3/hckms" "github.com/getsops/sops/v3/hcvault" "github.com/getsops/sops/v3/kms" + "github.com/getsops/sops/v3/ocikms" "github.com/getsops/sops/v3/pgp" "github.com/getsops/sops/v3/publish" "go.yaml.in/yaml/v3" @@ -137,6 +138,7 @@ type keyGroup struct { AzureKV []azureKVKey `yaml:"azure_keyvault"` Vault []string `yaml:"hc_vault"` Age []string `yaml:"age"` + OCIKMS []string `yaml:"oci_kms"` PGP []string `yaml:"pgp"` } @@ -179,6 +181,7 @@ type creationRule struct { PathRegex string `yaml:"path_regex"` KMS interface{} `yaml:"kms"` // string or []string AwsProfile string `yaml:"aws_profile"` + OCIKMS string `yaml:"oci_kms"` Age interface{} `yaml:"age"` // string or []string PGP interface{} `yaml:"pgp"` // string or []string GCPKMS interface{} `yaml:"gcp_kms"` // string or []string @@ -327,6 +330,9 @@ func extractMasterKeys(group keyGroup) (sops.KeyGroup, error) { keyGroup = append(keyGroup, key) } } + for _, k := range group.OCIKMS { + keyGroup = append(keyGroup, ocikms.NewMasterKeyFromOCID(k)) + } for _, k := range group.PGP { keyGroup = append(keyGroup, pgp.NewMasterKeyFromFingerprint(k)) } @@ -376,6 +382,9 @@ func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[ if err != nil { return nil, err } + for _, k := range group.OCIKMS { + keyGroup = append(keyGroup, ocikms.NewMasterKeyFromOCID(k)) + } groups = append(groups, keyGroup) } } else { @@ -423,6 +432,9 @@ func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[ for _, k := range hckmsMasterKeys { keyGroup = append(keyGroup, k) } + for _, k := range ocikms.MasterKeysFromOCIDString(cRule.OCIKMS) { + keyGroup = append(keyGroup, k) + } azKeys, err := getKeysWithValidation(cRule.GetAzureKeyVaultKeys, "azure_keyvault") if err != nil { return nil, err diff --git a/go.mod b/go.mod index e97c76c9be..b71a09725c 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,8 @@ require ( github.com/lib/pq v1.12.3 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-wordwrap v1.0.1 + github.com/ontariosystems/oci-cli-env-provider v0.1.0 + github.com/oracle/oci-go-sdk/v65 v65.101.0 github.com/ory/dockertest/v3 v3.12.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.10.1 @@ -101,6 +103,7 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-yaml v1.19.2 // indirect + github.com/gofrs/flock v0.12.1 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect @@ -133,11 +136,13 @@ require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/sony/gobreaker v1.0.0 // indirect github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.mongodb.org/mongo-driver v1.17.9 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect diff --git a/go.sum b/go.sum index 56b0cd64b3..aa8c9b2551 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.58.0/go.mod h1:dzcEjy1WJ0Q4u9twNR3LcLhNoYMRCrMCMafpxa0TjPQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.58.0 h1:SBZzZCiPmDrUV7NSCWY54OnKikO/oTydPCvyEyYaDDE= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.58.0/go.mod h1:YqwkQPrWSC7+byyc1VlKbWLBF5JsW5IoL6xUkemYSXk= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -173,6 +175,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= @@ -180,6 +184,9 @@ github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlnd github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -201,11 +208,14 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= +github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8 h1:ZI8gCoCjGzPsum4L21jHdQs8shFBIQih1TM9Rd/c+EQ= +github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= @@ -281,12 +291,20 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw= +github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/ontariosystems/oci-cli-env-provider v0.1.0 h1:xDEhUOXQrskVdyKPymw0tIvvhs8H/piKBCXNgOZ+Agc= +github.com/ontariosystems/oci-cli-env-provider v0.1.0/go.mod h1:7DFGsibH1hm9k4A31Ggf6Lg83ItgeC31I9G/xsVoiIQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runc v1.3.6 h1:SLGIymCtsk80iNPWgbc8dtjI30r+5mTVV+4dN8/17Sk= github.com/opencontainers/runc v1.3.6/go.mod h1:o1wyv76EDlTkcf0KTFgN8bMWLPvgF/HfX709lDv+rr4= +github.com/oracle/oci-go-sdk/v65 v65.101.0 h1:EErMOuw98JXi0P7DgPg5zjouCA5s61iWD5tFWNCVLHk= +github.com/oracle/oci-go-sdk/v65 v65.101.0/go.mod h1:RGiXfpDDmRRlLtqlStTzeBjjdUNXyqm3KXKyLCm3A/Q= github.com/ory/dockertest/v3 v3.12.0 h1:3oV9d0sDzlSQfHtIaB5k6ghUCVMVLpAY8hwrqoCyRCw= github.com/ory/dockertest/v3 v3.12.0/go.mod h1:aKNDTva3cp8dwOWwb9cWuX84aH5akkxXRvO7KCwWVjE= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= @@ -304,12 +322,17 @@ github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIH github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.10.1 h1:xi4336Zh11WpU14fXR6I67V3yaTPQYwRx2WEtHbRg4Q= github.com/sirupsen/logrus v1.10.1/go.mod h1:vsQHnG7xzNsxk3NrwboUiWPnIC3dmbjcGPykD7+tiHk= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ= +github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spiffe/go-spiffe/v2 v2.8.1 h1:eXZMLsu+3MLEPJyGJkolqtVrteZfQdUpOWj6LTiDl/E= github.com/spiffe/go-spiffe/v2 v2.8.1/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -335,6 +358,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1: github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU= @@ -361,6 +386,8 @@ go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRk go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -368,6 +395,11 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -375,6 +407,10 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -385,6 +421,11 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -394,6 +435,11 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -410,10 +456,25 @@ golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -423,6 +484,11 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= @@ -434,6 +500,11 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/keyservice/keyservice.go b/keyservice/keyservice.go index 04125f7510..5b65267008 100644 --- a/keyservice/keyservice.go +++ b/keyservice/keyservice.go @@ -14,6 +14,7 @@ import ( "github.com/getsops/sops/v3/hcvault" "github.com/getsops/sops/v3/keys" "github.com/getsops/sops/v3/kms" + "github.com/getsops/sops/v3/ocikms" "github.com/getsops/sops/v3/pgp" ) @@ -87,6 +88,14 @@ func KeyFromMasterKey(mk keys.MasterKey) Key { }, }, } + case *ocikms.MasterKey: + return Key{ + KeyType: &Key_OciKey{ + OciKey: &OciKey{ + Ocid: mk.Ocid, + }, + }, + } default: panic(fmt.Sprintf("Tried to convert unknown MasterKey type %T to keyservice.Key", mk)) } diff --git a/keyservice/keyservice.pb.go b/keyservice/keyservice.pb.go index 6314929c7d..c3677086cf 100644 --- a/keyservice/keyservice.pb.go +++ b/keyservice/keyservice.pb.go @@ -1,17 +1,16 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.35.2 -// protoc v5.28.3 +// protoc v3.21.9 // source: keyservice/keyservice.proto package keyservice import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) const ( @@ -35,6 +34,7 @@ type Key struct { // *Key_VaultKey // *Key_AgeKey // *Key_HckmsKey + // *Key_OciKey KeyType isKey_KeyType `protobuf_oneof:"key_type"` } @@ -124,6 +124,13 @@ func (x *Key) GetHckmsKey() *HckmsKey { return nil } +func (x *Key) GetOciKey() *OciKey { + if x, ok := x.GetKeyType().(*Key_OciKey); ok { + return x.OciKey + } + return nil +} + type isKey_KeyType interface { isKey_KeyType() } @@ -156,6 +163,10 @@ type Key_HckmsKey struct { HckmsKey *HckmsKey `protobuf:"bytes,7,opt,name=hckms_key,json=hckmsKey,proto3,oneof"` } +type Key_OciKey struct { + OciKey *OciKey `protobuf:"bytes,8,opt,name=oci_key,json=ociKey,proto3,oneof"` +} + func (*Key_KmsKey) isKey_KeyType() {} func (*Key_PgpKey) isKey_KeyType() {} @@ -170,6 +181,8 @@ func (*Key_AgeKey) isKey_KeyType() {} func (*Key_HckmsKey) isKey_KeyType() {} +func (*Key_OciKey) isKey_KeyType() {} + type PgpKey struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -541,6 +554,51 @@ func (x *HckmsKey) GetKeyId() string { return "" } +type OciKey struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ocid string `protobuf:"bytes,1,opt,name=ocid,proto3" json:"ocid,omitempty"` +} + +func (x *OciKey) Reset() { + *x = OciKey{} + mi := &file_keyservice_keyservice_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *OciKey) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OciKey) ProtoMessage() {} + +func (x *OciKey) ProtoReflect() protoreflect.Message { + mi := &file_keyservice_keyservice_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OciKey.ProtoReflect.Descriptor instead. +func (*OciKey) Descriptor() ([]byte, []int) { + return file_keyservice_keyservice_proto_rawDescGZIP(), []int{8} +} + +func (x *OciKey) GetOcid() string { + if x != nil { + return x.Ocid + } + return "" +} + type EncryptRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -552,7 +610,7 @@ type EncryptRequest struct { func (x *EncryptRequest) Reset() { *x = EncryptRequest{} - mi := &file_keyservice_keyservice_proto_msgTypes[8] + mi := &file_keyservice_keyservice_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -564,7 +622,7 @@ func (x *EncryptRequest) String() string { func (*EncryptRequest) ProtoMessage() {} func (x *EncryptRequest) ProtoReflect() protoreflect.Message { - mi := &file_keyservice_keyservice_proto_msgTypes[8] + mi := &file_keyservice_keyservice_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -577,7 +635,7 @@ func (x *EncryptRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use EncryptRequest.ProtoReflect.Descriptor instead. func (*EncryptRequest) Descriptor() ([]byte, []int) { - return file_keyservice_keyservice_proto_rawDescGZIP(), []int{8} + return file_keyservice_keyservice_proto_rawDescGZIP(), []int{9} } func (x *EncryptRequest) GetKey() *Key { @@ -604,7 +662,7 @@ type EncryptResponse struct { func (x *EncryptResponse) Reset() { *x = EncryptResponse{} - mi := &file_keyservice_keyservice_proto_msgTypes[9] + mi := &file_keyservice_keyservice_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -616,7 +674,7 @@ func (x *EncryptResponse) String() string { func (*EncryptResponse) ProtoMessage() {} func (x *EncryptResponse) ProtoReflect() protoreflect.Message { - mi := &file_keyservice_keyservice_proto_msgTypes[9] + mi := &file_keyservice_keyservice_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -629,7 +687,7 @@ func (x *EncryptResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use EncryptResponse.ProtoReflect.Descriptor instead. func (*EncryptResponse) Descriptor() ([]byte, []int) { - return file_keyservice_keyservice_proto_rawDescGZIP(), []int{9} + return file_keyservice_keyservice_proto_rawDescGZIP(), []int{10} } func (x *EncryptResponse) GetCiphertext() []byte { @@ -650,7 +708,7 @@ type DecryptRequest struct { func (x *DecryptRequest) Reset() { *x = DecryptRequest{} - mi := &file_keyservice_keyservice_proto_msgTypes[10] + mi := &file_keyservice_keyservice_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -662,7 +720,7 @@ func (x *DecryptRequest) String() string { func (*DecryptRequest) ProtoMessage() {} func (x *DecryptRequest) ProtoReflect() protoreflect.Message { - mi := &file_keyservice_keyservice_proto_msgTypes[10] + mi := &file_keyservice_keyservice_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -675,7 +733,7 @@ func (x *DecryptRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DecryptRequest.ProtoReflect.Descriptor instead. func (*DecryptRequest) Descriptor() ([]byte, []int) { - return file_keyservice_keyservice_proto_rawDescGZIP(), []int{10} + return file_keyservice_keyservice_proto_rawDescGZIP(), []int{11} } func (x *DecryptRequest) GetKey() *Key { @@ -702,7 +760,7 @@ type DecryptResponse struct { func (x *DecryptResponse) Reset() { *x = DecryptResponse{} - mi := &file_keyservice_keyservice_proto_msgTypes[11] + mi := &file_keyservice_keyservice_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -714,7 +772,7 @@ func (x *DecryptResponse) String() string { func (*DecryptResponse) ProtoMessage() {} func (x *DecryptResponse) ProtoReflect() protoreflect.Message { - mi := &file_keyservice_keyservice_proto_msgTypes[11] + mi := &file_keyservice_keyservice_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -727,7 +785,7 @@ func (x *DecryptResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DecryptResponse.ProtoReflect.Descriptor instead. func (*DecryptResponse) Descriptor() ([]byte, []int) { - return file_keyservice_keyservice_proto_rawDescGZIP(), []int{11} + return file_keyservice_keyservice_proto_rawDescGZIP(), []int{12} } func (x *DecryptResponse) GetPlaintext() []byte { @@ -741,7 +799,7 @@ var File_keyservice_keyservice_proto protoreflect.FileDescriptor var file_keyservice_keyservice_proto_rawDesc = []byte{ 0x0a, 0x1b, 0x6b, 0x65, 0x79, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x6b, 0x65, 0x79, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc2, 0x02, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe6, 0x02, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x07, 0x6b, 0x6d, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x07, 0x70, 0x67, 0x70, @@ -761,66 +819,70 @@ var file_keyservice_keyservice_proto_rawDesc = []byte{ 0x65, 0x79, 0x48, 0x00, 0x52, 0x06, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x09, 0x68, 0x63, 0x6b, 0x6d, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x48, 0x63, 0x6b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x48, 0x00, 0x52, 0x08, 0x68, 0x63, - 0x6b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x22, 0x2a, 0x0a, 0x06, 0x50, 0x67, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, - 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x22, 0xbb, - 0x01, 0x0a, 0x06, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x72, - 0x6f, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, - 0x2e, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, - 0x1f, 0x0a, 0x0b, 0x61, 0x77, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x77, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, - 0x1a, 0x3a, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2c, 0x0a, 0x09, - 0x47, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x22, 0x6b, 0x0a, 0x08, 0x56, 0x61, - 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, - 0x61, 0x75, 0x6c, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, - 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, - 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, 0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, - 0x4b, 0x65, 0x79, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x76, - 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x76, 0x61, 0x75, 0x6c, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x26, 0x0a, 0x06, 0x41, 0x67, 0x65, 0x4b, 0x65, 0x79, - 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x21, - 0x0a, 0x08, 0x48, 0x63, 0x6b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, - 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, - 0x64, 0x22, 0x46, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x04, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x70, - 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, - 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x31, 0x0a, 0x0f, 0x45, 0x6e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, - 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x48, 0x0a, 0x0e, - 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4b, 0x65, - 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, - 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, - 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2f, 0x0a, 0x0f, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6c, 0x61, - 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x6c, - 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x32, 0x6c, 0x0a, 0x0a, 0x4b, 0x65, 0x79, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x12, 0x0f, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x10, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x07, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x12, 0x0f, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x10, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x07, 0x6f, 0x63, 0x69, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x4f, 0x63, 0x69, 0x4b, 0x65, 0x79, + 0x48, 0x00, 0x52, 0x06, 0x6f, 0x63, 0x69, 0x4b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x6b, 0x65, + 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2a, 0x0a, 0x06, 0x50, 0x67, 0x70, 0x4b, 0x65, 0x79, + 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, + 0x6e, 0x74, 0x22, 0xbb, 0x01, 0x0a, 0x06, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x12, + 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, + 0x6f, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x2e, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x77, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x77, 0x73, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x1a, 0x3a, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x2c, 0x0a, 0x09, 0x47, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, + 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x22, 0x6b, + 0x0a, 0x08, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x61, + 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x50, 0x61, 0x74, 0x68, + 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, 0x10, 0x41, + 0x7a, 0x75, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, + 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x26, 0x0a, 0x06, 0x41, 0x67, + 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, + 0x6e, 0x74, 0x22, 0x21, 0x0a, 0x08, 0x48, 0x63, 0x6b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x15, + 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x1c, 0x0a, 0x06, 0x4f, 0x63, 0x69, 0x4b, 0x65, 0x79, 0x12, + 0x12, 0x0a, 0x04, 0x6f, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6f, + 0x63, 0x69, 0x64, 0x22, 0x46, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1c, 0x0a, + 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x31, 0x0a, 0x0f, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x48, + 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, + 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2f, 0x0a, 0x0f, 0x44, 0x65, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, + 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x32, 0x6c, 0x0a, 0x0a, 0x4b, 0x65, 0x79, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x07, 0x44, 0x65, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x2f, 0x6b, 0x65, 0x79, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -835,7 +897,7 @@ func file_keyservice_keyservice_proto_rawDescGZIP() []byte { return file_keyservice_keyservice_proto_rawDescData } -var file_keyservice_keyservice_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_keyservice_keyservice_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_keyservice_keyservice_proto_goTypes = []any{ (*Key)(nil), // 0: Key (*PgpKey)(nil), // 1: PgpKey @@ -845,11 +907,12 @@ var file_keyservice_keyservice_proto_goTypes = []any{ (*AzureKeyVaultKey)(nil), // 5: AzureKeyVaultKey (*AgeKey)(nil), // 6: AgeKey (*HckmsKey)(nil), // 7: HckmsKey - (*EncryptRequest)(nil), // 8: EncryptRequest - (*EncryptResponse)(nil), // 9: EncryptResponse - (*DecryptRequest)(nil), // 10: DecryptRequest - (*DecryptResponse)(nil), // 11: DecryptResponse - nil, // 12: KmsKey.ContextEntry + (*OciKey)(nil), // 8: OciKey + (*EncryptRequest)(nil), // 9: EncryptRequest + (*EncryptResponse)(nil), // 10: EncryptResponse + (*DecryptRequest)(nil), // 11: DecryptRequest + (*DecryptResponse)(nil), // 12: DecryptResponse + nil, // 13: KmsKey.ContextEntry } var file_keyservice_keyservice_proto_depIdxs = []int32{ 2, // 0: Key.kms_key:type_name -> KmsKey @@ -859,18 +922,19 @@ var file_keyservice_keyservice_proto_depIdxs = []int32{ 4, // 4: Key.vault_key:type_name -> VaultKey 6, // 5: Key.age_key:type_name -> AgeKey 7, // 6: Key.hckms_key:type_name -> HckmsKey - 12, // 7: KmsKey.context:type_name -> KmsKey.ContextEntry - 0, // 8: EncryptRequest.key:type_name -> Key - 0, // 9: DecryptRequest.key:type_name -> Key - 8, // 10: KeyService.Encrypt:input_type -> EncryptRequest - 10, // 11: KeyService.Decrypt:input_type -> DecryptRequest - 9, // 12: KeyService.Encrypt:output_type -> EncryptResponse - 11, // 13: KeyService.Decrypt:output_type -> DecryptResponse - 12, // [12:14] is the sub-list for method output_type - 10, // [10:12] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 8, // 7: Key.oci_key:type_name -> OciKey + 13, // 8: KmsKey.context:type_name -> KmsKey.ContextEntry + 0, // 9: EncryptRequest.key:type_name -> Key + 0, // 10: DecryptRequest.key:type_name -> Key + 9, // 11: KeyService.Encrypt:input_type -> EncryptRequest + 11, // 12: KeyService.Decrypt:input_type -> DecryptRequest + 10, // 13: KeyService.Encrypt:output_type -> EncryptResponse + 12, // 14: KeyService.Decrypt:output_type -> DecryptResponse + 13, // [13:15] is the sub-list for method output_type + 11, // [11:13] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_keyservice_keyservice_proto_init() } @@ -886,6 +950,7 @@ func file_keyservice_keyservice_proto_init() { (*Key_VaultKey)(nil), (*Key_AgeKey)(nil), (*Key_HckmsKey)(nil), + (*Key_OciKey)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -893,7 +958,7 @@ func file_keyservice_keyservice_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_keyservice_keyservice_proto_rawDesc, NumEnums: 0, - NumMessages: 13, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, diff --git a/keyservice/keyservice.proto b/keyservice/keyservice.proto index 3a471a34fd..a37ad146f8 100644 --- a/keyservice/keyservice.proto +++ b/keyservice/keyservice.proto @@ -11,6 +11,7 @@ message Key { VaultKey vault_key = 5; AgeKey age_key = 6; HckmsKey hckms_key = 7; + OciKey oci_key = 8; } } @@ -49,6 +50,10 @@ message HckmsKey { string key_id = 1; } +message OciKey { + string ocid = 1; +} + message EncryptRequest { Key key = 1; bytes plaintext = 2; diff --git a/keyservice/keyservice_grpc.pb.go b/keyservice/keyservice_grpc.pb.go index d278b82d97..7cdd3a2adc 100644 --- a/keyservice/keyservice_grpc.pb.go +++ b/keyservice/keyservice_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.3 +// - protoc v3.21.9 // source: keyservice/keyservice.proto package keyservice diff --git a/keyservice/server.go b/keyservice/server.go index c1f1e8ce86..f7d4c02ec7 100644 --- a/keyservice/server.go +++ b/keyservice/server.go @@ -9,6 +9,7 @@ import ( "github.com/getsops/sops/v3/hckms" "github.com/getsops/sops/v3/hcvault" "github.com/getsops/sops/v3/kms" + "github.com/getsops/sops/v3/ocikms" "github.com/getsops/sops/v3/pgp" "golang.org/x/net/context" "google.golang.org/grpc/codes" @@ -100,6 +101,17 @@ func (ks *Server) encryptWithAge(key *AgeKey, plaintext []byte) ([]byte, error) return []byte(ageKey.EncryptedKey), nil } +func (ks *Server) encryptWithOciKms(key *OciKey, plaintext []byte) ([]byte, error) { + ociKmsKey := ocikms.MasterKey{ + Ocid: key.Ocid, + } + err := ociKmsKey.Encrypt(plaintext) + if err != nil { + return nil, err + } + return []byte(ociKmsKey.EncryptedKey), nil +} + func (ks *Server) decryptWithPgp(key *PgpKey, ciphertext []byte) ([]byte, error) { pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint) pgpKey.EncryptedKey = string(ciphertext) @@ -167,6 +179,15 @@ func (ks *Server) decryptWithAge(key *AgeKey, ciphertext []byte) ([]byte, error) return []byte(plaintext), err } +func (ks *Server) decryptWithOciKms(key *OciKey, ciphertext []byte) ([]byte, error) { + ociKmsKey := ocikms.MasterKey{ + Ocid: key.Ocid, + } + ociKmsKey.EncryptedKey = string(ciphertext) + plaintext, err := ociKmsKey.Decrypt() + return []byte(plaintext), err +} + // Encrypt takes an encrypt request and encrypts the provided plaintext with the provided key, returning the encrypted // result func (ks Server) Encrypt(ctx context.Context, @@ -230,6 +251,14 @@ func (ks Server) Encrypt(ctx context.Context, response = &EncryptResponse{ Ciphertext: ciphertext, } + case *Key_OciKey: + ciphertext, err := ks.encryptWithOciKms(k.OciKey, req.Plaintext) + if err != nil { + return nil, err + } + response = &EncryptResponse{ + Ciphertext: ciphertext, + } case nil: return nil, status.Errorf(codes.NotFound, "Must provide a key") default: @@ -258,6 +287,8 @@ func keyToString(key *Key) string { return fmt.Sprintf("Hashicorp Vault key with URI %s/v1/%s/keys/%s", k.VaultKey.VaultAddress, k.VaultKey.EnginePath, k.VaultKey.KeyName) case *Key_HckmsKey: return fmt.Sprintf("HuaweiCloud KMS key with ID %s", k.HckmsKey.KeyId) + case *Key_OciKey: + return fmt.Sprintf("OCI KMS key with OCID %s", k.OciKey.Ocid) default: return "Unknown key type" } @@ -342,6 +373,14 @@ func (ks Server) Decrypt(ctx context.Context, response = &DecryptResponse{ Plaintext: plaintext, } + case *Key_OciKey: + plaintext, err := ks.decryptWithOciKms(k.OciKey, req.Ciphertext) + if err != nil { + return nil, err + } + response = &DecryptResponse{ + Plaintext: plaintext, + } case nil: return nil, status.Errorf(codes.NotFound, "Must provide a key") default: diff --git a/ocikms/config_provider.go b/ocikms/config_provider.go new file mode 100644 index 0000000000..b3e4a6a6c3 --- /dev/null +++ b/ocikms/config_provider.go @@ -0,0 +1,125 @@ +package ocikms + +import ( + "crypto/rsa" + "os" + "sync" + + ocep "github.com/ontariosystems/oci-cli-env-provider" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/common/auth" +) + +// newIPProvider is a variable to allow tests to stub the Instance Principal provider factory +var newIPProvider = auth.InstancePrincipalConfigurationProvider + +// configurationProvider composes multiple OCI configuration providers to make +// authentication work seamlessly across environments. +// Order of precedence (composing provider will try each in order until one works): +// 1) OCI_CLI_* environment variables (via ontariosystems/oci-cli-env-provider) +// 2) OCI_* environment variables (native SDK env provider) +// 3) Config file providers (OCI_CLI_CONFIG_FILE/PROFILE if set) +// 4) Default config provider (~/.oci/config, TF_VAR_*) +// 5) Instance Principals (when running on OCI compute) - lazily evaluated as last resort +func configurationProvider() (common.ConfigurationProvider, error) { + var providers []common.ConfigurationProvider + + // 1) Prefer the CLI-compatible envs used widely in CI/containers (envs only; no implicit fallbacks) + providers = append(providers, ocep.OciCliEnvironmentConfigurationProvider()) + + // 2) Native SDK envs (OCI_tenancy_ocid, OCI_user_ocid, OCI_fingerprint, OCI_private_key_path, OCI_region) + providers = append(providers, common.ConfigurationProviderEnvironmentVariables("OCI", "")) + + // 3) File-based fallbacks + if cfg := os.Getenv(OCICLIConfigFile); cfg != "" { + if prof := os.Getenv(OCICLIProfile); prof != "" { + if p, err := common.ConfigurationProviderFromFileWithProfile(cfg, prof, ""); err == nil { + providers = append(providers, p) + } + } else { + if p, err := common.ConfigurationProviderFromFile(cfg, ""); err == nil { + providers = append(providers, p) + } + } + } + + // 4) Default config provider (~/.oci/config, TF_VAR_*) + providers = append(providers, common.DefaultConfigProvider()) + + // 5) Instance principals for compute instances (lazy, only called if nothing else works) + providers = append(providers, &lazyConfigurationProvider{factory: newIPProvider}) + + return common.ComposingConfigurationProvider(providers) +} + +// lazyConfigurationProvider wraps a ConfigurationProvider factory function and defers its +// creation until the first method call. This is useful for expensive providers +// like Instance Principal that may timeout or fail in non-OCI environments. +type lazyConfigurationProvider struct { + factory func() (common.ConfigurationProvider, error) + provider common.ConfigurationProvider + once sync.Once + err error +} + +var _ common.ConfigurationProvider = (*lazyConfigurationProvider)(nil) + +func (l *lazyConfigurationProvider) init() { + l.provider, l.err = l.factory() +} + +func (l *lazyConfigurationProvider) TenancyOCID() (string, error) { + l.once.Do(l.init) + if l.err != nil { + return "", l.err + } + return l.provider.TenancyOCID() +} + +func (l *lazyConfigurationProvider) UserOCID() (string, error) { + l.once.Do(l.init) + if l.err != nil { + return "", l.err + } + return l.provider.UserOCID() +} + +func (l *lazyConfigurationProvider) KeyFingerprint() (string, error) { + l.once.Do(l.init) + if l.err != nil { + return "", l.err + } + return l.provider.KeyFingerprint() +} + +func (l *lazyConfigurationProvider) Region() (string, error) { + l.once.Do(l.init) + if l.err != nil { + return "", l.err + } + return l.provider.Region() +} + +func (l *lazyConfigurationProvider) KeyID() (string, error) { + l.once.Do(l.init) + if l.err != nil { + return "", l.err + } + return l.provider.KeyID() +} + +func (l *lazyConfigurationProvider) PrivateRSAKey() (*rsa.PrivateKey, error) { + l.once.Do(l.init) + if l.err != nil { + return nil, l.err + } + return l.provider.PrivateRSAKey() +} + +func (l *lazyConfigurationProvider) AuthType() (common.AuthConfig, error) { + l.once.Do(l.init) + if l.err != nil { + return common.AuthConfig{}, l.err + } + return l.provider.AuthType() +} diff --git a/ocikms/config_provider_test.go b/ocikms/config_provider_test.go new file mode 100644 index 0000000000..12b106a88d --- /dev/null +++ b/ocikms/config_provider_test.go @@ -0,0 +1,503 @@ +package ocikms + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/stretchr/testify/require" +) + +// writeTempRSAKey writes an unencrypted PKCS#1 RSA private key to a temp file. +func writeTempRSAKey(t *testing.T, dir string) string { + t.Helper() + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("generate key: %v", err) + } + keyBytes := x509.MarshalPKCS1PrivateKey(key) + pemBlock := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyBytes} + pemData := pem.EncodeToMemory(pemBlock) + path := filepath.Join(dir, "oci-test-private-key.pem") + if err := os.WriteFile(path, pemData, 0600); err != nil { + t.Fatalf("write key: %v", err) + } + return path +} + +// writeOCIConfig writes a minimal ~/.oci/config style file. +func writeOCIConfig(t *testing.T, path string, profile string, user string, tenancy string, region string, fingerprint string, keyFile string) { + t.Helper() + content := strings.Join([]string{ + "[" + profile + "]", + "user=" + user, + "fingerprint=" + fingerprint, + "key_file=" + keyFile, + "tenancy=" + tenancy, + "region=" + region, + "", + }, "\n") + if err := os.WriteFile(path, []byte(content), 0600); err != nil { + t.Fatalf("write config: %v", err) + } +} + +// clearOCIEnv clears OCI SDK environment variables to prevent interference +func clearOCIEnv(t *testing.T) { + t.Helper() + envVars := []string{ + "OCI_tenancy_ocid", + "OCI_user_ocid", + "OCI_region", + "OCI_fingerprint", + "OCI_private_key_path", + } + for _, env := range envVars { + t.Setenv(env, "") + } +} + +// clearCLIOCIEnv clears OCI CLI environment variables to prevent interference +func clearCLIOCIEnv() { + envVars := []string{ + OCICLITenancy, + OCICLIUser, + OCICLIRegion, + OCICLIFingerprint, + OCICLIKeyFile, + } + for _, env := range envVars { + os.Unsetenv(env) + } +} + +// disableIPProvider disables Instance Principal provider in tests +func disableIPProvider(t *testing.T) { + old := newIPProvider + t.Cleanup(func() { newIPProvider = old }) + newIPProvider = func() (common.ConfigurationProvider, error) { + return nil, fmt.Errorf("ip disabled in tests") + } +} + +func TestConfigurationProvider_OCI_CLI_Env(t *testing.T) { + // Disable IP network path in tests by overriding factory + disableIPProvider(t) + // Isolate HOME to avoid default file provider interference + t.Setenv(HomeEnv, t.TempDir()) + + // Generate key + keyDir := t.TempDir() + keyPath := writeTempRSAKey(t, keyDir) + + // Set OCI_CLI_* envs + t.Setenv(OCICLITenancy, "ocid1.tenancy.oc1..exampletenancy") + t.Setenv(OCICLIUser, "ocid1.user.oc1..exampleuser") + t.Setenv(OCICLIRegion, "us-ashburn-1") + t.Setenv(OCICLIFingerprint, "aa:bb:cc:dd") + t.Setenv(OCICLIKeyFile, keyPath) + + // Ensure other providers are not set by accident + // Native SDK env provider uses lower-case suffixes with prefix OCI_ + clearOCIEnv(t) + + prov, err := configurationProvider() + require.NoError(t, err) + + tenancy, err := prov.TenancyOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.tenancy.oc1..exampletenancy", tenancy) + + user, err := prov.UserOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.user.oc1..exampleuser", user) + + region, err := prov.Region() + require.NoError(t, err) + require.Equal(t, "us-ashburn-1", region) + + fp, err := prov.KeyFingerprint() + require.NoError(t, err) + require.Equal(t, "aa:bb:cc:dd", fp) +} + +func TestConfigurationProvider_OCI_Env(t *testing.T) { + disableIPProvider(t) + // Isolate HOME + t.Setenv(HomeEnv, t.TempDir()) + + keyDir := t.TempDir() + keyPath := writeTempRSAKey(t, keyDir) + + // SDK env provider expects lower-case suffixes + t.Setenv(OCITenancyOCID, "ocid1.tenancy.oc1..ten") + t.Setenv(OCIUserOCID, "ocid1.user.oc1..usr") + t.Setenv(OCIRegion, "eu-frankfurt-1") + t.Setenv(OCIFingerprint, "11:22:33:44") + t.Setenv(OCIPrivateKeyPath, keyPath) + + // Ensure CLI envs are not set (unset, not empty strings) + clearCLIOCIEnv() + + prov, err := configurationProvider() + require.NoError(t, err) + + tenancy, err := prov.TenancyOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.tenancy.oc1..ten", tenancy) + + user, err := prov.UserOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.user.oc1..usr", user) + + region, err := prov.Region() + require.NoError(t, err) + require.Equal(t, "eu-frankfurt-1", region) + + fp, err := prov.KeyFingerprint() + require.NoError(t, err) + require.Equal(t, "11:22:33:44", fp) +} + +func TestConfigurationProvider_FileViaEnv(t *testing.T) { + disableIPProvider(t) + // Isolate HOME + t.Setenv(HomeEnv, t.TempDir()) + + d := t.TempDir() + keyPath := writeTempRSAKey(t, d) + cfgPath := filepath.Join(d, "config") + writeOCIConfig(t, cfgPath, "DEFAULT", "ocid1.user.oc1..fileusr", "ocid1.tenancy.oc1..fileten", "uk-london-1", "ff:ee:dd:cc", keyPath) + + // Point to config via env + t.Setenv(OCICLIConfigFile, cfgPath) + // Explicit profile not required; default is DEFAULT + + // Ensure env-based providers are not set + clearCLIOCIEnv() + + clearOCIEnv(t) + + prov, err := configurationProvider() + require.NoError(t, err) + + tenancy, err := prov.TenancyOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.tenancy.oc1..fileten", tenancy) + + user, err := prov.UserOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.user.oc1..fileusr", user) + + region, err := prov.Region() + require.NoError(t, err) + require.Equal(t, "uk-london-1", region) + + fp, err := prov.KeyFingerprint() + require.NoError(t, err) + require.Equal(t, "ff:ee:dd:cc", fp) +} + +func TestConfigurationProvider_DefaultFileFallback(t *testing.T) { + disableIPProvider(t) + // Set HOME to a temp dir and create ~/.oci/config + home := t.TempDir() + if runtime.GOOS == "windows" { + // USERPROFILE is also consulted on Windows + t.Setenv(UserProfileEnv, home) + } + t.Setenv(HomeEnv, home) + + ociDir := filepath.Join(home, ".oci") + if err := os.MkdirAll(ociDir, 0700); err != nil { + t.Fatalf("mkdir: %v", err) + } + keyPath := writeTempRSAKey(t, ociDir) + cfgPath := filepath.Join(ociDir, "config") + writeOCIConfig(t, cfgPath, "DEFAULT", "ocid1.user.oc1..defusr", "ocid1.tenancy.oc1..deften", "ap-tokyo-1", "00:aa:bb:cc", keyPath) + + // Ensure no env points to explicit file and env providers are empty + os.Unsetenv(OCICLIConfigFile) + + clearCLIOCIEnv() + + clearOCIEnv(t) + + prov, err := common.ConfigurationProviderFromFile(cfgPath, "") + require.NoError(t, err) + + tenancy, err := prov.TenancyOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.tenancy.oc1..deften", tenancy) + + user, err := prov.UserOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.user.oc1..defusr", user) + + region, err := prov.Region() + require.NoError(t, err) + require.Equal(t, "ap-tokyo-1", region) + + fp, err := prov.KeyFingerprint() + require.NoError(t, err) + require.Equal(t, "00:aa:bb:cc", fp) +} + +// ipStubProvider implements common.ConfigurationProvider to stub Instance Principal in tests +type ipStubProvider struct{} + +func (ipStubProvider) TenancyOCID() (string, error) { return "ocid1.tenancy.oc1..ipstub", nil } +func (ipStubProvider) UserOCID() (string, error) { return "", nil } +func (ipStubProvider) KeyFingerprint() (string, error) { return "ip:stub:fp", nil } +func (ipStubProvider) Region() (string, error) { return "me-dubai-1", nil } +func (ipStubProvider) KeyID() (string, error) { return "ST$ipstub", nil } +func (ipStubProvider) PrivateRSAKey() (*rsa.PrivateKey, error) { + // generate a small key for completeness + k, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + return nil, err + } + return k, nil +} +func (ipStubProvider) AuthType() (common.AuthConfig, error) { return common.AuthConfig{}, nil } + +func TestConfigurationProvider_InstancePrincipal_Stubbed(t *testing.T) { + // Override IP factory to return stub, no network + old := newIPProvider + t.Cleanup(func() { newIPProvider = old }) + newIPProvider = func() (common.ConfigurationProvider, error) { return ipStubProvider{}, nil } + + // Isolate environment so that only IP path is viable + t.Setenv(HomeEnv, t.TempDir()) + os.Unsetenv(OCICLIConfigFile) + os.Unsetenv(OCICLIProfile) + + // Clear CLI envs + clearCLIOCIEnv() + + // Clear native SDK envs + clearOCIEnv(t) + + prov, err := configurationProvider() + require.NoError(t, err) + + tenancy, err := prov.TenancyOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.tenancy.oc1..ipstub", tenancy) + + region, err := prov.Region() + require.NoError(t, err) + require.Equal(t, "me-dubai-1", region) + + fp, err := prov.KeyFingerprint() + require.NoError(t, err) + require.Equal(t, "ip:stub:fp", fp) +} + +// TestConfigurationProvider_EarlyExit_SkipsInstancePrincipal verifies that +// when environment variables provide valid credentials, Instance Principal +// is NOT attempted (performance optimization). +func TestConfigurationProvider_EarlyExit_SkipsInstancePrincipal(t *testing.T) { + // Track whether Instance Principal provider was called + ipCalled := false + old := newIPProvider + t.Cleanup(func() { newIPProvider = old }) + newIPProvider = func() (common.ConfigurationProvider, error) { + ipCalled = true + // Return an error - if this is called, we want to know + return nil, fmt.Errorf("Instance Principal should not be called when env vars work") + } + + // Isolate HOME + t.Setenv(HomeEnv, t.TempDir()) + + // Generate key for env var auth + keyDir := t.TempDir() + keyPath := writeTempRSAKey(t, keyDir) + + // Set OCI_CLI_* env vars (highest priority) + t.Setenv(OCICLITenancy, "ocid1.tenancy.oc1..envtest") + t.Setenv(OCICLIUser, "ocid1.user.oc1..envtest") + t.Setenv(OCICLIRegion, "us-phoenix-1") + t.Setenv(OCICLIFingerprint, "aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99") + t.Setenv(OCICLIKeyFile, keyPath) + + prov, err := configurationProvider() + require.NoError(t, err) + + // Verify we got credentials from env vars + tenancy, err := prov.TenancyOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.tenancy.oc1..envtest", tenancy) + + region, err := prov.Region() + require.NoError(t, err) + require.Equal(t, "us-phoenix-1", region) + + // CRITICAL: Instance Principal should NOT have been called + require.False(t, ipCalled, "Instance Principal provider should NOT be called when env vars provide valid credentials (early exit optimization)") +} + +// TestConfigurationProvider_EarlyExit_FallsBackToInstancePrincipal verifies that +// when environment variables are missing or invalid, Instance Principal IS attempted. +func TestConfigurationProvider_EarlyExit_FallsBackToInstancePrincipal(t *testing.T) { + // Track whether Instance Principal provider was called + ipCalled := false + old := newIPProvider + t.Cleanup(func() { newIPProvider = old }) + newIPProvider = func() (common.ConfigurationProvider, error) { + ipCalled = true + return ipStubProvider{}, nil + } + + // Isolate environment - NO valid env vars or config files + t.Setenv(HomeEnv, t.TempDir()) + os.Unsetenv(OCICLIConfigFile) + os.Unsetenv(OCICLIProfile) + clearCLIOCIEnv() + clearOCIEnv(t) + + prov, err := configurationProvider() + require.NoError(t, err) + + tenancy, err := prov.TenancyOCID() + require.NoError(t, err) + + // Should have gotten Instance Principal credentials + require.Equal(t, "ocid1.tenancy.oc1..ipstub", tenancy) + + // CRITICAL: Instance Principal SHOULD have been called as fallback + require.True(t, ipCalled, "Instance Principal provider SHOULD be called when env vars don't provide credentials") +} + +// Test suite for lazyConfigurationProvider +func TestLazyProvider_FactoryNotCalledUntilFirstUse(t *testing.T) { + factoryCalled := false + factory := func() (common.ConfigurationProvider, error) { + factoryCalled = true + return ipStubProvider{}, nil + } + + lp := &lazyConfigurationProvider{factory: factory} + + // Factory should NOT be called just by creating the lazy provider + require.False(t, factoryCalled, "Factory should not be called on lazy provider creation") + + // Call a method - this should trigger factory + _, err := lp.TenancyOCID() + require.NoError(t, err) + require.True(t, factoryCalled, "Factory should be called on first method invocation") +} + +func TestLazyProvider_FactoryCalledOnlyOnce(t *testing.T) { + callCount := 0 + factory := func() (common.ConfigurationProvider, error) { + callCount++ + return ipStubProvider{}, nil + } + + lp := &lazyConfigurationProvider{factory: factory} + + // Call multiple methods + _, _ = lp.TenancyOCID() + _, _ = lp.Region() + _, _ = lp.KeyFingerprint() + _, _ = lp.UserOCID() + _, _ = lp.KeyID() + _, _ = lp.PrivateRSAKey() + _, _ = lp.AuthType() + + // Factory should only be called once despite 7 method calls + require.Equal(t, 1, callCount, "Factory should only be called once via sync.Once") +} + +func TestLazyProvider_PropagatesFactoryError(t *testing.T) { + expectedErr := fmt.Errorf("factory initialization failed") + factory := func() (common.ConfigurationProvider, error) { + return nil, expectedErr + } + + lp := &lazyConfigurationProvider{factory: factory} + + // All methods should return the factory error + _, err := lp.TenancyOCID() + require.ErrorIs(t, err, expectedErr) + + _, err = lp.Region() + require.ErrorIs(t, err, expectedErr) + + _, err = lp.KeyFingerprint() + require.ErrorIs(t, err, expectedErr) +} + +func TestLazyProvider_AllMethodsWorkAfterInit(t *testing.T) { + factory := func() (common.ConfigurationProvider, error) { + return ipStubProvider{}, nil + } + + lp := &lazyConfigurationProvider{factory: factory} + + // Test all ConfigurationProvider methods work correctly + tenancy, err := lp.TenancyOCID() + require.NoError(t, err) + require.Equal(t, "ocid1.tenancy.oc1..ipstub", tenancy) + + region, err := lp.Region() + require.NoError(t, err) + require.Equal(t, "me-dubai-1", region) + + fp, err := lp.KeyFingerprint() + require.NoError(t, err) + require.Equal(t, "ip:stub:fp", fp) + + keyID, err := lp.KeyID() + require.NoError(t, err) + require.Equal(t, "ST$ipstub", keyID) + + user, err := lp.UserOCID() + require.NoError(t, err) + require.Equal(t, "", user) + + key, err := lp.PrivateRSAKey() + require.NoError(t, err) + require.NotNil(t, key) + + authType, err := lp.AuthType() + require.NoError(t, err) + require.Equal(t, common.AuthConfig{}, authType) +} + +func TestLazyProvider_ConcurrentAccess(t *testing.T) { + callCount := 0 + factory := func() (common.ConfigurationProvider, error) { + callCount++ + return ipStubProvider{}, nil + } + + lp := &lazyConfigurationProvider{factory: factory} + + // Simulate concurrent access from multiple goroutines + done := make(chan bool, 10) + for i := 0; i < 10; i++ { + go func() { + _, _ = lp.TenancyOCID() + done <- true + }() + } + + // Wait for all goroutines + for i := 0; i < 10; i++ { + <-done + } + + // Factory should still only be called once (sync.Once is thread-safe) + require.Equal(t, 1, callCount, "Factory should only be called once even with concurrent access") +} diff --git a/ocikms/consts.go b/ocikms/consts.go new file mode 100644 index 0000000000..25ad44ace9 --- /dev/null +++ b/ocikms/consts.go @@ -0,0 +1,53 @@ +package ocikms + +// Key type constants +const ( + // KeyTypeIdentifier is the string used to identify an OCI KMS MasterKey in configuration + KeyTypeIdentifier = "oci_kms" +) + +// OCI CLI environment variables (used by oci-cli-env-provider) +const ( + // OCICLIConfigFile is the environment variable for OCI CLI config file path + OCICLIConfigFile = "OCI_CLI_CONFIG_FILE" + // OCICLIProfile is the environment variable for OCI CLI profile name + OCICLIProfile = "OCI_CLI_PROFILE" + // OCICLITenancy is the environment variable for OCI CLI tenancy OCID + OCICLITenancy = "OCI_CLI_TENANCY" + // OCICLIUser is the environment variable for OCI CLI user OCID + OCICLIUser = "OCI_CLI_USER" + // OCICLIRegion is the environment variable for OCI CLI region + OCICLIRegion = "OCI_CLI_REGION" + // OCICLIFingerprint is the environment variable for OCI CLI key fingerprint + OCICLIFingerprint = "OCI_CLI_FINGERPRINT" + // OCICLIKeyFile is the environment variable for OCI CLI private key file path + OCICLIKeyFile = "OCI_CLI_KEY_FILE" +) + +// OCI native SDK environment variables (lowercase after OCI_ prefix) +const ( + // OCITenancyOCID is the environment variable for OCI tenancy OCID (OCI_tenancy_ocid) + OCITenancyOCID = "OCI_tenancy_ocid" + // OCIUserOCID is the environment variable for OCI user OCID (OCI_user_ocid) + OCIUserOCID = "OCI_user_ocid" + // OCIRegion is the environment variable for OCI region (OCI_region) + OCIRegion = "OCI_region" + // OCIFingerprint is the environment variable for OCI key fingerprint (OCI_fingerprint) + OCIFingerprint = "OCI_fingerprint" + // OCIPrivateKeyPath is the environment variable for OCI private key path (OCI_private_key_path) + OCIPrivateKeyPath = "OCI_private_key_path" +) + +// Other environment variables +const ( + // HomeEnv is the HOME environment variable + HomeEnv = "HOME" + // UserProfileEnv is the USERPROFILE environment variable (Windows) + UserProfileEnv = "USERPROFILE" +) + +// Logger constants +const ( + // LoggerName is the name used for the OCI KMS logger + LoggerName = "OCIKMS" +) diff --git a/ocikms/keysource.go b/ocikms/keysource.go new file mode 100644 index 0000000000..aca08e2409 --- /dev/null +++ b/ocikms/keysource.go @@ -0,0 +1,265 @@ +package ocikms + +import ( + "context" + "encoding/base64" + "fmt" + "strings" + "time" + + "github.com/getsops/sops/v3/logging" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/keymanagement" + "github.com/sirupsen/logrus" +) + +var ( + // log is the global logger for any OCI KMS MasterKey. + log *logrus.Logger + // ocikmsTTL is the duration after which a MasterKey requires rotation. + ocikmsTTL = time.Hour * 24 * 30 * 6 +) + +const ( + // ocidParts is the number of parts in an OCID, separated by ".", eg: "ocid1.key.oc1.uk-london-1.aaaalgz5aacmg.aaaailjtjbkbc5ufsorrihgv2agugpfe7wrtngukihgkybqxcoozz7sbh6lq" + ocidParts = 6 +) + +func init() { + log = logging.NewLogger(LoggerName) +} + +// MasterKey is an Oracle Cloud KMS key used to encrypt and decrypt sops' data key. +type MasterKey struct { + // Ocid is the Oracle Cloud Identifier for the KMS key + Ocid string + // EncryptedKey stores the SOPS data key in its encrypted form + EncryptedKey string + // CreationDate is when this MasterKey was created + CreationDate time.Time + + // configProvider is used to configure the OCI client with credentials. + // It can be injected by a (local) keyservice.KeyServiceServer using + // ConfigurationProvider.ApplyToMasterKey. If nil, a fresh config + // provider is created on each operation which tries multiple auth methods. + configProvider common.ConfigurationProvider + // httpClient is used to override the default HTTP client used by the OCI client. + // Mostly useful for testing purposes. + httpClient common.HTTPRequestDispatcher +} + +func NewMasterKeyFromOCID(ocid string) *MasterKey { + return &MasterKey{ + Ocid: strings.Replace(ocid, " ", "", -1), + CreationDate: time.Now().UTC(), + } +} + +func MasterKeysFromOCIDString(ocids string) []*MasterKey { + var keys []*MasterKey + if ocids == "" { + return keys + } + for _, s := range strings.Split(ocids, ",") { + keys = append(keys, NewMasterKeyFromOCID(s)) + } + return keys +} + +// createCryptoClient creates a new OCI KMS client. It uses the injected configProvider +// if available, otherwise creates a new one on each call. If httpClient is set, it uses +// that for HTTP requests (useful for testing). +func (key *MasterKey) createCryptoClient() (client keymanagement.KmsCryptoClient, err error) { + region, vaultExt, err := extractRefs(key) + if err != nil { + log.WithField("ocid", key.Ocid).Errorf("Failed to extract region and vault from OCID: %s", err) + return client, fmt.Errorf("failed to parse OCI KMS key OCID: %w", err) + } + + cryptoEndpointTemplate := fmt.Sprintf("https://%s-crypto.kms.{region}.{secondLevelDomain}", vaultExt) + cryptoEndpoint := common.StringToRegion(region).EndpointForTemplate("kms", cryptoEndpointTemplate) + log.WithField("endpoint", cryptoEndpoint).Info("Creating OCI KMS client") + + // Use injected config provider if available, otherwise create a fresh one + cfg := key.configProvider + if cfg == nil { + cfg, err = configurationProvider() + if err != nil { + return client, fmt.Errorf("failed to create OCI configuration provider: %w", err) + } + } + + client, err = keymanagement.NewKmsCryptoClientWithConfigurationProvider(cfg, cryptoEndpoint) + if err != nil { + return client, fmt.Errorf("failed to create OCI KMS client: %w", err) + } + + // Inject custom HTTP client if provided (for testing) + if key.httpClient != nil { + client.HTTPClient = key.httpClient + } + + return client, nil +} + +func extractRefs(key *MasterKey) (string, string, error) { + parts := strings.Split(key.Ocid, ".") + if len(parts) != ocidParts { + return "", "", fmt.Errorf("invalid OCID format '%s': expected %d parts, got %d", key.Ocid, ocidParts, len(parts)) + } + region := parts[3] + vaultExt := parts[4] + return region, vaultExt, nil +} + +// EncryptedDataKey returns the encrypted data key this master key holds +func (key *MasterKey) EncryptedDataKey() []byte { + return []byte(key.EncryptedKey) +} + +// SetEncryptedDataKey sets the encrypted data key for this master key +func (key *MasterKey) SetEncryptedDataKey(enc []byte) { + key.EncryptedKey = string(enc) +} + +// Encrypt takes a sops data key, encrypts it with OCI KMS and stores the result +// in the EncryptedKey field. +// +// Consider using EncryptContext instead. +func (key *MasterKey) Encrypt(dataKey []byte) error { + return key.EncryptContext(context.Background(), dataKey) +} + +// EncryptContext takes a sops data key, encrypts it with OCI KMS and stores the result +// in the EncryptedKey field. +func (key *MasterKey) EncryptContext(ctx context.Context, dataKey []byte) error { + c, err := key.createCryptoClient() + if err != nil { + log.WithField("ocid", key.Ocid).Info("Encryption failed") + return fmt.Errorf("failed to create OCI KMS service: %w", err) + } + + data := base64.StdEncoding.EncodeToString(dataKey) + + res, err := c.Encrypt(ctx, keymanagement.EncryptRequest{ + EncryptDataDetails: keymanagement.EncryptDataDetails{ + KeyId: common.String(key.Ocid), + Plaintext: &data, + }, + RequestMetadata: common.RequestMetadata{}, + }) + + if err != nil { + log.WithError(err).WithField("ocid", key.Ocid). + Error("Encryption failed") + return fmt.Errorf("failed to encrypt sops data key with OCI KMS key: %w", err) + } + + key.EncryptedKey = *res.EncryptedData.Ciphertext + log.WithField("ocid", key.Ocid).Info("Encryption succeeded") + + return nil +} + +// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet +func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error { + if key.EncryptedKey == "" { + return key.Encrypt(dataKey) + } + return nil +} + +// Decrypt decrypts the EncryptedKey field with OCI KMS and returns the result. +// +// Consider using DecryptContext instead. +func (key *MasterKey) Decrypt() ([]byte, error) { + return key.DecryptContext(context.Background()) +} + +// DecryptContext decrypts the EncryptedKey field with OCI KMS and returns the result. +func (key *MasterKey) DecryptContext(ctx context.Context) ([]byte, error) { + c, err := key.createCryptoClient() + if err != nil { + log.WithField("ocid", key.Ocid).Info("Decryption failed") + return nil, fmt.Errorf("failed to create OCI KMS service: %w", err) + } + + res, err := c.Decrypt(ctx, keymanagement.DecryptRequest{ + DecryptDataDetails: keymanagement.DecryptDataDetails{ + Ciphertext: &key.EncryptedKey, + KeyId: &key.Ocid, + }, + }) + + if err != nil { + log.WithError(err).WithField("ocid", key.Ocid).Error("Decryption failed") + return nil, fmt.Errorf("failed to decrypt sops data key with OCI KMS key: %w", err) + } + + plaintext, err := base64.StdEncoding.DecodeString(*res.Plaintext) + if err != nil { + log.WithError(err).WithField("ocid", key.Ocid).Error("Decryption failed") + return nil, fmt.Errorf("failed to base64 decode OCI KMS decrypted key: %w", err) + } + + log.WithField("ocid", key.Ocid).Info("Decryption succeeded") + return plaintext, nil +} + +// NeedsRotation returns whether the data key needs to be rotated or not. +func (key *MasterKey) NeedsRotation() bool { + return time.Since(key.CreationDate) > ocikmsTTL +} + +// ToString converts the key to a string representation +func (key *MasterKey) ToString() string { + return key.Ocid +} + +// ToMap converts the MasterKey to a map for serialization purposes +func (key MasterKey) ToMap() map[string]interface{} { + out := make(map[string]interface{}) + out["ocid"] = key.Ocid + out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339) + out["enc"] = key.EncryptedKey + return out +} + +// TypeToIdentifier returns the string identifier for the MasterKey type. +func (key *MasterKey) TypeToIdentifier() string { + return KeyTypeIdentifier +} + +// ConfigurationProvider is a wrapper around common.ConfigurationProvider used for +// authentication towards OCI KMS. +type ConfigurationProvider struct { + provider common.ConfigurationProvider +} + +// NewConfigurationProvider creates a new ConfigurationProvider with the provided +// common.ConfigurationProvider. +func NewConfigurationProvider(cp common.ConfigurationProvider) *ConfigurationProvider { + return &ConfigurationProvider{provider: cp} +} + +// ApplyToMasterKey configures the ConfigurationProvider on the provided key. +func (c ConfigurationProvider) ApplyToMasterKey(key *MasterKey) { + key.configProvider = c.provider +} + +// HTTPClient is a wrapper around common.HTTPRequestDispatcher used for +// configuring the OCI KMS client HTTP requests. +type HTTPClient struct { + client common.HTTPRequestDispatcher +} + +// NewHTTPClient creates a new HTTPClient with the provided common.HTTPRequestDispatcher. +func NewHTTPClient(hc common.HTTPRequestDispatcher) *HTTPClient { + return &HTTPClient{client: hc} +} + +// ApplyToMasterKey configures the HTTP client on the provided key. +func (h HTTPClient) ApplyToMasterKey(key *MasterKey) { + key.httpClient = h.client +} diff --git a/ocikms/keysource_integration_test.go b/ocikms/keysource_integration_test.go new file mode 100644 index 0000000000..d74c137ef9 --- /dev/null +++ b/ocikms/keysource_integration_test.go @@ -0,0 +1,495 @@ +package ocikms + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "testing" + "time" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + // testOCID is a valid OCID format for testing + testOCID = "ocid1.key.oc1.uk-london-1.aaaalgz5aacmg.aaaailjtjbkbc5ufsorrihgv2agugpfe7wrtngukihgkybqxcoozz7sbh6lq" + // testDataKey is a dummy 32-byte data key for testing + testDataKey = "testtesttesttesttesttesttest1234" +) + +// mockHTTPClient implements common.HTTPRequestDispatcher for testing +type mockHTTPClient struct { + // requests stores all requests made for verification + requests []*http.Request + // responses is a queue of responses to return + responses []*http.Response + // errors is a queue of errors to return + errors []error + // currentIndex tracks which response to return next + currentIndex int +} + +func newMockHTTPClient() *mockHTTPClient { + return &mockHTTPClient{ + requests: make([]*http.Request, 0), + responses: make([]*http.Response, 0), + errors: make([]error, 0), + } +} + +// Do implements the common.HTTPRequestDispatcher interface +func (m *mockHTTPClient) Do(req *http.Request) (*http.Response, error) { + // Store the request for verification + m.requests = append(m.requests, req) + + if m.currentIndex >= len(m.responses) && m.currentIndex >= len(m.errors) { + return nil, fmt.Errorf("mock client: no more responses configured") + } + + // Return error if configured + if m.currentIndex < len(m.errors) && m.errors[m.currentIndex] != nil { + err := m.errors[m.currentIndex] + m.currentIndex++ + return nil, err + } + + // Return response if configured + if m.currentIndex < len(m.responses) { + resp := m.responses[m.currentIndex] + m.currentIndex++ + return resp, nil + } + + return nil, fmt.Errorf("mock client: no response or error configured for request %d", m.currentIndex) +} + +// addResponse adds a mock HTTP response to the queue +func (m *mockHTTPClient) addResponse(statusCode int, body string) { + resp := &http.Response{ + StatusCode: statusCode, + Body: io.NopCloser(strings.NewReader(body)), + Header: make(http.Header), + } + resp.Header.Set("Content-Type", "application/json") + m.responses = append(m.responses, resp) +} + +// addError adds an error to the queue +func (m *mockHTTPClient) addError(err error) { + m.errors = append(m.errors, err) +} + +// getLastRequest returns the most recent request made +func (m *mockHTTPClient) getLastRequest() *http.Request { + if len(m.requests) == 0 { + return nil + } + return m.requests[len(m.requests)-1] +} + +// mockConfigProvider implements common.ConfigurationProvider for testing +type mockConfigProvider struct { + privateKey *rsa.PrivateKey +} + +func newMockConfigProvider() mockConfigProvider { + // Generate a test RSA key (required by OCI SDK for request signing) + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + panic(fmt.Sprintf("failed to generate test RSA key: %v", err)) + } + return mockConfigProvider{ + privateKey: privateKey, + } +} + +func (m mockConfigProvider) TenancyOCID() (string, error) { + return "ocid1.tenancy.oc1..test", nil +} + +func (m mockConfigProvider) UserOCID() (string, error) { + return "ocid1.user.oc1..test", nil +} + +func (m mockConfigProvider) KeyFingerprint() (string, error) { + return "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00", nil +} + +func (m mockConfigProvider) Region() (string, error) { + return "uk-london-1", nil +} + +func (m mockConfigProvider) PrivateRSAKey() (*rsa.PrivateKey, error) { + return m.privateKey, nil +} + +func (m mockConfigProvider) KeyID() (string, error) { + tenancy, _ := m.TenancyOCID() + user, _ := m.UserOCID() + fingerprint, _ := m.KeyFingerprint() + return fmt.Sprintf("%s/%s/%s", tenancy, user, fingerprint), nil +} + +func (m mockConfigProvider) AuthType() (common.AuthConfig, error) { + return common.AuthConfig{ + AuthType: common.UserPrincipal, + }, nil +} + +// createTestMasterKey creates a MasterKey configured for testing with mock HTTP client +func createTestMasterKey(ocid string, mockHTTP *mockHTTPClient) *MasterKey { + key := NewMasterKeyFromOCID(ocid) + + // Inject mock config provider to avoid real auth + configProvider := NewConfigurationProvider(newMockConfigProvider()) + configProvider.ApplyToMasterKey(key) + + // Inject mock HTTP client + if mockHTTP != nil { + httpClient := NewHTTPClient(mockHTTP) + httpClient.ApplyToMasterKey(key) + } + + return key +} + +// createEncryptResponse creates a mock OCI KMS encrypt response +func createEncryptResponse(ciphertext string) string { + response := map[string]interface{}{ + "ciphertext": ciphertext, + } + data, _ := json.Marshal(response) + return string(data) +} + +// createDecryptResponse creates a mock OCI KMS decrypt response +func createDecryptResponse(plaintext string) string { + response := map[string]interface{}{ + "plaintext": plaintext, + } + data, _ := json.Marshal(response) + return string(data) +} + +func TestEncryptContext(t *testing.T) { + tests := []struct { + name string + dataKey []byte + mockResponse string + mockStatusCode int + mockError error + expectError bool + errorContains string + }{ + { + name: "successful encryption", + dataKey: []byte(testDataKey), + mockResponse: createEncryptResponse("ENCRYPTED_DATA_KEY_BASE64"), + mockStatusCode: 200, + expectError: false, + }, + { + name: "network error", + dataKey: []byte(testDataKey), + mockError: fmt.Errorf("network timeout"), + expectError: true, + errorContains: "failed to encrypt sops data key with OCI KMS key", + }, + { + name: "HTTP 500 error", + dataKey: []byte(testDataKey), + mockResponse: `{"code":"InternalServerError","message":"Internal server error"}`, + mockStatusCode: 500, + expectError: true, + errorContains: "failed to encrypt sops data key with OCI KMS key", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockHTTP := newMockHTTPClient() + + if tt.mockError != nil { + mockHTTP.addError(tt.mockError) + } else { + mockHTTP.addResponse(tt.mockStatusCode, tt.mockResponse) + } + + key := createTestMasterKey(testOCID, mockHTTP) + + err := key.EncryptContext(context.Background(), tt.dataKey) + + if tt.expectError { + assert.Error(t, err) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.NotEmpty(t, key.EncryptedKey) + assert.Equal(t, "ENCRYPTED_DATA_KEY_BASE64", key.EncryptedKey) + } + + // Verify request was made (unless error before request) + if tt.mockError == nil || tt.mockStatusCode > 0 { + assert.Greater(t, len(mockHTTP.requests), 0, "should have made at least one HTTP request") + } + }) + } +} + +func TestDecryptContext(t *testing.T) { + dataKeyBase64 := base64.StdEncoding.EncodeToString([]byte(testDataKey)) + + tests := []struct { + name string + encryptedKey string + mockResponse string + mockStatusCode int + mockError error + expectError bool + errorContains string + expectedPlain []byte + }{ + { + name: "successful decryption", + encryptedKey: "ENCRYPTED_DATA_KEY_BASE64", + mockResponse: createDecryptResponse(dataKeyBase64), + mockStatusCode: 200, + expectError: false, + expectedPlain: []byte(testDataKey), + }, + { + name: "network error", + encryptedKey: "ENCRYPTED_DATA_KEY_BASE64", + mockError: fmt.Errorf("connection refused"), + expectError: true, + errorContains: "failed to decrypt sops data key with OCI KMS key", + }, + { + name: "invalid ciphertext", + encryptedKey: "INVALID_CIPHERTEXT", + mockResponse: `{"code":"InvalidCiphertext","message":"The ciphertext is invalid"}`, + mockStatusCode: 400, + expectError: true, + errorContains: "failed to decrypt sops data key with OCI KMS key", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockHTTP := newMockHTTPClient() + + if tt.mockError != nil { + mockHTTP.addError(tt.mockError) + } else { + mockHTTP.addResponse(tt.mockStatusCode, tt.mockResponse) + } + + key := createTestMasterKey(testOCID, mockHTTP) + key.EncryptedKey = tt.encryptedKey + + plaintext, err := key.DecryptContext(context.Background()) + + if tt.expectError { + assert.Error(t, err) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedPlain, plaintext) + } + }) + } +} + +func TestHTTPClientInjection(t *testing.T) { + mockHTTP := newMockHTTPClient() + mockHTTP.addResponse(200, createEncryptResponse("ENCRYPTED")) + + key := NewMasterKeyFromOCID(testOCID) + + // Inject config provider (required for client creation) + configProvider := NewConfigurationProvider(newMockConfigProvider()) + configProvider.ApplyToMasterKey(key) + + // Inject HTTP client + httpClient := NewHTTPClient(mockHTTP) + httpClient.ApplyToMasterKey(key) + + // Perform encryption + err := key.EncryptContext(context.Background(), []byte("test")) + require.NoError(t, err) + + // Verify our mock client was used + assert.Equal(t, 1, len(mockHTTP.requests), "should have used injected HTTP client") +} + +func TestEncryptDecryptRoundTrip(t *testing.T) { + dataKey := []byte("this-is-a-32-byte-test-key-12345") + dataKeyBase64 := base64.StdEncoding.EncodeToString(dataKey) + ciphertext := "MOCK_ENCRYPTED_CIPHERTEXT_BASE64" + + mockHTTP := newMockHTTPClient() + + // Mock encrypt response + mockHTTP.addResponse(200, createEncryptResponse(ciphertext)) + // Mock decrypt response + mockHTTP.addResponse(200, createDecryptResponse(dataKeyBase64)) + + key := createTestMasterKey(testOCID, mockHTTP) + + // Encrypt + err := key.EncryptContext(context.Background(), dataKey) + require.NoError(t, err) + assert.Equal(t, ciphertext, key.EncryptedKey) + + // Decrypt + decrypted, err := key.DecryptContext(context.Background()) + require.NoError(t, err) + assert.Equal(t, dataKey, decrypted) + + // Verify two requests were made + assert.Equal(t, 2, len(mockHTTP.requests)) +} + +func TestContextCancellation(t *testing.T) { + mockHTTP := newMockHTTPClient() + mockHTTP.addResponse(200, createEncryptResponse("ENCRYPTED")) + + key := createTestMasterKey(testOCID, mockHTTP) + + // Create a context that's already cancelled + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + // Attempt encryption with cancelled context + err := key.EncryptContext(ctx, []byte("test")) + + // Should fail due to context cancellation + // Note: actual behavior depends on when OCI SDK checks context + // This test documents the expected behavior + _ = err // May or may not error depending on when context is checked +} + +func TestEncryptIfNeeded(t *testing.T) { + dataKey := []byte("test-data-key-32-bytes-long-1234") + + t.Run("encrypts when EncryptedKey is empty", func(t *testing.T) { + mockHTTP := newMockHTTPClient() + mockHTTP.addResponse(200, createEncryptResponse("ENCRYPTED")) + + key := createTestMasterKey(testOCID, mockHTTP) + key.EncryptedKey = "" // Explicitly empty + + err := key.EncryptIfNeeded(dataKey) + require.NoError(t, err) + assert.Equal(t, "ENCRYPTED", key.EncryptedKey) + assert.Equal(t, 1, len(mockHTTP.requests)) + }) + + t.Run("skips encryption when EncryptedKey exists", func(t *testing.T) { + mockHTTP := newMockHTTPClient() + // Don't add any responses - should not be called + + key := createTestMasterKey(testOCID, mockHTTP) + key.EncryptedKey = "ALREADY_ENCRYPTED" + + err := key.EncryptIfNeeded(dataKey) + require.NoError(t, err) + assert.Equal(t, "ALREADY_ENCRYPTED", key.EncryptedKey) + assert.Equal(t, 0, len(mockHTTP.requests), "should not make HTTP request") + }) +} + +func TestNeedsRotation(t *testing.T) { + t.Run("new key does not need rotation", func(t *testing.T) { + key := NewMasterKeyFromOCID(testOCID) + assert.False(t, key.NeedsRotation()) + }) + + t.Run("old key needs rotation", func(t *testing.T) { + key := NewMasterKeyFromOCID(testOCID) + // Set creation date to 7 months ago (> 6 months) + key.CreationDate = time.Now().UTC().Add(-7 * 30 * 24 * time.Hour) + assert.True(t, key.NeedsRotation()) + }) + + t.Run("6-month-old key does not need rotation", func(t *testing.T) { + key := NewMasterKeyFromOCID(testOCID) + // Set creation date to just under 6 months ago + key.CreationDate = time.Now().UTC().Add(-6*30*24*time.Hour + time.Hour) + // Should not need rotation (> is used, not >=) + assert.False(t, key.NeedsRotation()) + }) +} + +func TestToString(t *testing.T) { + key := NewMasterKeyFromOCID(testOCID) + assert.Equal(t, testOCID, key.ToString()) +} + +func TestTypeToIdentifier(t *testing.T) { + key := NewMasterKeyFromOCID(testOCID) + assert.Equal(t, KeyTypeIdentifier, key.TypeToIdentifier()) + assert.Equal(t, "oci_kms", key.TypeToIdentifier()) +} + +func TestExtractRefs(t *testing.T) { + tests := []struct { + name string + ocid string + expectError bool + expectedRegion string + expectedVault string + }{ + { + name: "valid OCID", + ocid: "ocid1.key.oc1.uk-london-1.aaaalgz5aacmg.aaaailjtjbkbc5ufsorrihgv2agugpfe7wrtngukihgkybqxcoozz7sbh6lq", + expectError: false, + expectedRegion: "uk-london-1", + expectedVault: "aaaalgz5aacmg", + }, + { + name: "valid OCID 2", + ocid: "ocid1.vault.oc1.iad.asdadsasdagz5aacmg.abwgiljtjasdasdasdagugpfe7wrtngukihgkybqxcoozz7sbh6lq", + expectError: false, + expectedRegion: "iad", + expectedVault: "asdadsasdagz5aacmg", + }, + { + name: "invalid OCID - too few parts", + ocid: "ocid1.key.oc1", + expectError: true, + }, + { + name: "invalid OCID - too many parts", + ocid: "ocid1.key.oc1.region.vault.extra.extra", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + key := NewMasterKeyFromOCID(tt.ocid) + region, vault, err := extractRefs(key) + + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedRegion, region) + assert.Equal(t, tt.expectedVault, vault) + } + }) + } +} diff --git a/ocikms/keysource_test.go b/ocikms/keysource_test.go new file mode 100644 index 0000000000..e3efe1c629 --- /dev/null +++ b/ocikms/keysource_test.go @@ -0,0 +1,36 @@ +package ocikms + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestMasterKeysFromOCIDString(t *testing.T) { + s := "ocid1.key.oc1.uk-london-1.aaaalgz5aacmg.aaaailjtjbkbc5ufsorrihgv2agugpfe7wrtngukihgkybqxcoozz7sbh6lq, ocid1.key.oc1.uk-london-1.bbbblgz5aacmg.bbbbiljtjbkbc5ufsorrihgv2agugpfe7wrtngukihgkybqxcoozz7sbh6lq" + ks := MasterKeysFromOCIDString(s) + k1 := ks[0] + k2 := ks[1] + expectedOcid1 := "ocid1.key.oc1.uk-london-1.aaaalgz5aacmg.aaaailjtjbkbc5ufsorrihgv2agugpfe7wrtngukihgkybqxcoozz7sbh6lq" + expectedOcid2 := "ocid1.key.oc1.uk-london-1.bbbblgz5aacmg.bbbbiljtjbkbc5ufsorrihgv2agugpfe7wrtngukihgkybqxcoozz7sbh6lq" + if k1.Ocid != expectedOcid1 { + t.Errorf("Ocid mismatch. Expected %s, found %s", expectedOcid1, k1.Ocid) + } + if k2.Ocid != expectedOcid2 { + t.Errorf("Ocid mismatch. Expected %s, found %s", expectedOcid2, k2.Ocid) + } +} + +func TestKeyToMap(t *testing.T) { + key := MasterKey{ + CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC), + Ocid: "foo", + EncryptedKey: "this is encrypted", + } + assert.Equal(t, map[string]interface{}{ + "ocid": "foo", + "enc": "this is encrypted", + "created_at": "2016-10-31T10:00:00Z", + }, key.ToMap()) +} diff --git a/stores/stores.go b/stores/stores.go index b1b496dc76..09c284689b 100644 --- a/stores/stores.go +++ b/stores/stores.go @@ -22,6 +22,7 @@ import ( "github.com/getsops/sops/v3/hckms" "github.com/getsops/sops/v3/hcvault" "github.com/getsops/sops/v3/kms" + "github.com/getsops/sops/v3/ocikms" "github.com/getsops/sops/v3/pgp" ) @@ -43,6 +44,7 @@ type metadata struct { AzureKeyVaultKeys []azkvkey `mapstructure:"azure_kv,omitempty,deep"` VaultKeys []vaultkey `mapstructure:"hc_vault,omitempty,deep"` AgeKeys []agekey `mapstructure:"age,omitempty,deep"` + OCIKMSKeys []ocikmskey `mapstructure:"oci_kms,omitempty,deep"` LastModified string `mapstructure:"lastmodified"` MessageAuthenticationCode string `mapstructure:"mac"` PGPKeys []pgpkey `mapstructure:"pgp,omitempty,deep"` @@ -64,6 +66,7 @@ type keygroup struct { AzureKeyVaultKeys []azkvkey `mapstructure:"azure_kv,omitempty,deep"` VaultKeys []vaultkey `mapstructure:"hc_vault,deep"` AgeKeys []agekey `mapstructure:"age,deep"` + OCIKMSKeys []ocikmskey `mapstructure:"oci_kms,omitempty,deep"` } type pgpkey struct { @@ -114,6 +117,12 @@ type hckmskey struct { EncryptedDataKey string `mapstructure:"enc"` } +type ocikmskey struct { + Ocid string `mapstructure:"ocid"` + CreatedAt string `mapstructure:"created_at"` + EncryptedDataKey string `mapstructure:"enc"` +} + // metadataFromInternal converts an internal SOPS metadata representation to a // representation appropriate for storage. func metadataFromInternal(sopsMetadata sops.Metadata) metadata { @@ -138,6 +147,7 @@ func metadataFromInternal(sopsMetadata sops.Metadata) metadata { m.VaultKeys = vaultKeysFromGroup(group) m.AzureKeyVaultKeys = azkvKeysFromGroup(group) m.AgeKeys = ageKeysFromGroup(group) + m.OCIKMSKeys = ocikmsKeysFromGroup(group) } else { for _, group := range sopsMetadata.KeyGroups { m.KeyGroups = append(m.KeyGroups, keygroup{ @@ -148,6 +158,7 @@ func metadataFromInternal(sopsMetadata sops.Metadata) metadata { VaultKeys: vaultKeysFromGroup(group), AzureKeyVaultKeys: azkvKeysFromGroup(group), AgeKeys: ageKeysFromGroup(group), + OCIKMSKeys: ocikmsKeysFromGroup(group), }) } } @@ -258,6 +269,20 @@ func hckmsKeysFromGroup(group sops.KeyGroup) (keys []hckmskey) { return } +func ocikmsKeysFromGroup(group sops.KeyGroup) (keys []ocikmskey) { + for _, key := range group { + switch key := key.(type) { + case *ocikms.MasterKey: + keys = append(keys, ocikmskey{ + Ocid: key.Ocid, + CreatedAt: key.CreationDate.Format(time.RFC3339), + EncryptedDataKey: key.EncryptedKey, + }) + } + } + return +} + // ToInternal converts a storage-appropriate Metadata struct to a SOPS internal representation func (m *metadata) ToInternal() (sops.Metadata, error) { lastModified, err := time.Parse(time.RFC3339, m.LastModified) @@ -312,7 +337,7 @@ func (m *metadata) ToInternal() (sops.Metadata, error) { }, nil } -func internalGroupFrom(kmsKeys []kmskey, pgpKeys []pgpkey, gcpKmsKeys []gcpkmskey, hckmsKeys []hckmskey, azkvKeys []azkvkey, vaultKeys []vaultkey, ageKeys []agekey) (sops.KeyGroup, error) { +func internalGroupFrom(kmsKeys []kmskey, pgpKeys []pgpkey, gcpKmsKeys []gcpkmskey, hckmsKeys []hckmskey, azkvKeys []azkvkey, vaultKeys []vaultkey, ageKeys []agekey, ociKmsKeys []ocikmskey) (sops.KeyGroup, error) { var internalGroup sops.KeyGroup for _, kmsKey := range kmsKeys { k, err := kmsKey.toInternal() @@ -363,13 +388,20 @@ func internalGroupFrom(kmsKeys []kmskey, pgpKeys []pgpkey, gcpKmsKeys []gcpkmske } internalGroup = append(internalGroup, k) } + for _, ociKmsKey := range ociKmsKeys { + k, err := ociKmsKey.toInternal() + if err != nil { + return nil, err + } + internalGroup = append(internalGroup, k) + } return internalGroup, nil } func (m *metadata) internalKeygroups() ([]sops.KeyGroup, error) { var internalGroups []sops.KeyGroup - if len(m.PGPKeys) > 0 || len(m.KMSKeys) > 0 || len(m.GCPKMSKeys) > 0 || len(m.HCKmsKeys) > 0 || len(m.AzureKeyVaultKeys) > 0 || len(m.VaultKeys) > 0 || len(m.AgeKeys) > 0 { - internalGroup, err := internalGroupFrom(m.KMSKeys, m.PGPKeys, m.GCPKMSKeys, m.HCKmsKeys, m.AzureKeyVaultKeys, m.VaultKeys, m.AgeKeys) + if len(m.PGPKeys) > 0 || len(m.KMSKeys) > 0 || len(m.GCPKMSKeys) > 0 || len(m.HCKmsKeys) > 0 || len(m.AzureKeyVaultKeys) > 0 || len(m.VaultKeys) > 0 || len(m.AgeKeys) > 0 || len(m.OCIKMSKeys) > 0 { + internalGroup, err := internalGroupFrom(m.KMSKeys, m.PGPKeys, m.GCPKMSKeys, m.HCKmsKeys, m.AzureKeyVaultKeys, m.VaultKeys, m.AgeKeys, m.OCIKMSKeys) if err != nil { return nil, err } @@ -377,7 +409,7 @@ func (m *metadata) internalKeygroups() ([]sops.KeyGroup, error) { return internalGroups, nil } else if len(m.KeyGroups) > 0 { for _, group := range m.KeyGroups { - internalGroup, err := internalGroupFrom(group.KMSKeys, group.PGPKeys, group.GCPKMSKeys, group.HCKmsKeys, group.AzureKeyVaultKeys, group.VaultKeys, group.AgeKeys) + internalGroup, err := internalGroupFrom(group.KMSKeys, group.PGPKeys, group.GCPKMSKeys, group.HCKmsKeys, group.AzureKeyVaultKeys, group.VaultKeys, group.AgeKeys, group.OCIKMSKeys) if err != nil { return nil, err } @@ -477,6 +509,18 @@ func (hckmsKey *hckmskey) toInternal() (*hckms.MasterKey, error) { return key, nil } +func (ociKmsKey *ocikmskey) toInternal() (*ocikms.MasterKey, error) { + creationDate, err := time.Parse(time.RFC3339, ociKmsKey.CreatedAt) + if err != nil { + return nil, err + } + return &ocikms.MasterKey{ + Ocid: ociKmsKey.Ocid, + EncryptedKey: ociKmsKey.EncryptedDataKey, + CreationDate: creationDate, + }, nil +} + // ExampleComplexTree is an example sops.Tree object exhibiting complex relationships var ExampleComplexTree = sops.Tree{ Branches: sops.TreeBranches{