diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml
index d5920e452..64f8f74f3 100644
--- a/.github/workflows/checks.yaml
+++ b/.github/workflows/checks.yaml
@@ -169,7 +169,7 @@ jobs:
if: matrix.crypto == 'standard'
- name: Added Trusted Certs
run: |
- sudo chmod -R 777 ./keys
+ sudo chmod -R 777 ./keys
sudo apt-get install -y ca-certificates
sudo cp ./keys/localhost.crt /usr/local/share/ca-certificates
sudo update-ca-certificates
diff --git a/docs/grpc/index.html b/docs/grpc/index.html
index 546007c21..582fb2cc7 100644
--- a/docs/grpc/index.html
+++ b/docs/grpc/index.html
@@ -2865,6 +2865,13 @@
PublicKeyResponse
|
+
+ kid |
+ string |
+ |
+ |
+
+
diff --git a/docs/openapi/kas/kas.swagger.json b/docs/openapi/kas/kas.swagger.json
index d5fb0b060..986420072 100644
--- a/docs/openapi/kas/kas.swagger.json
+++ b/docs/openapi/kas/kas.swagger.json
@@ -94,20 +94,21 @@
"parameters": [
{
"name": "algorithm",
+ "description": "algorithm type rsa:\u003ckeysize\u003e or ec:\u003ccurvename\u003e",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "fmt",
- "description": "version",
+ "description": "response format",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "v",
- "description": "version",
+ "description": "request version",
"in": "query",
"required": false,
"type": "string"
@@ -166,6 +167,9 @@
"properties": {
"publicKey": {
"type": "string"
+ },
+ "kid": {
+ "type": "string"
}
}
},
diff --git a/examples/cmd/encrypt.go b/examples/cmd/encrypt.go
index d86c562d1..9662c6149 100644
--- a/examples/cmd/encrypt.go
+++ b/examples/cmd/encrypt.go
@@ -65,10 +65,11 @@ func encrypt(cmd *cobra.Command, args []string) error {
}
}()
- if !nanoFormat {
+ attributes := strings.Split(joinedDataAttributes, " ")
+ if !nanoFormat {
tdf, err := client.CreateTDF(out, in,
- sdk.WithDataAttributes(strings.Split(joinedDataAttributes, " ")...),
+ sdk.WithDataAttributes(attributes...),
sdk.WithKasInformation(
sdk.KASInfo{
// examples assume insecure http
@@ -83,19 +84,12 @@ func encrypt(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
-
- // Print Manifest
cmd.Println(string(manifestJSON))
} else {
- attributes := []string{
- "https://example.com/attr/attr1/value/value1",
- }
-
nanoTDFConfig, err := client.NewNanoTDFConfig()
if err != nil {
return err
}
-
nanoTDFConfig.SetAttributes(attributes)
nanoTDFConfig.EnableECDSAPolicyBinding()
err = nanoTDFConfig.SetKasURL(fmt.Sprintf("http://%s/kas", platformEndpoint))
diff --git a/go.work.sum b/go.work.sum
index 797f0adcb..fff4b4146 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -1784,6 +1784,7 @@ golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.16.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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1846,6 +1847,7 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1879,6 +1881,7 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1987,6 +1990,7 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.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/telemetry v0.0.0-20240208230135-b75ee8823808 h1:+Kc94D8UVEVxJnLXp/+FMfqQARZtWHfVrcRtcG8aT3g=
golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
@@ -2090,6 +2094,7 @@ golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
+golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
diff --git a/opentdf-dev.yaml b/opentdf-dev.yaml
index 1ae7905cf..387959f97 100644
--- a/opentdf-dev.yaml
+++ b/opentdf-dev.yaml
@@ -11,6 +11,8 @@ logger:
services:
kas:
enabled: true
+ eccertid: e1
+ rsacertid: r1
policy:
enabled: true
authorization:
@@ -98,17 +100,15 @@ server:
cryptoProvider:
type: standard
standard:
- rsa:
- 123:
- private_key_path: kas-private.pem
- public_key_path: kas-cert.pem
- 456:
- private_key_path: kas-private.pem
- public_key_path: kas-cert.pem
- ec:
- 123:
- private_key_path: kas-ec-private.pem
- public_key_path: kas-ec-cert.pem
+ keys:
+ - kid: r1
+ alg: rsa:2048
+ private: kas-private.pem
+ cert: kas-cert.pem
+ - kid: e1
+ alg: ec:secp256r1
+ private: kas-ec-private.pem
+ cert: kas-ec-cert.pem
port: 8080
opa:
embedded: true # Only for local development
diff --git a/opentdf-example.yaml b/opentdf-example.yaml
index 4d5b79a3c..b639fc509 100644
--- a/opentdf-example.yaml
+++ b/opentdf-example.yaml
@@ -11,6 +11,8 @@ db:
services:
kas:
enabled: true
+ eccertid: e1
+ rsacertid: r1
policy:
enabled: true
entityresolution:
@@ -97,17 +99,15 @@ server:
cryptoProvider:
type: standard
standard:
- rsa:
- 123:
- private_key_path: /keys/kas-private.pem
- public_key_path: /keys/kas-cert.pem
- 456:
- private_key_path: /keys/kas-private.pem
- public_key_path: /keys/kas-cert.pem
- ec:
- 123:
- private_key_path: /keys/kas-ec-private.pem
- public_key_path: /keys/kas-ec-cert.pem
+ keys:
+ - kid: r1
+ alg: rsa:2048
+ private: /keys/kas-private.pem
+ cert: /keys/kas-cert.pem
+ - kid: e1
+ alg: ec:secp256r1
+ private: /keys/kas-ec-private.pem
+ cert: /keys/kas-ec-cert.pem
port: 8080
opa:
embedded: true # Only for local development
diff --git a/opentdf-with-hsm.yaml b/opentdf-with-hsm.yaml
index 9d7774a3d..a6181aa02 100644
--- a/opentdf-with-hsm.yaml
+++ b/opentdf-with-hsm.yaml
@@ -11,6 +11,8 @@ logger:
services:
kas:
enabled: true
+ eccertid: e1
+ rsacertid: r1
policy:
enabled: true
entityresolution:
@@ -106,10 +108,12 @@ server:
pin: "12345"
slotlabel: "dev-token"
keys:
- rsa:
- label: development-rsa-kas
- ec:
- label: development-ec-kas
+ - kid: r1
+ alg: rsa:2048
+ private: development-rsa-kas
+ - kid: e1
+ alg: ec:secp256r1
+ private: development-ec-kas
port: 8080
opa:
embedded: true # Only for local development
diff --git a/protocol/go/kas/kas.pb.go b/protocol/go/kas/kas.pb.go
index b613b76f4..b7c592485 100644
--- a/protocol/go/kas/kas.pb.go
+++ b/protocol/go/kas/kas.pb.go
@@ -226,6 +226,7 @@ type PublicKeyResponse struct {
unknownFields protoimpl.UnknownFields
PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
+ Kid string `protobuf:"bytes,2,opt,name=kid,proto3" json:"kid,omitempty"`
}
func (x *PublicKeyResponse) Reset() {
@@ -267,6 +268,13 @@ func (x *PublicKeyResponse) GetPublicKey() string {
return ""
}
+func (x *PublicKeyResponse) GetKid() string {
+ if x != nil {
+ return x.Kid
+ }
+ return ""
+}
+
type RewrapRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -405,80 +413,86 @@ var file_kas_kas_proto_rawDesc = []byte{
0x61, 0x63, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
- 0x6d, 0x22, 0x6c, 0x0a, 0x10, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74,
- 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69,
- 0x74, 0x68, 0x6d, 0x12, 0x1e, 0x0a, 0x03, 0x66, 0x6d, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
- 0x42, 0x0c, 0x92, 0x41, 0x09, 0x32, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x03,
- 0x66, 0x6d, 0x74, 0x12, 0x1a, 0x0a, 0x01, 0x76, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0c,
- 0x92, 0x41, 0x09, 0x32, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x01, 0x76, 0x22,
- 0x32, 0x0a, 0x11, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b,
- 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
- 0x4b, 0x65, 0x79, 0x22, 0x4f, 0x0a, 0x0d, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x06, 0x62, 0x65,
- 0x61, 0x72, 0x65, 0x72, 0x22, 0xa7, 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64,
- 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6b, 0x61, 0x73, 0x2e,
- 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d,
- 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65,
- 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2c, 0x0a, 0x12, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
- 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x0c, 0x52, 0x10, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65,
- 0x64, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
- 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x10, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
- 0x65, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x76, 0x65, 0x72,
- 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x63, 0x68, 0x65,
- 0x6d, 0x61, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x53, 0x0a, 0x0d, 0x4d, 0x65, 0x74,
- 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
- 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05,
- 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f,
- 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61,
- 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x8f,
- 0x03, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
- 0x12, 0x45, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x49,
- 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6b, 0x61, 0x73,
- 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x92,
- 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02,
- 0x06, 0x12, 0x04, 0x2f, 0x6b, 0x61, 0x73, 0x12, 0x66, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69,
- 0x63, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69,
- 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6b, 0x61,
- 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12,
- 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x76, 0x32,
- 0x2f, 0x6b, 0x61, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x12,
- 0x75, 0x0a, 0x0f, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
- 0x65, 0x79, 0x12, 0x1b, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50,
- 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
- 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x27, 0x92,
- 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02,
- 0x15, 0x12, 0x13, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x6b, 0x61, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c,
- 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x58, 0x0a, 0x06, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70,
- 0x12, 0x12, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61,
- 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x92, 0x41, 0x09, 0x4a, 0x07,
- 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a,
- 0x22, 0x0e, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x72, 0x65, 0x77, 0x72, 0x61, 0x70,
- 0x42, 0xe2, 0x01, 0x92, 0x41, 0x73, 0x12, 0x71, 0x0a, 0x1a, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x44,
- 0x46, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x53, 0x65, 0x72,
- 0x76, 0x69, 0x63, 0x65, 0x2a, 0x4c, 0x0a, 0x12, 0x42, 0x53, 0x44, 0x20, 0x33, 0x2d, 0x43, 0x6c,
- 0x61, 0x75, 0x73, 0x65, 0x20, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x12, 0x36, 0x68, 0x74, 0x74, 0x70,
- 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f,
- 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x62,
- 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e,
- 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x6b,
- 0x61, 0x73, 0x42, 0x08, 0x4b, 0x61, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b,
- 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74,
- 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74,
- 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x6b, 0x61, 0x73, 0xa2, 0x02, 0x03, 0x4b, 0x58,
- 0x58, 0xaa, 0x02, 0x03, 0x4b, 0x61, 0x73, 0xca, 0x02, 0x03, 0x4b, 0x61, 0x73, 0xe2, 0x02, 0x0f,
- 0x4b, 0x61, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea,
- 0x02, 0x03, 0x4b, 0x61, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x6d, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x51, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69,
+ 0x74, 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x33, 0x92, 0x41, 0x30, 0x32, 0x2e,
+ 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72,
+ 0x73, 0x61, 0x3a, 0x3c, 0x6b, 0x65, 0x79, 0x73, 0x69, 0x7a, 0x65, 0x3e, 0x20, 0x6f, 0x72, 0x20,
+ 0x65, 0x63, 0x3a, 0x3c, 0x63, 0x75, 0x72, 0x76, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x3e, 0x52, 0x09,
+ 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x26, 0x0a, 0x03, 0x66, 0x6d, 0x74,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x14, 0x92, 0x41, 0x11, 0x32, 0x0f, 0x72, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x03, 0x66, 0x6d,
+ 0x74, 0x12, 0x22, 0x0a, 0x01, 0x76, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x14, 0x92, 0x41,
+ 0x11, 0x32, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x52, 0x01, 0x76, 0x22, 0x44, 0x0a, 0x11, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
+ 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75,
+ 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
+ 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x69, 0x64,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x69, 0x64, 0x22, 0x4f, 0x0a, 0x0d, 0x52,
+ 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x14,
+ 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74,
+ 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e,
+ 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4a, 0x04,
+ 0x08, 0x02, 0x10, 0x03, 0x52, 0x06, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x22, 0xa7, 0x02, 0x0a,
+ 0x0e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+ 0x3d, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28,
+ 0x0b, 0x32, 0x21, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45,
+ 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2c,
+ 0x0a, 0x12, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64,
+ 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x65, 0x6e, 0x74, 0x69,
+ 0x74, 0x79, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x12,
+ 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b,
+ 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x63,
+ 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x0d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x1a, 0x53, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74,
+ 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x8f, 0x03, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x65, 0x73,
+ 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f,
+ 0x12, 0x10, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30,
+ 0x30, 0x12, 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x06, 0x12, 0x04, 0x2f, 0x6b, 0x61, 0x73, 0x12,
+ 0x66, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x2e, 0x6b,
+ 0x61, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
+ 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x92, 0x41, 0x09,
+ 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12,
+ 0x16, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x6b, 0x61, 0x73, 0x5f, 0x70, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x75, 0x0a, 0x0f, 0x4c, 0x65, 0x67, 0x61, 0x63,
+ 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x2e, 0x6b, 0x61, 0x73,
+ 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x27, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30,
+ 0x30, 0x12, 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x6b, 0x61, 0x73, 0x2f,
+ 0x6b, 0x61, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x58,
+ 0x0a, 0x06, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x12, 0x12, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x52,
+ 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6b,
+ 0x61, 0x73, 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x22, 0x25, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82,
+ 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x76,
+ 0x32, 0x2f, 0x72, 0x65, 0x77, 0x72, 0x61, 0x70, 0x42, 0xe2, 0x01, 0x92, 0x41, 0x73, 0x12, 0x71,
+ 0x0a, 0x1a, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x44, 0x46, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x41, 0x63,
+ 0x63, 0x65, 0x73, 0x73, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2a, 0x4c, 0x0a, 0x12,
+ 0x42, 0x53, 0x44, 0x20, 0x33, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20, 0x43, 0x6c, 0x65,
+ 0x61, 0x72, 0x12, 0x36, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68,
+ 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x62,
+ 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74,
+ 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x35, 0x2e,
+ 0x30, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x6b, 0x61, 0x73, 0x42, 0x08, 0x4b, 0x61, 0x73, 0x50,
+ 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66,
+ 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f,
+ 0x6b, 0x61, 0x73, 0xa2, 0x02, 0x03, 0x4b, 0x58, 0x58, 0xaa, 0x02, 0x03, 0x4b, 0x61, 0x73, 0xca,
+ 0x02, 0x03, 0x4b, 0x61, 0x73, 0xe2, 0x02, 0x0f, 0x4b, 0x61, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d,
+ 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x4b, 0x61, 0x73, 0x62, 0x06, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/sdk/kas_client.go b/sdk/kas_client.go
index 8247190bd..81e23bded 100644
--- a/sdk/kas_client.go
+++ b/sdk/kas_client.go
@@ -287,26 +287,40 @@ func (k *KASClient) getRewrapRequest(keyAccess KeyAccess, policy string) (*kas.R
return &rewrapRequest, nil
}
-func getPublicKey(kasInfo KASInfo, opts ...grpc.DialOption) (string, error) {
- req := kas.PublicKeyRequest{}
+type publicKeyWithID struct {
+ publicKey, kid string
+}
+
+func (s SDK) getPublicKey(kasInfo KASInfo) (*publicKeyWithID, error) {
grpcAddress, err := getGRPCAddress(kasInfo.URL)
if err != nil {
- return "", err
+ return nil, err
}
- conn, err := grpc.Dial(grpcAddress, opts...)
+ conn, err := grpc.Dial(grpcAddress, s.dialOptions...)
if err != nil {
- return "", fmt.Errorf("error connecting to grpc service at %s: %w", kasInfo.URL, err)
+ return nil, fmt.Errorf("error connecting to grpc service at %s: %w", kasInfo.URL, err)
}
defer conn.Close()
ctx := context.Background()
serviceClient := kas.NewAccessServiceClient(conn)
+ req := kas.PublicKeyRequest{
+ Algorithm: "rsa:2048",
+ }
+ if s.config.tdfFeatures.noKID {
+ req.V = "1"
+ }
resp, err := serviceClient.PublicKey(ctx, &req)
if err != nil {
- return "", fmt.Errorf("error making request to KAS: %w", err)
+ return nil, fmt.Errorf("error making request to KAS: %w", err)
+ }
+
+ kid := resp.GetKid()
+ if s.config.tdfFeatures.noKID {
+ kid = ""
}
- return resp.GetPublicKey(), nil
+ return &publicKeyWithID{resp.GetPublicKey(), kid}, nil
}
diff --git a/sdk/manifest.go b/sdk/manifest.go
index 869cba1f5..1d40d2c32 100644
--- a/sdk/manifest.go
+++ b/sdk/manifest.go
@@ -26,6 +26,7 @@ type KeyAccess struct {
WrappedKey string `json:"wrappedKey"`
PolicyBinding string `json:"policyBinding"`
EncryptedMetadata string `json:"encryptedMetadata,omitempty"`
+ KID string `json:"kid,omitempty"`
}
type Method struct {
diff --git a/sdk/options.go b/sdk/options.go
index 242bd1163..3b8958df7 100644
--- a/sdk/options.go
+++ b/sdk/options.go
@@ -31,6 +31,13 @@ type config struct {
kasSessionKey *ocrypto.RsaKeyPair
dpopKey *ocrypto.RsaKeyPair
ipc bool
+ tdfFeatures tdfFeatures
+}
+
+// Options specific to TDF protocol features
+type tdfFeatures struct {
+ // For backward compatibility, don't store the KID in the KAO.
+ noKID bool
}
type PlatformConfiguration map[string]interface{}
@@ -160,3 +167,11 @@ func WithIPC() Option {
c.ipc = true
}
}
+
+// WithNoKIDInKAO disables storing the KID in the KAO. This allows generating
+// TDF files that are compatible with legacy file formats (no KID).
+func WithNoKIDInKAO() Option {
+ return func(c *config) {
+ c.tdfFeatures.noKID = true
+ }
+}
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 2efeec565..410f63e1a 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -41,6 +41,7 @@ func (c Error) Error() string {
}
type SDK struct {
+ config
conn *grpc.ClientConn
dialOptions []grpc.DialOption
kasSessionKey ocrypto.RsaKeyPair
@@ -138,6 +139,7 @@ func New(platformEndpoint string, opts ...Option) (*SDK, error) {
}
return &SDK{
+ config: *cfg,
conn: defaultConn,
dialOptions: dialOptions,
tokenSource: accessTokenSource,
diff --git a/sdk/tdf.go b/sdk/tdf.go
index 5e630580e..4aa13cee5 100644
--- a/sdk/tdf.go
+++ b/sdk/tdf.go
@@ -101,7 +101,7 @@ func (s SDK) CreateTDF(writer io.Writer, reader io.ReadSeeker, opts ...TDFOption
}
// How do we want to handle different dial options for different KAS servers?
- err = fillInPublicKeys(tdfConfig.kasInfoList, s.dialOptions...)
+ err = s.fillInPublicKeys(tdfConfig.kasInfoList)
if err != nil {
return nil, err
}
@@ -275,6 +275,7 @@ func (t *TDFObject) prepareManifest(tdfConfig TDFConfig) error { //nolint:funlen
keyAccess := KeyAccess{}
keyAccess.KeyType = kWrapped
keyAccess.KasURL = kasInfo.URL
+ keyAccess.KID = kasInfo.KID
keyAccess.Protocol = kKasProtocol
// add policyBinding
@@ -741,18 +742,19 @@ func validateRootSignature(manifest Manifest, secret []byte) (bool, error) {
return false, nil
}
-func fillInPublicKeys(kasInfos []KASInfo, opts ...grpc.DialOption) error {
+func (s SDK) fillInPublicKeys(kasInfos []KASInfo) error {
for idx, kasInfo := range kasInfos {
if kasInfo.PublicKey != "" {
continue
}
- publicKey, err := getPublicKey(kasInfo, opts...)
+ publicKey, err := s.getPublicKey(kasInfo)
if err != nil {
return fmt.Errorf("unable to retrieve public key from KAS at [%s]: %w", kasInfo.URL, err)
}
- kasInfos[idx].PublicKey = publicKey
+ kasInfos[idx].PublicKey = publicKey.publicKey
+ kasInfos[idx].KID = publicKey.kid
}
return nil
}
diff --git a/sdk/tdf_config.go b/sdk/tdf_config.go
index ba6ca25d0..513a0527d 100644
--- a/sdk/tdf_config.go
+++ b/sdk/tdf_config.go
@@ -32,6 +32,8 @@ type KASInfo struct {
URL string
// Public key can be empty. If it is empty, the public key will be fetched from the KAS server.
PublicKey string
+ // Key identifier associated with the given key, if present.
+ KID string
}
type TDFOption func(*TDFConfig) error
diff --git a/sdk/tdf_test.go b/sdk/tdf_test.go
index 07ed15a31..d02ddbbed 100644
--- a/sdk/tdf_test.go
+++ b/sdk/tdf_test.go
@@ -146,10 +146,12 @@ var testHarnesses = []tdfTest{ //nolint:gochecknoglobals // requires for testing
{
URL: "http://localhost:65432/api/kas",
PublicKey: mockKasPublicKey,
+ KID: "a",
},
{
URL: "http://localhost:65432/api/kas",
PublicKey: mockKasPublicKey,
+ KID: "b",
},
},
},
@@ -161,10 +163,12 @@ var testHarnesses = []tdfTest{ //nolint:gochecknoglobals // requires for testing
{
URL: "http://localhost:65432/api/kas",
PublicKey: mockKasPublicKey,
+ KID: "a",
},
{
URL: "http://localhost:65432/api/kas",
PublicKey: mockKasPublicKey,
+ KID: "b",
},
},
},
diff --git a/service/go.mod b/service/go.mod
index 691a79b40..b1789801e 100644
--- a/service/go.mod
+++ b/service/go.mod
@@ -95,7 +95,7 @@ require (
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
- github.com/mitchellh/mapstructure v1.5.0 // indirect
+ github.com/mitchellh/mapstructure v1.5.0
github.com/moby/locker v1.0.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
diff --git a/service/internal/security/crypto_provider.go b/service/internal/security/crypto_provider.go
index 4d6459d42..9538d3d6a 100644
--- a/service/internal/security/crypto_provider.go
+++ b/service/internal/security/crypto_provider.go
@@ -5,14 +5,24 @@ import (
"crypto/elliptic"
)
+const (
+ // Key agreement along P-256
+ AlgorithmECP256R1 = "ec:secp256r1"
+ // Used for encryption with RSA of the KAO
+ AlgorithmRSA2048 = "rsa:2048"
+)
+
type CryptoProvider interface {
+ // Gets some KID associated with a given algorithm.
+ // Returns empty string if none are found.
+ FindKID(alg string) string
RSAPublicKey(keyID string) (string, error)
RSAPublicKeyAsJSON(keyID string) (string, error)
RSADecrypt(hash crypto.Hash, keyID string, keyLabel string, ciphertext []byte) ([]byte, error)
ECPublicKey(keyID string) (string, error)
ECCertificate(keyID string) (string, error)
- GenerateNanoTDFSymmetricKey(ephemeralPublicKeyBytes []byte, curve elliptic.Curve) ([]byte, error)
+ GenerateNanoTDFSymmetricKey(kasKID string, ephemeralPublicKeyBytes []byte, curve elliptic.Curve) ([]byte, error)
GenerateEphemeralKasKeys() (any, []byte, error)
GenerateNanoTDFSessionKey(privateKeyHandle any, ephemeralPublicKey []byte) ([]byte, error)
Close()
diff --git a/service/internal/security/hsm.go b/service/internal/security/hsm.go
index dac4e77a8..2e23f44db 100644
--- a/service/internal/security/hsm.go
+++ b/service/internal/security/hsm.go
@@ -33,6 +33,13 @@ type Config struct {
StandardConfig StandardConfig `yaml:"standard,omitempty" mapstructure:"standard"`
}
+func (h HSMSession) FindKID(alg string) string {
+ if kid, ok := h.kidByAlg[alg]; ok {
+ return kid
+ }
+ return ""
+}
+
func NewCryptoProvider(cfg Config) (CryptoProvider, error) {
switch cfg.Type {
case "hsm":
@@ -54,37 +61,24 @@ func NewCryptoProvider(cfg Config) (CryptoProvider, error) {
type HSMSession struct {
ctx pkcs11.Ctx
sh pkcs11.SessionHandle
- RSA *RSAKeyPair
- EC *ECKeyPair
+ // Default kid for each algorithm. Avoid; specify by service instead.
+ kidByAlg map[string]string
+ keysByKID map[string]LiveKeyPair
}
type HSMConfig struct {
- Enabled bool `yaml:"enabled"`
- ModulePath string `yaml:"modulePath,omitempty"`
- PIN string `yaml:"pin,omitempty"`
- SlotID uint `yaml:"slotId,omitempty"`
- SlotLabel string `yaml:"slotLabel,omitempty"`
- Keys map[string]KeyInfo `yaml:"keys,omitempty"`
-}
-
-type KeyInfo struct {
- Name string `yaml:"name,omitempty"`
- Label string `yaml:"label,omitempty"`
-}
-
-type PrivateKeyRSA pkcs11.ObjectHandle
-
-type PrivateKeyEC pkcs11.ObjectHandle
-
-type ECKeyPair struct {
- PrivateKey PrivateKeyEC
- *ecdsa.PublicKey
- *x509.Certificate
-}
-
-type RSAKeyPair struct {
- PrivateKey PrivateKeyRSA
- *rsa.PublicKey
+ Enabled bool `yaml:"enabled"`
+ ModulePath string `yaml:"modulePath,omitempty"`
+ PIN string `yaml:"pin,omitempty"`
+ SlotID uint `yaml:"slotId,omitempty"`
+ SlotLabel string `yaml:"slotLabel,omitempty"`
+ Keys []KeyPairInfo `yaml:"keys,omitempty"`
+}
+
+type LiveKeyPair struct {
+ KeyPairInfo
+ pkcs11.ObjectHandle
+ crypto.PublicKey
*x509.Certificate
}
@@ -310,28 +304,33 @@ func NewHSM(c *HSMConfig) (*HSMSession, error) {
return hs, err
}
-func (h *HSMSession) loadKeys(keys map[string]KeyInfo) error {
- for name, info := range keys {
- if info.Name == "" {
- info.Name = name
+func (h *HSMSession) loadKeys(keys []KeyPairInfo) error {
+ h.keysByKID = make(map[string]LiveKeyPair)
+ for _, info := range keys {
+ if _, ok := h.kidByAlg[info.Algorithm]; !ok {
+ h.kidByAlg[info.Algorithm] = info.KID
}
- switch info.Name {
- case "rsa":
+ switch info.Algorithm {
+ case AlgorithmRSA2048:
pair, err := h.LoadRSAKey(info)
if err != nil {
- slog.Error("pkcs11 error unable to load RSA key", "err", err)
+ slog.Error("pkcs11 error unable to load RSA key", "err", err, "label", info.Private, "kid", info.KID)
+ } else if _, ok := h.keysByKID[pair.KID]; ok {
+ slog.Error("unable to load key with duplicate key identifier", "err", err, "label", info.Private, "kid", info.KID)
} else {
- h.RSA = pair
+ h.keysByKID[pair.KID] = *pair
}
- case "ec":
+ case AlgorithmECP256R1:
pair, err := h.LoadECKey(info)
if err != nil {
- slog.Error("pkcs11 error unable to load EC key", "err", err)
+ slog.Error("pkcs11 error unable to load EC key", "err", err, "label", info.Private, "kid", info.KID)
+ } else if _, ok := h.keysByKID[pair.KID]; ok {
+ slog.Error("unable to load key with duplicate key identifier", "err", err, "label", info.Private, "kid", info.KID)
} else {
- h.EC = pair
+ h.keysByKID[pair.KID] = *pair
}
default:
- return fmt.Errorf("unrecognized key type [%s], %w", info.Name, ErrKeyConfig)
+ return fmt.Errorf("unrecognized key algorithm [%s], %w", info.Algorithm, ErrKeyConfig)
}
}
return nil
@@ -391,20 +390,19 @@ func (h *HSMSession) findKey(class uint, label string) (pkcs11.ObjectHandle, err
return handle, err
}
-func (h *HSMSession) LoadRSAKey(info KeyInfo) (*RSAKeyPair, error) {
- var pair RSAKeyPair
+func (h *HSMSession) LoadRSAKey(info KeyPairInfo) (*LiveKeyPair, error) {
+ pair := LiveKeyPair{KeyPairInfo: info}
slog.Debug("Finding RSA key to wrap.")
- keyHandle, err := h.findKey(pkcs11.CKO_PRIVATE_KEY, info.Label)
+ keyHandle, err := h.findKey(pkcs11.CKO_PRIVATE_KEY, info.Private)
if err != nil {
slog.Error("pkcs11 error finding key", "err", err)
return nil, errors.Join(ErrKeyConfig, err)
}
+ pair.ObjectHandle = keyHandle
- pair.PrivateKey = PrivateKeyRSA(keyHandle)
-
- slog.Debug("Finding RSA certificate", "rsaLabel", info.Label)
- certHandle, err := h.findKey(pkcs11.CKO_CERTIFICATE, info.Label)
+ slog.Debug("Finding RSA certificate", "label", info.Private)
+ certHandle, err := h.findKey(pkcs11.CKO_CERTIFICATE, info.Private)
certTemplate := []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_CERTIFICATE),
pkcs11.NewAttribute(pkcs11.CKA_CERTIFICATE_TYPE, pkcs11.CKC_X_509),
@@ -448,20 +446,20 @@ func (h *HSMSession) LoadRSAKey(info KeyInfo) (*RSAKeyPair, error) {
return &pair, nil
}
-func (h *HSMSession) LoadECKey(info KeyInfo) (*ECKeyPair, error) {
- var pair ECKeyPair
+func (h *HSMSession) LoadECKey(info KeyPairInfo) (*LiveKeyPair, error) {
+ pair := LiveKeyPair{KeyPairInfo: info}
- slog.Debug("Finding EC private key", "ecLabel", info.Label)
- keyHandleEC, err := h.findKey(pkcs11.CKO_PRIVATE_KEY, info.Label)
+ slog.Debug("Finding EC private key", "kid", info.KID, "alg", info.Algorithm, "label", info.Private)
+ keyHandleEC, err := h.findKey(pkcs11.CKO_PRIVATE_KEY, info.Private)
if err != nil {
slog.Error("pkcs11 error finding ec key", "err", err)
return nil, errors.Join(ErrKeyConfig, err)
}
- pair.PrivateKey = PrivateKeyEC(keyHandleEC)
+ pair.ObjectHandle = keyHandleEC
// EC Cert
- certECHandle, err := h.findKey(pkcs11.CKO_CERTIFICATE, info.Label)
+ certECHandle, err := h.findKey(pkcs11.CKO_CERTIFICATE, info.Private)
if err != nil {
slog.Error("public key EC cert error")
return nil, errors.Join(ErrKeyConfig, err)
@@ -532,7 +530,7 @@ func oaepForHash(hashFunction crypto.Hash, keyLabel string) (*pkcs11.OAEPParams,
[]byte(keyLabel)), nil
}
-func (h *HSMSession) GenerateNanoTDFSymmetricKey(ephemeralPublicKeyBytes []byte, _ elliptic.Curve) ([]byte, error) {
+func (h *HSMSession) GenerateNanoTDFSymmetricKey(kasKID string, ephemeralPublicKeyBytes []byte, _ elliptic.Curve) ([]byte, error) {
template := []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, false),
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_SECRET_KEY),
@@ -551,7 +549,12 @@ func (h *HSMSession) GenerateNanoTDFSymmetricKey(ephemeralPublicKeyBytes []byte,
pkcs11.NewMechanism(pkcs11.CKM_ECDH1_DERIVE, ¶ms),
}
- handle, err := h.ctx.DeriveKey(h.sh, mech, pkcs11.ObjectHandle(h.EC.PrivateKey), template)
+ ec, ok := h.keysByKID[kasKID]
+ if !ok {
+ return nil, ErrCertNotFound
+ }
+
+ handle, err := h.ctx.DeriveKey(h.sh, mech, ec.ObjectHandle, template)
if err != nil {
return nil, fmt.Errorf("failed to derive symmetric key: %w", err)
}
@@ -582,7 +585,7 @@ func (h *HSMSession) GenerateNanoTDFSessionKey(
privateKey any,
ephemeralPublicKey []byte,
) ([]byte, error) {
- privateKeyHandle, ok := privateKey.(PrivateKeyEC)
+ privateKeyHandle, ok := privateKey.(pkcs11.ObjectHandle)
if !ok {
return nil, ErrHSMUnexpected
}
@@ -664,18 +667,16 @@ func (h *HSMSession) GenerateEphemeralKasKeys() (any, []byte, error) {
}
publicKeyBytes := pubBytes[0].Value
- return PrivateKeyEC(prvHandle), publicKeyBytes, nil
+ return prvHandle, publicKeyBytes, nil
}
-func (h *HSMSession) RSAPublicKey(keyID string) (string, error) {
- // TODO: For now ignore the key id
- slog.Info("⚠️ Ignoring the", slog.String("key id", keyID))
-
- if h.RSA == nil {
+func (h *HSMSession) RSAPublicKey(kid string) (string, error) {
+ rsa, ok := h.keysByKID[kid]
+ if !ok || rsa.Algorithm != AlgorithmRSA2048 || rsa.PublicKey == nil {
return "", ErrCertNotFound
}
- pubkeyBytes, err := x509.MarshalPKIXPublicKey(h.RSA.PublicKey)
+ pubkeyBytes, err := x509.MarshalPKIXPublicKey(rsa.PublicKey)
if err != nil {
return "", errors.Join(ErrPublicKeyMarshal, err)
}
@@ -693,14 +694,12 @@ func (h *HSMSession) RSAPublicKey(keyID string) (string, error) {
return string(certPem), nil
}
-func (h *HSMSession) RSAPublicKeyAsJSON(keyID string) (string, error) {
- // TODO: For now ignore the key id
- slog.Info("⚠️ Ignoring the", slog.String("key id", keyID))
-
- if h.RSA == nil || h.RSA.PublicKey == nil {
+func (h *HSMSession) RSAPublicKeyAsJSON(kid string) (string, error) {
+ rsa, ok := h.keysByKID[kid]
+ if !ok || rsa.Algorithm != AlgorithmRSA2048 || rsa.PublicKey == nil {
return "", ErrCertNotFound
}
- rsaPublicKeyJwk, err := jwk.FromRaw(h.RSA.PublicKey)
+ rsaPublicKeyJwk, err := jwk.FromRaw(rsa.PublicKey)
if err != nil {
return "", fmt.Errorf("jwk.FromRaw: %w", err)
}
@@ -713,11 +712,12 @@ func (h *HSMSession) RSAPublicKeyAsJSON(keyID string) (string, error) {
return string(jsonPublicKey), nil
}
-func (h *HSMSession) ECPublicKey(string) (string, error) {
- if h.EC == nil || h.EC.PublicKey == nil {
+func (h *HSMSession) ECPublicKey(kid string) (string, error) {
+ ec, ok := h.keysByKID[kid]
+ if !ok || ec.Algorithm != AlgorithmECP256R1 || ec.PublicKey == nil {
return "", ErrCertNotFound
}
- pubkeyBytes, err := x509.MarshalPKIXPublicKey(h.EC.PublicKey)
+ pubkeyBytes, err := x509.MarshalPKIXPublicKey(ec.PublicKey)
if err != nil {
return "", errors.Join(ErrPublicKeyMarshal, err)
}
@@ -732,17 +732,18 @@ func (h *HSMSession) ECPublicKey(string) (string, error) {
return string(pubkeyPem), nil
}
-func (h *HSMSession) RSADecrypt(hash crypto.Hash, keyID string, keyLabel string, ciphertext []byte) ([]byte, error) {
- // TODO: For now ignore the key id
- slog.Info("⚠️ Ignoring the", slog.String("key id", keyID))
-
+func (h *HSMSession) RSADecrypt(hash crypto.Hash, kid string, keyLabel string, ciphertext []byte) ([]byte, error) {
oaepParams, err := oaepForHash(hash, keyLabel)
if err != nil {
return nil, errors.Join(ErrHSMDecrypt, err)
}
mech := pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_OAEP, oaepParams)
- err = h.ctx.DecryptInit(h.sh, []*pkcs11.Mechanism{mech}, pkcs11.ObjectHandle(h.RSA.PrivateKey))
+ rsa, ok := h.keysByKID[kid]
+ if !ok || rsa.Algorithm != AlgorithmRSA2048 {
+ return nil, ErrCertNotFound
+ }
+ err = h.ctx.DecryptInit(h.sh, []*pkcs11.Mechanism{mech}, rsa.ObjectHandle)
if err != nil {
return nil, errors.Join(ErrHSMDecrypt, err)
}
@@ -753,6 +754,10 @@ func (h *HSMSession) RSADecrypt(hash crypto.Hash, keyID string, keyLabel string,
return decrypt, nil
}
-func (h *HSMSession) ECCertificate(string) (string, error) {
- return "", nil
+func (h *HSMSession) ECCertificate(kid string) (string, error) {
+ k, ok := h.keysByKID[kid]
+ if !ok || k.Algorithm != AlgorithmECP256R1 || k.Certificate == nil {
+ return "", ErrCertNotFound
+ }
+ return "", errors.New("ec cert format unimplemented")
}
diff --git a/service/internal/security/hsm_test.go b/service/internal/security/hsm_test.go
index ecfd5b1dd..449d53a27 100644
--- a/service/internal/security/hsm_test.go
+++ b/service/internal/security/hsm_test.go
@@ -108,7 +108,7 @@ func TestDecryptOAEPUnsupportedRSAFailure(t *testing.T) {
sh: sessionHandle,
}
- decrypted, err := session.RSADecrypt(crypto.BLAKE2b_384, "unknown", "sample label", []byte("sample ciphertext"))
+ decrypted, err := session.RSADecrypt(crypto.BLAKE2b_384, "", "sample label", []byte("sample ciphertext"))
t.Log(err)
t.Log(decrypted)
diff --git a/service/internal/security/standard_crypto.go b/service/internal/security/standard_crypto.go
index 0ef0a8b93..7570cebc0 100644
--- a/service/internal/security/standard_crypto.go
+++ b/service/internal/security/standard_crypto.go
@@ -16,16 +16,33 @@ import (
"github.com/opentdf/platform/lib/ocrypto"
)
-// / Constants
const (
kNanoTDFMagicStringAndVersion = "L1L"
)
-var errStandardCryptoObjIsInvalid = errors.New("standard crypto object is invalid")
-
type StandardConfig struct {
+ Keys []KeyPairInfo `mapstructure:"keys"`
+ // Deprecated
RSAKeys map[string]StandardKeyInfo `yaml:"rsa,omitempty" mapstructure:"rsa"`
- ECKeys map[string]StandardKeyInfo `yaml:"ec,omitempty" mapstructure:"ec"`
+ // Deprecated
+ ECKeys map[string]StandardKeyInfo `yaml:"ec,omitempty" mapstructure:"ec"`
+}
+
+type KeyPairInfo struct {
+ // Valid algorithm. May be able to be derived from Private but it is better to just say it.
+ Algorithm string `mapstructure:"alg"`
+ // Key identifier. Should be short
+ KID string `mapstructure:"kid"`
+ // Implementation specific locator for private key;
+ // for 'standard' crypto service this is the path to a PEM file
+ Private string `mapstructure:"private"`
+ // Optional locator for the corresponding certificate.
+ // If not found, only public key (derivable from Private) is available.
+ Certificate string `mapstructure:"cert"`
+ // Optional enumeration of intended usages of keypair
+ Usage string `mapstructure:"usage"`
+ // Optional long form description of key pair including purpose and life cycle information
+ Purpose string `mapstructure:"purpose"`
}
type StandardKeyInfo struct {
@@ -34,26 +51,104 @@ type StandardKeyInfo struct {
}
type StandardRSACrypto struct {
- Identifier string
+ KeyPairInfo
asymDecryption ocrypto.AsymDecryption
asymEncryption ocrypto.AsymEncryption
}
type StandardECCrypto struct {
- Identifier string
+ KeyPairInfo
ecPrivateKeyPem string
ecCertificatePEM string
}
+// List of keys by identifier
+type keylist map[string]any
+
type StandardCrypto struct {
- rsaKeys []StandardRSACrypto
- ecKeys []StandardECCrypto
+ // Lists of keys first sorted by algorithm
+ keys map[string]keylist
}
// NewStandardCrypto Create a new instance of standard crypto
func NewStandardCrypto(cfg StandardConfig) (*StandardCrypto, error) {
- standardCrypto := &StandardCrypto{}
- for id, kasInfo := range cfg.RSAKeys {
+ switch {
+ case len(cfg.Keys) > 0 && len(cfg.RSAKeys)+len(cfg.ECKeys) > 0:
+ return nil, errors.New("please specify `keys` only; remove deprecated `rsa` and `ec` fields from cfg")
+ case len(cfg.Keys) > 0:
+ return loadKeys(cfg.Keys)
+ default:
+ return loadDeprecatedKeys(cfg.RSAKeys, cfg.ECKeys)
+ }
+}
+
+func loadKeys(ks []KeyPairInfo) (*StandardCrypto, error) {
+ keys := make(map[string]keylist)
+ for _, k := range ks {
+ slog.Info("crypto cfg loading", "id", k.KID, "alg", k.Algorithm)
+ if _, ok := keys[k.Algorithm]; !ok {
+ keys[k.Algorithm] = make(map[string]any)
+ }
+ loadedKey, err := loadKey(k)
+ if err != nil {
+ return nil, err
+ }
+ keys[k.Algorithm][k.KID] = loadedKey
+ }
+ return &StandardCrypto{
+ keys: keys,
+ }, nil
+}
+
+func loadKey(k KeyPairInfo) (any, error) {
+ privatePEM, err := os.ReadFile(k.Private)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read private key file [%s]: %w", k.Private, err)
+ }
+ var certPEM []byte
+ if k.Certificate != "" {
+ certPEM, err = os.ReadFile(k.Certificate)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read certificate file [%s]: %w", k.Certificate, err)
+ }
+ }
+ switch k.Algorithm {
+ case AlgorithmECP256R1:
+ return StandardECCrypto{
+ KeyPairInfo: k,
+ ecPrivateKeyPem: string(privatePEM),
+ ecCertificatePEM: string(certPEM),
+ }, nil
+ case AlgorithmRSA2048:
+ asymDecryption, err := ocrypto.NewAsymDecryption(string(privatePEM))
+ if err != nil {
+ return nil, fmt.Errorf("ocrypto.NewAsymDecryption failed: %w", err)
+ }
+ asymEncryption, err := ocrypto.NewAsymEncryption(string(certPEM))
+ if err != nil {
+ return nil, fmt.Errorf("ocrypto.NewAsymEncryption failed: %w", err)
+ }
+ return StandardRSACrypto{
+ KeyPairInfo: k,
+ asymDecryption: asymDecryption,
+ asymEncryption: asymEncryption,
+ }, nil
+ default:
+ return nil, errors.New("unsupported algorithm [" + k.Algorithm + "]")
+ }
+}
+
+func loadDeprecatedKeys(rsaKeys map[string]StandardKeyInfo, ecKeys map[string]StandardKeyInfo) (*StandardCrypto, error) {
+ keys := make(map[string]keylist)
+
+ if len(ecKeys) > 0 {
+ keys[AlgorithmECP256R1] = make(map[string]any)
+ }
+ if len(rsaKeys) > 0 {
+ keys[AlgorithmRSA2048] = make(map[string]any)
+ }
+
+ for id, kasInfo := range rsaKeys {
privatePemData, err := os.ReadFile(kasInfo.PrivateKeyPath)
if err != nil {
return nil, fmt.Errorf("failed to rsa private key file: %w", err)
@@ -74,13 +169,18 @@ func NewStandardCrypto(cfg StandardConfig) (*StandardCrypto, error) {
return nil, fmt.Errorf("ocrypto.NewAsymEncryption failed: %w", err)
}
- standardCrypto.rsaKeys = append(standardCrypto.rsaKeys, StandardRSACrypto{
- Identifier: id,
+ keys[AlgorithmRSA2048][id] = StandardRSACrypto{
+ KeyPairInfo: KeyPairInfo{
+ Algorithm: AlgorithmRSA2048,
+ KID: id,
+ Private: kasInfo.PrivateKeyPath,
+ Certificate: kasInfo.PublicKeyPath,
+ },
asymDecryption: asymDecryption,
asymEncryption: asymEncryption,
- })
+ }
}
- for id, kasInfo := range cfg.ECKeys {
+ for id, kasInfo := range ecKeys {
slog.Info("cfg.ECKeys", "id", id, "kasInfo", kasInfo)
// private and public EC KAS key
privatePemData, err := os.ReadFile(kasInfo.PrivateKeyPath)
@@ -92,25 +192,47 @@ func NewStandardCrypto(cfg StandardConfig) (*StandardCrypto, error) {
if err != nil {
return nil, fmt.Errorf("failed to EC certificate file: %w", err)
}
- standardCrypto.ecKeys = append(standardCrypto.ecKeys, StandardECCrypto{
- Identifier: id,
+ keys[AlgorithmECP256R1][id] = StandardECCrypto{
+ KeyPairInfo: KeyPairInfo{
+ Algorithm: AlgorithmRSA2048,
+ KID: id,
+ Private: kasInfo.PrivateKeyPath,
+ Certificate: kasInfo.PublicKeyPath,
+ },
ecPrivateKeyPem: string(privatePemData),
ecCertificatePEM: string(ecCertificatePEM),
- })
+ }
}
- return standardCrypto, nil
+ return &StandardCrypto{
+ keys: keys,
+ }, nil
}
-func (s StandardCrypto) RSAPublicKey(keyID string) (string, error) {
- if len(s.rsaKeys) == 0 {
- return "", ErrCertNotFound
+func (s StandardCrypto) FindKID(alg string) string {
+ if ks, ok := s.keys[alg]; ok && len(ks) > 0 {
+ for kid := range ks {
+ return kid
+ }
}
+ return ""
+}
- // TODO: For now ignore the key id
- slog.Info("⚠️ Ignoring the", slog.String("key id", keyID))
+func (s StandardCrypto) RSAPublicKey(kid string) (string, error) {
+ rsaKeys, ok := s.keys[AlgorithmRSA2048]
+ if !ok || len(rsaKeys) == 0 {
+ return "", ErrCertNotFound
+ }
+ k, ok := rsaKeys[kid]
+ if !ok {
+ return "", ErrCertNotFound
+ }
+ rsa, ok := k.(StandardRSACrypto)
+ if !ok {
+ return "", ErrCertNotFound
+ }
- pem, err := s.rsaKeys[0].asymEncryption.PublicKeyInPemFormat()
+ pem, err := rsa.asymEncryption.PublicKeyInPemFormat()
if err != nil {
return "", fmt.Errorf("failed to retrieve rsa public key file: %w", err)
}
@@ -118,63 +240,73 @@ func (s StandardCrypto) RSAPublicKey(keyID string) (string, error) {
return pem, nil
}
-func (s StandardCrypto) ECCertificate(identifier string) (string, error) {
- if len(s.ecKeys) == 0 {
+func (s StandardCrypto) ECCertificate(kid string) (string, error) {
+ ecKeys, ok := s.keys[AlgorithmECP256R1]
+ if !ok || len(ecKeys) == 0 {
return "", ErrCertNotFound
}
- // this endpoint returns certificate
- for _, ecKey := range s.ecKeys {
- slog.Debug("ecKey", "id", ecKey.Identifier)
- if ecKey.Identifier == identifier {
- return ecKey.ecCertificatePEM, nil
- }
+ k, ok := ecKeys[kid]
+ if !ok {
+ return "", ErrCertNotFound
+ }
+ ec, ok := k.(StandardECCrypto)
+ if !ok {
+ return "", ErrCertNotFound
}
- return "", fmt.Errorf("no EC Key found with the given identifier: %s", identifier)
+ return ec.ecCertificatePEM, nil
}
-func (s StandardCrypto) ECPublicKey(identifier string) (string, error) {
- if len(s.ecKeys) == 0 {
+func (s StandardCrypto) ECPublicKey(kid string) (string, error) {
+ ecKeys, ok := s.keys[AlgorithmECP256R1]
+ if !ok || len(ecKeys) == 0 {
+ return "", ErrCertNotFound
+ }
+ k, ok := ecKeys[kid]
+ if !ok {
+ return "", ErrCertNotFound
+ }
+ ec, ok := k.(StandardECCrypto)
+ if !ok {
return "", ErrCertNotFound
}
- for _, ecKey := range s.ecKeys {
- slog.Debug("ecKey", "id", ecKey.Identifier)
- if ecKey.Identifier != identifier {
- continue
- }
- ecPrivateKey, err := ocrypto.ECPrivateKeyFromPem([]byte(ecKey.ecPrivateKeyPem))
- if err != nil {
- return "", fmt.Errorf("ECPrivateKeyFromPem failed: %s %w", identifier, err)
- }
+ ecPrivateKey, err := ocrypto.ECPrivateKeyFromPem([]byte(ec.ecPrivateKeyPem))
+ if err != nil {
+ return "", fmt.Errorf("ECPrivateKeyFromPem failed: %s %w", kid, err)
+ }
- ecPublicKey := ecPrivateKey.PublicKey()
- derBytes, err := x509.MarshalPKIXPublicKey(ecPublicKey)
- if err != nil {
- return "", fmt.Errorf("failed to marshal public key: %s %w", identifier, err)
- }
+ ecPublicKey := ecPrivateKey.PublicKey()
+ derBytes, err := x509.MarshalPKIXPublicKey(ecPublicKey)
+ if err != nil {
+ return "", fmt.Errorf("failed to marshal public key: %s %w", kid, err)
+ }
- pemBlock := &pem.Block{
- Type: "PUBLIC KEY",
- Bytes: derBytes,
- }
- pemBytes := pem.EncodeToMemory(pemBlock)
- if pemBytes == nil {
- return "", fmt.Errorf("failed to encode public key to PEM: %s", identifier)
- }
- return string(pemBytes), nil
+ pemBlock := &pem.Block{
+ Type: "PUBLIC KEY",
+ Bytes: derBytes,
+ }
+ pemBytes := pem.EncodeToMemory(pemBlock)
+ if pemBytes == nil {
+ return "", fmt.Errorf("failed to encode public key to PEM: %s", kid)
}
- return "", fmt.Errorf("no EC Key found with the given identifier: %s", identifier)
+ return string(pemBytes), nil
}
-func (s StandardCrypto) RSADecrypt(_ crypto.Hash, keyID string, _ string, ciphertext []byte) ([]byte, error) {
- if len(s.rsaKeys) == 0 {
- return nil, errStandardCryptoObjIsInvalid
+func (s StandardCrypto) RSADecrypt(_ crypto.Hash, kid string, _ string, ciphertext []byte) ([]byte, error) {
+ rsaKeys, ok := s.keys[AlgorithmRSA2048]
+ if !ok || len(rsaKeys) == 0 {
+ return nil, ErrCertNotFound
+ }
+ k, ok := rsaKeys[kid]
+ if !ok {
+ return nil, ErrCertNotFound
+ }
+ rsa, ok := k.(StandardRSACrypto)
+ if !ok {
+ return nil, ErrCertNotFound
}
- // TODO: For now ignore the key id
- slog.Info("⚠️ Ignoring the", slog.String("key id", keyID))
-
- data, err := s.rsaKeys[0].asymDecryption.Decrypt(ciphertext)
+ data, err := rsa.asymDecryption.Decrypt(ciphertext)
if err != nil {
return nil, fmt.Errorf("error decrypting data: %w", err)
}
@@ -182,15 +314,21 @@ func (s StandardCrypto) RSADecrypt(_ crypto.Hash, keyID string, _ string, cipher
return data, nil
}
-func (s StandardCrypto) RSAPublicKeyAsJSON(keyID string) (string, error) {
- if len(s.rsaKeys) == 0 {
- return "", errStandardCryptoObjIsInvalid
+func (s StandardCrypto) RSAPublicKeyAsJSON(kid string) (string, error) {
+ rsaKeys, ok := s.keys[AlgorithmRSA2048]
+ if !ok || len(rsaKeys) == 0 {
+ return "", ErrCertNotFound
+ }
+ k, ok := rsaKeys[kid]
+ if !ok {
+ return "", ErrCertNotFound
+ }
+ rsa, ok := k.(StandardRSACrypto)
+ if !ok {
+ return "", ErrCertNotFound
}
- // TODO: For now ignore the key id
- slog.Info("⚠️ Ignoring the", slog.String("key id", keyID))
-
- rsaPublicKeyJwk, err := jwk.FromRaw(s.rsaKeys[0].asymEncryption.PublicKey)
+ rsaPublicKeyJwk, err := jwk.FromRaw(rsa.asymEncryption.PublicKey)
if err != nil {
return "", fmt.Errorf("jwk.FromRaw: %w", err)
}
@@ -203,7 +341,7 @@ func (s StandardCrypto) RSAPublicKeyAsJSON(keyID string) (string, error) {
return string(jsonPublicKey), nil
}
-func (s StandardCrypto) GenerateNanoTDFSymmetricKey(ephemeralPublicKeyBytes []byte, curve elliptic.Curve) ([]byte, error) {
+func (s StandardCrypto) GenerateNanoTDFSymmetricKey(kasKID string, ephemeralPublicKeyBytes []byte, curve elliptic.Curve) ([]byte, error) {
ephemeralECDSAPublicKey, err := ocrypto.UncompressECPubKey(curve, ephemeralPublicKeyBytes)
if err != nil {
return nil, err
@@ -219,7 +357,20 @@ func (s StandardCrypto) GenerateNanoTDFSymmetricKey(ephemeralPublicKeyBytes []by
}
ephemeralECDSAPublicKeyPEM := pem.EncodeToMemory(pemBlock)
- symmetricKey, err := ocrypto.ComputeECDHKey([]byte(s.ecKeys[0].ecPrivateKeyPem), ephemeralECDSAPublicKeyPEM)
+ ecKeys, ok := s.keys[AlgorithmECP256R1]
+ if !ok || len(ecKeys) == 0 {
+ return nil, ErrCertNotFound
+ }
+ k, ok := ecKeys[kasKID]
+ if !ok {
+ return nil, ErrCertNotFound
+ }
+ ec, ok := k.(StandardECCrypto)
+ if !ok {
+ return nil, ErrCertNotFound
+ }
+
+ symmetricKey, err := ocrypto.ComputeECDHKey([]byte(ec.ecPrivateKeyPem), ephemeralECDSAPublicKeyPEM)
if err != nil {
return nil, fmt.Errorf("ocrypto.ComputeECDHKey failed: %w", err)
}
diff --git a/service/kas/access/provider.go b/service/kas/access/provider.go
index b0fc38062..266b1a0b0 100644
--- a/service/kas/access/provider.go
+++ b/service/kas/access/provider.go
@@ -25,6 +25,23 @@ type Provider struct {
CryptoProvider security.CryptoProvider
Logger *logger.Logger
Config *serviceregistry.ServiceConfig
+ KASConfig
+}
+
+type KASConfig struct {
+ // Which keys are currently the default.
+ Keyring []CurrentKeyFor `mapstructure:"keyring"`
+ // Deprecated
+ ECCertID string `mapstructure:"eccertid"`
+}
+
+// Specifies the preferred/default key for a given algorithm type.
+type CurrentKeyFor struct {
+ Algorithm string `mapstructure:"alg"`
+ KID string `mapstructure:"kid"`
+ // Indicates that the key should not be serves by default,
+ // but instead is allowed for legacy reasons on decrypt (rewrap) only
+ Legacy bool `mapstructure:"legacy"`
}
func (p *Provider) IsReady(ctx context.Context) error {
diff --git a/service/kas/access/publicKey.go b/service/kas/access/publicKey.go
index e03851092..3fa64653e 100644
--- a/service/kas/access/publicKey.go
+++ b/service/kas/access/publicKey.go
@@ -19,39 +19,28 @@ import (
const (
ErrCertificateEncode = Error("certificate encode error")
ErrPublicKeyMarshal = Error("public key marshal error")
- algorithmEc256 = "ec:secp256r1"
- algorithmRSA2048 = "rsa:2048"
)
func (p Provider) lookupKid(ctx context.Context, algorithm string) (string, error) {
- key := "unknown"
- defaultKid := "unknown"
- if algorithm == algorithmEc256 {
- defaultKid = "123"
- key = "eccertid"
+ if len(p.KASConfig.Keyring) == 0 {
+ slog.WarnContext(ctx, "no default keys found", "algorithm", algorithm)
+ return "", errors.Join(ErrConfig, status.Error(codes.NotFound, "no default keys configured"))
}
- if p.Config == nil || p.Config.ExtraProps == nil {
- slog.WarnContext(ctx, "using default kid", "kid", defaultKid, "algorithm", algorithm, "certid", key)
- return defaultKid, nil
- }
-
- certid, ok := p.Config.ExtraProps[key]
- if !ok {
- slog.WarnContext(ctx, "using default kid", "kid", defaultKid, "algorithm", algorithm, "certid", key)
- return defaultKid, nil
- }
-
- kid, ok := certid.(string)
- if !ok {
- slog.ErrorContext(ctx, "invalid key configuration", "kid", defaultKid, "algorithm", algorithm, "certid", key)
- return "", ErrConfig
+ for _, k := range p.KASConfig.Keyring {
+ if k.Algorithm == algorithm && !k.Legacy {
+ return k.KID, nil
+ }
}
- return kid, nil
+ slog.WarnContext(ctx, "no (non-legacy) key for requested algorithm", "algorithm", algorithm)
+ return "", errors.Join(ErrConfig, status.Error(codes.NotFound, "no default key for algorithm"))
}
func (p Provider) LegacyPublicKey(ctx context.Context, in *kaspb.LegacyPublicKeyRequest) (*wrapperspb.StringValue, error) {
algorithm := in.GetAlgorithm()
+ if algorithm == "" {
+ algorithm = security.AlgorithmRSA2048
+ }
var pem string
var err error
if p.CryptoProvider == nil {
@@ -63,13 +52,13 @@ func (p Provider) LegacyPublicKey(ctx context.Context, in *kaspb.LegacyPublicKey
}
switch algorithm {
- case algorithmEc256:
+ case security.AlgorithmECP256R1:
pem, err = p.CryptoProvider.ECCertificate(kid)
if err != nil {
slog.ErrorContext(ctx, "CryptoProvider.ECPublicKey failed", "err", err)
return nil, errors.Join(ErrConfig, status.Error(codes.Internal, "configuration error"))
}
- case algorithmRSA2048:
+ case security.AlgorithmRSA2048:
fallthrough
case "":
pem, err = p.CryptoProvider.RSAPublicKey(kid)
@@ -85,13 +74,16 @@ func (p Provider) LegacyPublicKey(ctx context.Context, in *kaspb.LegacyPublicKey
func (p Provider) PublicKey(ctx context.Context, in *kaspb.PublicKeyRequest) (*kaspb.PublicKeyResponse, error) {
algorithm := in.GetAlgorithm()
+ if algorithm == "" {
+ algorithm = security.AlgorithmRSA2048
+ }
fmt := in.GetFmt()
kid, err := p.lookupKid(ctx, algorithm)
if err != nil {
return nil, err
}
- r := func(k string, err error) (*kaspb.PublicKeyResponse, error) {
+ r := func(value, kid string, err error) (*kaspb.PublicKeyResponse, error) {
if errors.Is(err, security.ErrCertNotFound) {
slog.ErrorContext(ctx, "no key found for", "err", err, "kid", kid, "algorithm", algorithm, "fmt", fmt)
return nil, errors.Join(err, status.Error(codes.NotFound, "no such key"))
@@ -99,25 +91,29 @@ func (p Provider) PublicKey(ctx context.Context, in *kaspb.PublicKeyRequest) (*k
slog.ErrorContext(ctx, "configuration error for key lookup", "err", err, "kid", kid, "algorithm", algorithm, "fmt", fmt)
return nil, errors.Join(ErrConfig, status.Error(codes.Internal, "configuration error"))
}
- return &kaspb.PublicKeyResponse{PublicKey: k}, nil
+ if in.GetV() == "1" {
+ slog.WarnContext(ctx, "hiding kid in public key response for legacy client", "kid", kid, "v", in.GetV())
+ return &kaspb.PublicKeyResponse{PublicKey: value}, nil
+ }
+ return &kaspb.PublicKeyResponse{PublicKey: value, Kid: kid}, nil
}
switch algorithm {
- case algorithmEc256:
+ case security.AlgorithmECP256R1:
ecPublicKeyPem, err := p.CryptoProvider.ECPublicKey(kid)
- return r(ecPublicKeyPem, err)
- case algorithmRSA2048:
+ return r(ecPublicKeyPem, kid, err)
+ case security.AlgorithmRSA2048:
fallthrough
case "":
switch fmt {
case "jwk":
rsaPublicKeyPem, err := p.CryptoProvider.RSAPublicKeyAsJSON(kid)
- return r(rsaPublicKeyPem, err)
+ return r(rsaPublicKeyPem, kid, err)
case "pkcs8":
fallthrough
case "":
rsaPublicKeyPem, err := p.CryptoProvider.RSAPublicKey(kid)
- return r(rsaPublicKeyPem, err)
+ return r(rsaPublicKeyPem, kid, err)
}
}
return nil, status.Error(codes.NotFound, "invalid algorithm or format")
diff --git a/service/kas/access/publicKey_hsm_test.go b/service/kas/access/publicKey_hsm_test.go
index 90e4451d3..ed482408d 100644
--- a/service/kas/access/publicKey_hsm_test.go
+++ b/service/kas/access/publicKey_hsm_test.go
@@ -20,14 +20,16 @@ var (
PIN: "12345",
SlotID: 0,
SlotLabel: "dev-token",
- Keys: map[string]security.KeyInfo{
- "rsa": {
- Name: "rsa",
- Label: "development-rsa-kas",
+ Keys: []security.KeyPairInfo{
+ {
+ Algorithm: security.AlgorithmRSA2048,
+ KID: "rsa",
+ Private: "development-rsa-kas",
},
- "ec": {
- Name: "ec",
- Label: "development-ec-kas",
+ {
+ Algorithm: security.AlgorithmECP256R1,
+ KID: "ec",
+ Private: "development-ec-kas",
},
},
},
@@ -35,10 +37,7 @@ var (
)
func TestHSMCertificateHandlerEmpty(t *testing.T) {
- configHSM.HSMConfig.Keys = map[string]security.KeyInfo{
- "rsa": {},
- "ec": {},
- }
+ configHSM.HSMConfig.Keys = []security.KeyPairInfo{}
c := mustNewCryptoProvider(t, configHSM)
defer c.Close()
kasURI := urlHost(t)
@@ -54,14 +53,16 @@ func TestHSMCertificateHandlerEmpty(t *testing.T) {
}
func TestCertificateHandlerWithEc256(t *testing.T) {
- configHSM.HSMConfig.Keys = map[string]security.KeyInfo{
- "rsa": {
- Name: "rsa",
- Label: "development-rsa-kas",
+ configHSM.HSMConfig.Keys = []security.KeyPairInfo{
+ {
+ Algorithm: security.AlgorithmRSA2048,
+ KID: "rsa",
+ Private: "development-rsa-kas",
},
- "ec": {
- Name: "ec",
- Label: "development-ec-kas",
+ {
+ Algorithm: security.AlgorithmECP256R1,
+ KID: "ec",
+ Private: "development-ec-kas",
},
}
c := mustNewCryptoProvider(t, configHSM)
@@ -72,21 +73,23 @@ func TestCertificateHandlerWithEc256(t *testing.T) {
CryptoProvider: c,
}
- result, err := kas.LegacyPublicKey(context.Background(), &kaspb.LegacyPublicKeyRequest{Algorithm: "ec:secp256r1"})
+ result, err := kas.LegacyPublicKey(context.Background(), &kaspb.LegacyPublicKeyRequest{Algorithm: security.AlgorithmECP256R1})
require.NoError(t, err)
require.NotNil(t, result)
assert.Contains(t, result.GetValue(), "BEGIN PUBLIC KEY")
}
func TestHSMPublicKeyHandlerWithEc256(t *testing.T) {
- configHSM.HSMConfig.Keys = map[string]security.KeyInfo{
- "rsa": {
- Name: "rsa",
- Label: "development-rsa-kas",
+ configHSM.HSMConfig.Keys = []security.KeyPairInfo{
+ {
+ Algorithm: security.AlgorithmRSA2048,
+ KID: "rsa",
+ Private: "development-rsa-kas",
},
- "ec": {
- Name: "ec",
- Label: "development-ec-kas",
+ {
+ Algorithm: security.AlgorithmECP256R1,
+ KID: "ec",
+ Private: "development-ec-kas",
},
}
c := mustNewCryptoProvider(t, configHSM)
@@ -97,21 +100,23 @@ func TestHSMPublicKeyHandlerWithEc256(t *testing.T) {
CryptoProvider: c,
}
- result, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{Algorithm: "ec:secp256r1"})
+ result, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{Algorithm: security.AlgorithmECP256R1})
require.NoError(t, err)
require.NotNil(t, result)
assert.Contains(t, result.GetPublicKey(), "BEGIN PUBLIC KEY")
}
func TestHSMPublicKeyHandlerV2(t *testing.T) {
- configHSM.HSMConfig.Keys = map[string]security.KeyInfo{
- "rsa": {
- Name: "rsa",
- Label: "development-rsa-kas",
+ configHSM.HSMConfig.Keys = []security.KeyPairInfo{
+ {
+ Algorithm: security.AlgorithmRSA2048,
+ KID: "rsa",
+ Private: "development-rsa-kas",
},
- "ec": {
- Name: "ec",
- Label: "development-ec-kas",
+ {
+ Algorithm: security.AlgorithmECP256R1,
+ KID: "ec",
+ Private: "development-ec-kas",
},
}
c := mustNewCryptoProvider(t, configHSM)
@@ -122,17 +127,14 @@ func TestHSMPublicKeyHandlerV2(t *testing.T) {
CryptoProvider: c,
}
- result, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{Algorithm: "rsa"})
+ result, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{Algorithm: security.AlgorithmRSA2048})
require.NoError(t, err)
require.NotNil(t, result)
assert.Contains(t, result.GetPublicKey(), "BEGIN PUBLIC KEY")
}
func TestHSMPublicKeyHandlerV2Failure(t *testing.T) {
- configHSM.HSMConfig.Keys = map[string]security.KeyInfo{
- "rsa": {},
- "ec": {},
- }
+ configHSM.HSMConfig.Keys = []security.KeyPairInfo{}
c := mustNewCryptoProvider(t, configHSM)
defer c.Close()
kasURI := urlHost(t)
@@ -141,19 +143,21 @@ func TestHSMPublicKeyHandlerV2Failure(t *testing.T) {
CryptoProvider: c,
}
- _, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{Algorithm: "rsa"})
+ _, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{Algorithm: security.AlgorithmRSA2048})
assert.Error(t, err)
}
func TestHSMPublicKeyHandlerV2WithEc256(t *testing.T) {
- configHSM.HSMConfig.Keys = map[string]security.KeyInfo{
- "rsa": {
- Name: "rsa",
- Label: "development-rsa-kas",
+ configHSM.HSMConfig.Keys = []security.KeyPairInfo{
+ {
+ Algorithm: security.AlgorithmRSA2048,
+ KID: "rsa",
+ Private: "development-rsa-kas",
},
- "ec": {
- Name: "ec",
- Label: "development-ec-kas",
+ {
+ Algorithm: security.AlgorithmECP256R1,
+ KID: "ec",
+ Private: "development-ec-kas",
},
}
c := mustNewCryptoProvider(t, configHSM)
@@ -164,7 +168,7 @@ func TestHSMPublicKeyHandlerV2WithEc256(t *testing.T) {
CryptoProvider: c,
}
- result, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{Algorithm: "ec:secp256r1",
+ result, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{Algorithm: security.AlgorithmECP256R1,
V: "2"})
require.NoError(t, err)
require.NotNil(t, result)
@@ -172,14 +176,16 @@ func TestHSMPublicKeyHandlerV2WithEc256(t *testing.T) {
}
func TestHSMPublicKeyHandlerV2WithJwk(t *testing.T) {
- configHSM.HSMConfig.Keys = map[string]security.KeyInfo{
- "rsa": {
- Name: "rsa",
- Label: "development-rsa-kas",
+ configHSM.HSMConfig.Keys = []security.KeyPairInfo{
+ {
+ Algorithm: security.AlgorithmRSA2048,
+ KID: "rsa",
+ Private: "development-rsa-kas",
},
- "ec": {
- Name: "ec",
- Label: "development-ec-kas",
+ {
+ Algorithm: security.AlgorithmECP256R1,
+ KID: "ec",
+ Private: "development-ec-kas",
},
}
c := mustNewCryptoProvider(t, configHSM)
@@ -191,7 +197,7 @@ func TestHSMPublicKeyHandlerV2WithJwk(t *testing.T) {
}
result, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{
- Algorithm: "rsa",
+ Algorithm: security.AlgorithmRSA2048,
V: "2",
Fmt: "jwk",
})
diff --git a/service/kas/access/publicKey_test.go b/service/kas/access/publicKey_test.go
index 30f32163c..55fc4d974 100644
--- a/service/kas/access/publicKey_test.go
+++ b/service/kas/access/publicKey_test.go
@@ -154,6 +154,14 @@ func TestStandardPublicKeyHandlerV2(t *testing.T) {
kas := Provider{
URI: *kasURI,
CryptoProvider: c,
+ KASConfig: KASConfig{
+ Keyring: []CurrentKeyFor{
+ {
+ Algorithm: security.AlgorithmRSA2048,
+ KID: "rsa",
+ },
+ },
+ },
}
result, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{})
@@ -227,6 +235,14 @@ func TestStandardPublicKeyHandlerV2WithJwk(t *testing.T) {
kas := Provider{
URI: *kasURI,
CryptoProvider: c,
+ KASConfig: KASConfig{
+ Keyring: []CurrentKeyFor{
+ {
+ Algorithm: security.AlgorithmRSA2048,
+ KID: "rsa",
+ },
+ },
+ },
}
result, err := kas.PublicKey(context.Background(), &kaspb.PublicKeyRequest{
diff --git a/service/kas/access/rewrap.go b/service/kas/access/rewrap.go
index 1fc9239ed..55fa905ab 100644
--- a/service/kas/access/rewrap.go
+++ b/service/kas/access/rewrap.go
@@ -30,6 +30,7 @@ import (
"github.com/opentdf/platform/service/internal/auth"
"github.com/opentdf/platform/service/internal/logger"
"github.com/opentdf/platform/service/internal/logger/audit"
+ "github.com/opentdf/platform/service/internal/security"
"github.com/opentdf/platform/service/kas/tdf3"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
@@ -289,7 +290,29 @@ func (p *Provider) Rewrap(ctx context.Context, in *kaspb.RewrapRequest) (*kaspb.
}
func (p *Provider) tdf3Rewrap(ctx context.Context, body *RequestBody, entity *entityInfo) (*kaspb.RewrapResponse, error) {
- symmetricKey, err := p.CryptoProvider.RSADecrypt(crypto.SHA1, "UnKnown", "", body.KeyAccess.WrappedKey)
+ var kidsToCheck []string
+ if body.KeyAccess.KID != "" {
+ kidsToCheck = []string{body.KeyAccess.KID}
+ } else {
+ p.Logger.InfoContext(ctx, "kid free kao")
+ for _, k := range p.KASConfig.Keyring {
+ if k.Algorithm == security.AlgorithmRSA2048 && k.Legacy {
+ kidsToCheck = append(kidsToCheck, k.KID)
+ }
+ }
+ if len(kidsToCheck) == 0 {
+ p.Logger.WarnContext(ctx, "failure to find legacy kids for rsa")
+ return nil, err400("bad request")
+ }
+ }
+ symmetricKey, err := p.CryptoProvider.RSADecrypt(crypto.SHA1, kidsToCheck[0], "", body.KeyAccess.WrappedKey)
+ for _, kid := range kidsToCheck[1:] {
+ p.Logger.WarnContext(ctx, "continue paging through legacy KIDs for kid free kao", "err", err)
+ if err == nil {
+ break
+ }
+ symmetricKey, err = p.CryptoProvider.RSADecrypt(crypto.SHA1, kid, "", body.KeyAccess.WrappedKey)
+ }
if err != nil {
p.Logger.WarnContext(ctx, "failure to decrypt dek", "err", err)
return nil, err400("bad request")
@@ -353,6 +376,13 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, body *RequestBody, entity *en
}
func (p *Provider) nanoTDFRewrap(ctx context.Context, body *RequestBody, entity *entityInfo) (*kaspb.RewrapResponse, error) {
+ // TODO Lookup KID from request content
+ // Should this be in the locator or somewhere else?
+ kid, err := p.lookupKid(ctx, security.AlgorithmECP256R1)
+ if err != nil {
+ p.Logger.WarnContext(ctx, "failure to find default kid for ec", "err", err)
+ return nil, err400("bad request")
+ }
headerReader := bytes.NewReader(body.KeyAccess.Header)
header, _, err := sdk.NewNanoTDFHeaderFromReader(headerReader)
@@ -365,7 +395,7 @@ func (p *Provider) nanoTDFRewrap(ctx context.Context, body *RequestBody, entity
return nil, fmt.Errorf("ECCurve failed: %w", err)
}
- symmetricKey, err := p.CryptoProvider.GenerateNanoTDFSymmetricKey(header.EphemeralKey, ecCurve)
+ symmetricKey, err := p.CryptoProvider.GenerateNanoTDFSymmetricKey(kid, header.EphemeralKey, ecCurve)
if err != nil {
return nil, fmt.Errorf("failed to generate symmetric key: %w", err)
}
diff --git a/service/kas/kas.go b/service/kas/kas.go
index 3a2a9a657..ec477e32c 100644
--- a/service/kas/kas.go
+++ b/service/kas/kas.go
@@ -8,7 +8,9 @@ import (
"strings"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+ "github.com/mitchellh/mapstructure"
kaspb "github.com/opentdf/platform/protocol/go/kas"
+ "github.com/opentdf/platform/service/internal/security"
"github.com/opentdf/platform/service/kas/access"
"github.com/opentdf/platform/service/pkg/serviceregistry"
)
@@ -29,6 +31,42 @@ func NewRegistration() serviceregistry.Registration {
panic(fmt.Errorf("invalid kas address [%s] %w", kasURLString, err))
}
+ var kasCfg access.KASConfig
+ if err := mapstructure.Decode(srp.Config.ExtraProps, &kasCfg); err != nil {
+ panic(fmt.Errorf("invalid kas cfg [%v] %w", srp.Config.ExtraProps, err))
+ }
+
+ switch {
+ case kasCfg.ECCertID != "" && len(kasCfg.Keyring) > 0:
+ panic("invalid kas cfg: please specify keyring or eccertid, not both")
+ case len(kasCfg.Keyring) == 0:
+ if kasCfg.ECCertID != "" {
+ kasCfg.Keyring = append(kasCfg.Keyring, access.CurrentKeyFor{
+ Algorithm: security.AlgorithmECP256R1,
+ KID: kasCfg.ECCertID,
+ })
+ } else {
+ eccertid := srp.OTDF.CryptoProvider.FindKID(security.AlgorithmECP256R1)
+ if eccertid == "" {
+ slog.Warn("no known EC key for KAS")
+ } else {
+ kasCfg.Keyring = append(kasCfg.Keyring, access.CurrentKeyFor{
+ Algorithm: security.AlgorithmECP256R1,
+ KID: eccertid,
+ })
+ }
+ }
+ rsacertid := srp.OTDF.CryptoProvider.FindKID(security.AlgorithmRSA2048)
+ if rsacertid != "" {
+ kasCfg.Keyring = append(kasCfg.Keyring, access.CurrentKeyFor{
+ Algorithm: security.AlgorithmRSA2048,
+ KID: rsacertid,
+ })
+ } else {
+ slog.Warn("no known RSA key for KAS")
+ }
+ }
+
p := access.Provider{
URI: *kasURI,
AttributeSvc: nil,
@@ -36,6 +74,7 @@ func NewRegistration() serviceregistry.Registration {
SDK: srp.SDK,
Logger: srp.Logger,
Config: &srp.Config,
+ KASConfig: kasCfg,
}
if err := srp.RegisterReadinessCheck("kas", p.IsReady); err != nil {
diff --git a/service/kas/kas.proto b/service/kas/kas.proto
index 5c9e62afb..4a85fdd21 100644
--- a/service/kas/kas.proto
+++ b/service/kas/kas.proto
@@ -32,13 +32,14 @@ message LegacyPublicKeyRequest {
}
message PublicKeyRequest {
- string algorithm = 1;
- string fmt = 2 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "version"}];
- string v = 3 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "version"}];
+ string algorithm = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "algorithm type rsa: or ec:"}];
+ string fmt = 2 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "response format"}];
+ string v = 3 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "request version"}];
}
message PublicKeyResponse {
string public_key = 1;
+ string kid = 2;
}
message RewrapRequest {
diff --git a/service/kas/tdf3/keyaccess.go b/service/kas/tdf3/keyaccess.go
index f5333907f..e937c8f63 100644
--- a/service/kas/tdf3/keyaccess.go
+++ b/service/kas/tdf3/keyaccess.go
@@ -6,6 +6,7 @@ type KeyAccess struct {
Protocol string `json:"protocol"`
Type string `json:"type"`
URL string `json:"url"`
+ KID string `json:"kid,omitempty"`
WrappedKey []byte `json:"wrappedKey,omitempty"`
Header []byte `json:"header,omitempty"`
Algorithm string `json:"algorithm,omitempty"`
diff --git a/test/service-start.bats b/test/service-start.bats
index d8037a904..b2b5e1f0f 100755
--- a/test/service-start.bats
+++ b/test/service-start.bats
@@ -25,6 +25,9 @@
# Is an RSA key
printf '%s\n' "$p" | openssl asn1parse | grep rsaEncryption
+
+ # Has expected kid
+ [ $(jq -r .kid <<<"${output}") = r1 ]
}
@test "REST: new public key endpoint (no algorithm)" {
@@ -37,6 +40,9 @@
# Is an RSA key
printf '%s\n' "$p" | openssl asn1parse | grep rsaEncryption
+
+ # Has expected kid
+ [ $(jq -r .kid <<<"${output}") = r1 ]
}
@test "REST: new public key endpoint (ec)" {
@@ -49,6 +55,9 @@
# Is an EC P256r1 curve
printf '%s\n' "$p" | openssl asn1parse | grep prime256v1
+
+ # Has expected kid
+ [ $(jq -r .kid <<<"${output}") = e1 ]
}
@test "REST: public key endpoint (unknown algorithm)" {