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)" {