From 0b225f16af4ed885916bc3b497c54661e857ae6e Mon Sep 17 00:00:00 2001 From: Paul Flynn Date: Thu, 5 Sep 2024 14:14:09 -0400 Subject: [PATCH 1/4] Add ECPublicKeyFetcher for NanoTDF key handling Introduced ECPublicKeyFetcher interface and its implementations to handle EC public key fetching. Updated SDK and nanotdf test cases to utilize the new interface, ensuring better abstraction and testability. Added validation for KID length and tag size in encryption methods. --- lib/ocrypto/aes_gcm.go | 4 ++ sdk/nanotdf.go | 17 +++-- sdk/nanotdf_test.go | 152 ++++++++++++++++++++++++++++++++++------- sdk/sdk.go | 13 ++++ 4 files changed, 158 insertions(+), 28 deletions(-) diff --git a/lib/ocrypto/aes_gcm.go b/lib/ocrypto/aes_gcm.go index 77aaf647e..7e789b017 100644 --- a/lib/ocrypto/aes_gcm.go +++ b/lib/ocrypto/aes_gcm.go @@ -61,11 +61,15 @@ func (aesGcm AesGcm) EncryptWithIV(iv, data []byte) ([]byte, error) { } // EncryptWithIVAndTagSize encrypts data with symmetric key. +// Tag sizes between 12 and 16 bytes are allowed. // NOTE: This method expects gcm standard nonce size(12) of iv. func (aesGcm AesGcm) EncryptWithIVAndTagSize(iv, data []byte, authTagSize int) ([]byte, error) { if len(iv) != GcmStandardNonceSize { return nil, errors.New("invalid nonce size, expects GcmStandardNonceSize") } + if authTagSize < 12 || authTagSize > 16 { + return nil, errors.New("invalid auth tag size, expects 12 or 16") + } gcm, err := cipher.NewGCMWithTagSize(aesGcm.block, authTagSize) if err != nil { diff --git a/sdk/nanotdf.go b/sdk/nanotdf.go index e87f00296..edadfa02b 100644 --- a/sdk/nanotdf.go +++ b/sdk/nanotdf.go @@ -195,6 +195,7 @@ type policyInfo struct { type CipherMode int const ( + // cipherModeAes256gcm64Bit unsupported due to tag size less than 12 cipherModeAes256gcm64Bit CipherMode = 0 cipherModeAes256gcm96Bit CipherMode = 1 cipherModeAes256gcm104Bit CipherMode = 2 @@ -319,6 +320,7 @@ func getECCKeyLength(curve ocrypto.ECCMode) (uint8, error) { // auth tag size in bytes for different ciphers const ( + // kCipher64AuthTagSize unsupported due to tag size less than 12 kCipher64AuthTagSize = 8 kCipher96AuthTagSize = 12 kCipher104AuthTagSize = 13 @@ -431,14 +433,14 @@ func writeNanoTDFHeader(writer io.Writer, config NanoTDFConfig) ([]byte, uint32, encoded := ocrypto.Base64Encode(symmetricKey) slog.Debug("writeNanoTDFHeader", slog.String("symmetricKey", string(encoded))) - aesGcm, err := ocrypto.NewAESGcm(symmetricKey) + tagSize, err := SizeOfAuthTagForCipher(config.sigCfg.cipher) if err != nil { - return nil, 0, fmt.Errorf("ocrypto.NewAESGcm failed:%w", err) + return nil, 0, fmt.Errorf("SizeOfAuthTagForCipher failed:%w", err) } - tagSize, err := SizeOfAuthTagForCipher(config.sigCfg.cipher) + aesGcm, err := ocrypto.NewAESGcm(symmetricKey) if err != nil { - return nil, 0, fmt.Errorf("SizeOfAuthTagForCipher failed:%w", err) + return nil, 0, fmt.Errorf("ocrypto.NewAESGcm failed:%w", err) } const ( @@ -687,7 +689,7 @@ func (s SDK) CreateNanoTDF(writer io.Writer, reader io.Reader, config NanoTDFCon if kasURL == "https://" || kasURL == "http://" { return 0, errors.New("config.kasUrl is empty") } - kasPublicKey, kid, err := getECPublicKeyKid(kasURL, s.dialOptions...) + kasPublicKey, kid, err := s.ecPublicKeyFetcher.GetECPublicKeyKid(kasURL, s.dialOptions...) if err != nil { return 0, fmt.Errorf("getECPublicKey failed:%w", err) } @@ -698,6 +700,11 @@ func (s SDK) CreateNanoTDF(writer io.Writer, reader io.Reader, config NanoTDFCon // update KAS URL with kid if set if kid != "" && !s.nanoFeatures.noKID { + // check length + identifierLen := len(kid) + if identifierLen > 32 { + return 0, fmt.Errorf("invalid KID: unsupported identifier length: %d", identifierLen) + } err = config.kasURL.setURLWithIdentifier(kasURL, kid) if err != nil { return 0, fmt.Errorf("getECPublicKey setURLWithIdentifier failed:%w", err) diff --git a/sdk/nanotdf_test.go b/sdk/nanotdf_test.go index 844e480d4..e63bc7e64 100644 --- a/sdk/nanotdf_test.go +++ b/sdk/nanotdf_test.go @@ -266,37 +266,114 @@ func TestGetECPublicKeyKid(t *testing.T) { } } +// MockECPublicKeyFetcher is a mock implementation for testing purposes. +type MockECPublicKeyFetcher struct { + MockPublicKey string + MockKID string + MockError error +} + +func (m MockECPublicKeyFetcher) GetECPublicKeyKid(kasURL string, opts ...grpc.DialOption) (string, string, error) { + return m.MockPublicKey, m.MockKID, m.MockError +} + func TestCreateNanoTDF(t *testing.T) { + const InvalidMockPublicKey = "-----BEGIN PUBLIC KEY-----\n" + + "GARBAGE\n" + + "-----END PUBLIC KEY-----\n\n" + const ValidMockPublicKey = "-----BEGIN PUBLIC KEY-----\n" + + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMLUYYICsSIDQJ+XnrAsM3x3jdNf2\n" + + "wJhIy/958wUXewDgZ6No/ndUr3G36wDpZHtuYaBXsZoC4jIBxb4+9hALSw==\n" + + "-----END PUBLIC KEY-----\n\n" + const invalidKID = "012345678901234567890123456789012" + keyPairP256, _ := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp256r1) + keyPairP384, _ := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp384r1) + keyPairP521, _ := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp521r1) tests := []struct { - name string - writer io.Writer - reader io.Reader - config NanoTDFConfig - expectedError error + name string + writer io.Writer + reader io.Reader + config NanoTDFConfig + publicKeyFetcher MockECPublicKeyFetcher + expectedError error }{ { - name: "Nil writer", - writer: nil, - reader: bytes.NewReader([]byte("test data")), - config: NanoTDFConfig{}, - expectedError: errors.New("writer is nil"), + name: "Nil writer", + writer: nil, + reader: bytes.NewReader([]byte("test data")), + config: NanoTDFConfig{}, + publicKeyFetcher: MockECPublicKeyFetcher{}, + expectedError: errors.New("writer is nil"), + }, + { + name: "Nil reader", + writer: new(bytes.Buffer), + reader: nil, + config: NanoTDFConfig{}, + publicKeyFetcher: MockECPublicKeyFetcher{}, + expectedError: errors.New("reader is nil"), + }, + { + name: "Empty NanoTDFConfig", + writer: new(bytes.Buffer), + reader: bytes.NewReader([]byte("test data")), + config: NanoTDFConfig{}, + publicKeyFetcher: MockECPublicKeyFetcher{}, + expectedError: errors.New("config.kasUrl is empty"), }, { - name: "Nil reader", - writer: new(bytes.Buffer), - reader: nil, - config: NanoTDFConfig{}, - expectedError: errors.New("reader is nil"), + name: "Invalid Public Key", + writer: new(bytes.Buffer), + reader: bytes.NewReader([]byte("test data")), + config: NanoTDFConfig{ + kasURL: ResourceLocator{ + protocol: 1, + body: "kas.com", + identifier: "e0", + }, + }, + publicKeyFetcher: MockECPublicKeyFetcher{ + MockPublicKey: InvalidMockPublicKey, + }, + expectedError: errors.New("ocrypto.ECPubKeyFromPem failed: failed to parse PEM formatted public key"), + }, + { + name: "Invalid Tag size P256", + writer: new(bytes.Buffer), + reader: bytes.NewReader([]byte("test data")), + config: NanoTDFConfig{ + kasURL: ResourceLocator{ + protocol: 1, + body: "kas.com", + identifier: "e0", + }, + cipher: cipherModeAes256gcm64Bit, + keyPair: keyPairP256, + }, + publicKeyFetcher: MockECPublicKeyFetcher{ + MockPublicKey: ValidMockPublicKey, + }, + expectedError: errors.New("writeNanoTDFHeader failed:AesGcm.EncryptWithIVAndTagSize failed:invalid auth tag size, expects 12 or 16"), }, { - name: "Empty NanoTDFConfig", - writer: new(bytes.Buffer), - reader: bytes.NewReader([]byte("test data")), - config: NanoTDFConfig{}, - expectedError: errors.New("config.kasUrl is empty"), + name: "Invalid Curve match P256-P384", + writer: new(bytes.Buffer), + reader: bytes.NewReader([]byte("test data")), + config: NanoTDFConfig{ + kasURL: ResourceLocator{ + protocol: 1, + body: "kas.com", + identifier: "e0", + }, + keyPair: keyPairP384, + }, + publicKeyFetcher: MockECPublicKeyFetcher{ + MockPublicKey: ValidMockPublicKey, + }, + expectedError: errors.New("writeNanoTDFHeader failed:ocrypto.ComputeECDHKeyFromEC failed:there was a problem deriving a shared ECDH key: crypto/ecdh: private key and public key curves do not match"), }, { - name: "KAS Identifier NanoTDFConfig", + name: "Invalid Curve match P256-P521", writer: new(bytes.Buffer), reader: bytes.NewReader([]byte("test data")), config: NanoTDFConfig{ @@ -305,14 +382,43 @@ func TestCreateNanoTDF(t *testing.T) { body: "kas.com", identifier: "e0", }, + keyPair: keyPairP521, + }, + publicKeyFetcher: MockECPublicKeyFetcher{ + MockPublicKey: ValidMockPublicKey, }, - expectedError: errors.New("getECPublicKey failed:error connecting to grpc service at https://kas.com: grpc: no transport security set (use grpc.WithTransportCredentials(insecure.NewCredentials()) explicitly or set credentials)"), + expectedError: errors.New("writeNanoTDFHeader failed:ocrypto.ComputeECDHKeyFromEC failed:there was a problem deriving a shared ECDH key: crypto/ecdh: private key and public key curves do not match"), + }, + { + name: "Invalid KID size", + writer: new(bytes.Buffer), + reader: bytes.NewReader([]byte("test data")), + config: NanoTDFConfig{ + kasURL: ResourceLocator{ + protocol: 1, + body: "kas.com", + identifier: invalidKID, + }, + sigCfg: signatureConfig{ + hasSignature: false, + signatureMode: 0, + cipher: cipherModeAes256gcm96Bit, + }, + keyPair: keyPairP256, + }, + publicKeyFetcher: MockECPublicKeyFetcher{ + MockPublicKey: ValidMockPublicKey, + MockKID: invalidKID, + }, + expectedError: errors.New("invalid KID: unsupported identifier length: 33"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - var s SDK + s := SDK{ + ecPublicKeyFetcher: tt.publicKeyFetcher, + } _, err := s.CreateNanoTDF(tt.writer, tt.reader, tt.config) if err != nil { if tt.expectedError == nil { diff --git a/sdk/sdk.go b/sdk/sdk.go index d0bc6373f..7f96d6bd3 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -51,11 +51,17 @@ func (c Error) Error() string { return string(c) } +// ECPublicKeyFetcher is an interface that defines the method to fetch the EC Public Key and KID. +type ECPublicKeyFetcher interface { + GetECPublicKeyKid(kasURL string, opts ...grpc.DialOption) (string, string, error) +} + type SDK struct { config *kasKeyCache conn *grpc.ClientConn dialOptions []grpc.DialOption + ecPublicKeyFetcher ECPublicKeyFetcher tokenSource auth.AccessTokenSource Namespaces namespaces.NamespaceServiceClient Attributes attributes.AttributesServiceClient @@ -184,6 +190,13 @@ func New(platformEndpoint string, opts ...Option) (*SDK, error) { }, nil } +// RealECPublicKeyFetcher is the real implementation for fetching the EC Public Key Kid. +type RealECPublicKeyFetcher struct{} + +func (r RealECPublicKeyFetcher) GetECPublicKeyKid(kasURL string, opts ...grpc.DialOption) (string, string, error) { + return getECPublicKeyKid(kasURL, opts...) +} + func SanitizePlatformEndpoint(e string) (string, error) { // check if there's a scheme, if not, add https if !regexp.MustCompile(`^https?://`).MatchString(e) { From 2e695a3f565da5dec7958bb274311a60e615f098 Mon Sep 17 00:00:00 2001 From: Paul Flynn Date: Thu, 5 Sep 2024 14:28:00 -0400 Subject: [PATCH 2/4] Rename struct and add KID max length check. Renamed `RealECPublicKeyFetcher` to `EcPublicKeyFetcher` for consistency. Introduced a constant for the maximum KID length and added a check for KID length in the `createNanoTDF` function. --- sdk/nanotdf.go | 7 ++++++- sdk/nanotdf_test.go | 18 +++++++++--------- sdk/sdk.go | 6 +++--- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/sdk/nanotdf.go b/sdk/nanotdf.go index edadfa02b..9b2ec78af 100644 --- a/sdk/nanotdf.go +++ b/sdk/nanotdf.go @@ -39,6 +39,7 @@ const ( kNanoTDFIvSize = 3 kNanoTDFGMACLength = 8 kNanoTDFMagicStringAndVersion = "L1L" + kNanoTDFKIDMaxLength = 32 ) /******************************** Header************************** @@ -689,6 +690,10 @@ func (s SDK) CreateNanoTDF(writer io.Writer, reader io.Reader, config NanoTDFCon if kasURL == "https://" || kasURL == "http://" { return 0, errors.New("config.kasUrl is empty") } + if s.ecPublicKeyFetcher == nil { + // refactored for testability, if not set then use wrapper around getECPublicKeyKid + s.ecPublicKeyFetcher = EcPublicKeyFetcher{} + } kasPublicKey, kid, err := s.ecPublicKeyFetcher.GetECPublicKeyKid(kasURL, s.dialOptions...) if err != nil { return 0, fmt.Errorf("getECPublicKey failed:%w", err) @@ -702,7 +707,7 @@ func (s SDK) CreateNanoTDF(writer io.Writer, reader io.Reader, config NanoTDFCon if kid != "" && !s.nanoFeatures.noKID { // check length identifierLen := len(kid) - if identifierLen > 32 { + if identifierLen > kNanoTDFKIDMaxLength { return 0, fmt.Errorf("invalid KID: unsupported identifier length: %d", identifierLen) } err = config.kasURL.setURLWithIdentifier(kasURL, kid) diff --git a/sdk/nanotdf_test.go b/sdk/nanotdf_test.go index e63bc7e64..c01e7a984 100644 --- a/sdk/nanotdf_test.go +++ b/sdk/nanotdf_test.go @@ -273,19 +273,19 @@ type MockECPublicKeyFetcher struct { MockError error } -func (m MockECPublicKeyFetcher) GetECPublicKeyKid(kasURL string, opts ...grpc.DialOption) (string, string, error) { +func (m MockECPublicKeyFetcher) GetECPublicKeyKid(_ string, _ ...grpc.DialOption) (string, string, error) { return m.MockPublicKey, m.MockKID, m.MockError } func TestCreateNanoTDF(t *testing.T) { - const InvalidMockPublicKey = "-----BEGIN PUBLIC KEY-----\n" + + invalidMockPublicKey := "-----BEGIN PUBLIC KEY-----\n" + "GARBAGE\n" + "-----END PUBLIC KEY-----\n\n" - const ValidMockPublicKey = "-----BEGIN PUBLIC KEY-----\n" + + validMockPublicKey := "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMLUYYICsSIDQJ+XnrAsM3x3jdNf2\n" + "wJhIy/958wUXewDgZ6No/ndUr3G36wDpZHtuYaBXsZoC4jIBxb4+9hALSw==\n" + "-----END PUBLIC KEY-----\n\n" - const invalidKID = "012345678901234567890123456789012" + invalidKID := "012345678901234567890123456789012" keyPairP256, _ := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp256r1) keyPairP384, _ := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp384r1) keyPairP521, _ := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp521r1) @@ -333,7 +333,7 @@ func TestCreateNanoTDF(t *testing.T) { }, }, publicKeyFetcher: MockECPublicKeyFetcher{ - MockPublicKey: InvalidMockPublicKey, + MockPublicKey: invalidMockPublicKey, }, expectedError: errors.New("ocrypto.ECPubKeyFromPem failed: failed to parse PEM formatted public key"), }, @@ -351,7 +351,7 @@ func TestCreateNanoTDF(t *testing.T) { keyPair: keyPairP256, }, publicKeyFetcher: MockECPublicKeyFetcher{ - MockPublicKey: ValidMockPublicKey, + MockPublicKey: validMockPublicKey, }, expectedError: errors.New("writeNanoTDFHeader failed:AesGcm.EncryptWithIVAndTagSize failed:invalid auth tag size, expects 12 or 16"), }, @@ -368,7 +368,7 @@ func TestCreateNanoTDF(t *testing.T) { keyPair: keyPairP384, }, publicKeyFetcher: MockECPublicKeyFetcher{ - MockPublicKey: ValidMockPublicKey, + MockPublicKey: validMockPublicKey, }, expectedError: errors.New("writeNanoTDFHeader failed:ocrypto.ComputeECDHKeyFromEC failed:there was a problem deriving a shared ECDH key: crypto/ecdh: private key and public key curves do not match"), }, @@ -385,7 +385,7 @@ func TestCreateNanoTDF(t *testing.T) { keyPair: keyPairP521, }, publicKeyFetcher: MockECPublicKeyFetcher{ - MockPublicKey: ValidMockPublicKey, + MockPublicKey: validMockPublicKey, }, expectedError: errors.New("writeNanoTDFHeader failed:ocrypto.ComputeECDHKeyFromEC failed:there was a problem deriving a shared ECDH key: crypto/ecdh: private key and public key curves do not match"), }, @@ -407,7 +407,7 @@ func TestCreateNanoTDF(t *testing.T) { keyPair: keyPairP256, }, publicKeyFetcher: MockECPublicKeyFetcher{ - MockPublicKey: ValidMockPublicKey, + MockPublicKey: validMockPublicKey, MockKID: invalidKID, }, expectedError: errors.New("invalid KID: unsupported identifier length: 33"), diff --git a/sdk/sdk.go b/sdk/sdk.go index 7f96d6bd3..5b83c8440 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -190,10 +190,10 @@ func New(platformEndpoint string, opts ...Option) (*SDK, error) { }, nil } -// RealECPublicKeyFetcher is the real implementation for fetching the EC Public Key Kid. -type RealECPublicKeyFetcher struct{} +// EcPublicKeyFetcher is the real implementation for fetching the EC Public Key Kid. +type EcPublicKeyFetcher struct{} -func (r RealECPublicKeyFetcher) GetECPublicKeyKid(kasURL string, opts ...grpc.DialOption) (string, string, error) { +func (r EcPublicKeyFetcher) GetECPublicKeyKid(kasURL string, opts ...grpc.DialOption) (string, string, error) { return getECPublicKeyKid(kasURL, opts...) } From e18c602d13e6f227c62912e8d5f6d591f45e52ab Mon Sep 17 00:00:00 2001 From: Paul Flynn <43211074+pflynn-virtru@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:10:37 -0400 Subject: [PATCH 3/4] Update lib/ocrypto/aes_gcm.go --- examples/oidc-hydra/README.md | 60 ++++ examples/oidc-hydra/db.sqlite | Bin 0 -> 397312 bytes examples/oidc-hydra/docker-compose.yaml | 51 ++++ examples/oidc-hydra/hydra.yaml | 41 +++ examples/samplejs.txt.ntdf | Bin 0 -> 194 bytes lib/ocrypto/aes_gcm.go | 2 +- mockpublickey.pem | 4 + opentdf-bkp.yaml | 65 ++++ opentdf-idp.yaml | 88 ++++++ policies/entitlements/conditions.rego | 31 ++ policies/entitlements/idp-client.json | 121 ++++++++ policies/entitlements/in-kas-simplified.json | 88 ++++++ policies/entitlements/in-kas.json | 280 ++++++++++++++++++ policies/entitlements/in-keycloak.json | 35 +++ policies/entitlements/in-simplified.json | 60 ++++ policies/entitlements/in.json | 222 ++++++++++++++ .../entitlements/input-condition-test.json | 12 + policies/entitlements/input-jwt.json | 85 ++++++ policies/entitlements/input-simple.json | 134 +++++++++ 19 files changed, 1378 insertions(+), 1 deletion(-) create mode 100644 examples/oidc-hydra/README.md create mode 100644 examples/oidc-hydra/db.sqlite create mode 100644 examples/oidc-hydra/docker-compose.yaml create mode 100644 examples/oidc-hydra/hydra.yaml create mode 100644 examples/samplejs.txt.ntdf create mode 100644 mockpublickey.pem create mode 100644 opentdf-bkp.yaml create mode 100644 opentdf-idp.yaml create mode 100644 policies/entitlements/conditions.rego create mode 100644 policies/entitlements/idp-client.json create mode 100644 policies/entitlements/in-kas-simplified.json create mode 100644 policies/entitlements/in-kas.json create mode 100644 policies/entitlements/in-keycloak.json create mode 100644 policies/entitlements/in-simplified.json create mode 100644 policies/entitlements/in.json create mode 100644 policies/entitlements/input-condition-test.json create mode 100644 policies/entitlements/input-jwt.json create mode 100644 policies/entitlements/input-simple.json diff --git a/examples/oidc-hydra/README.md b/examples/oidc-hydra/README.md new file mode 100644 index 000000000..a1a640063 --- /dev/null +++ b/examples/oidc-hydra/README.md @@ -0,0 +1,60 @@ +# OpenTDF Platform Ory Hydra example + +## Overview + +This example shows how to integrate with [Ory Hydra](https://www.ory.sh/hydra/), a hardened and certified OAuth 2.0 and OpenID Connect provider. + +The highlight of this example is to show th webhook integration. + +## Setup + +### Install Ory Hydra + +```shell +brew install ory/tap/hydra +``` + +### Start both public and administrative HTTP/2 APIs. +Also, create a sqllite db + +```shell +export DSN="sqlite://./db.sqlite?_fk=true" +hydra migrate sql $DSN --yes +hydra serve all --dev --config ./hydra.yaml +``` + +### Create clients + +client `opentdf` + +```shell +hydra create oauth2-client \ + --skip-tls-verify \ + --endpoint http://127.0.0.1:4445/ \ + --format json \ + --grant-type client_credentials \ + --name opentdf \ + --secret secret +``` + +client `opentdf-sdk` + +```shell +hydra create oauth2-client \ + --skip-tls-verify \ + --endpoint http://127.0.0.1:4445/ \ + --format json \ + --grant-type client_credentials \ + --audience "http://localhost:8080" \ + --name opentdf-sdk4 \ + --secret secret +``` + +- Update `opentdf.yaml` with UUID client ids returned from hydra +- Update `sdk.WithClientCredentials` and `sdk.WithTokenEndpoint` in `examples/cmd` + +### Start OpenTDF server + +```shell +../../opentdf start +``` diff --git a/examples/oidc-hydra/db.sqlite b/examples/oidc-hydra/db.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..85296c122b79a23ac3e49b0d9e5858ee8cd5ae5b GIT binary patch literal 397312 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⋘o`z&fz5oJm$5=gSxU7BV{q)QMwH|yC-SIqqD)s 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 literal 0 HcmV?d00001 diff --git a/examples/oidc-hydra/docker-compose.yaml b/examples/oidc-hydra/docker-compose.yaml new file mode 100644 index 000000000..1b3c7adc2 --- /dev/null +++ b/examples/oidc-hydra/docker-compose.yaml @@ -0,0 +1,51 @@ +# copied from https://github.com/ory/hydra/blob/master/quickstart.yml +########################################################################### +####### FOR DEMONSTRATION PURPOSES ONLY ####### +########################################################################### +# # +# If you have not yet read the tutorial, do so now: # +# https://www.ory.sh/docs/hydra/5min-tutorial # +# # +# This set up is only for demonstration purposes. The login # +# endpoint can only be used if you follow the steps in the tutorial. # +# # +########################################################################### +version: "3.7" +services: + hydra: + image: oryd/hydra:v2.2.0 + ports: + - "4444:4444" # Public port + - "4445:4445" # Admin port + - "5555:5555" # Port for hydra token user + command: serve -c /etc/config/hydra/hydra.yml all --dev + volumes: + - ./hydra.yaml:/etc/config/hydra/hydra.yaml + environment: + - DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true + restart: unless-stopped + depends_on: + - hydra-migrate + networks: + - intranet + hydra-migrate: + image: oryd/hydra:v2.2.0 + environment: + - DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true + command: migrate -c /etc/config/hydra/hydra.yml sql -e --yes + volumes: + - ./hydra.yaml:/etc/config/hydra/hydra.yaml + restart: on-failure + networks: + - intranet + consent: + environment: + - HYDRA_ADMIN_URL=http://hydra:4445 + image: oryd/hydra-login-consent-node:v2.2.0 + ports: + - "3000:3000" + restart: unless-stopped + networks: + - intranet +networks: + intranet: diff --git a/examples/oidc-hydra/hydra.yaml b/examples/oidc-hydra/hydra.yaml new file mode 100644 index 000000000..00cc65085 --- /dev/null +++ b/examples/oidc-hydra/hydra.yaml @@ -0,0 +1,41 @@ +serve: + cookies: + same_site_mode: Lax + +urls: + self: + issuer: http://127.0.0.1:4444 + consent: http://127.0.0.1:3000/consent + login: http://127.0.0.1:3000/login + logout: http://127.0.0.1:3000/logout + +secrets: + system: + - youReallyNeedToChangeThis + +oidc: + subject_identifiers: + supported_types: + - pairwise + - public + pairwise: + salt: youReallyNeedToChangeThis + +strategies: + ## access_token ## + # + # Defines access token type. jwt is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens + # + # Default value: opaque + # + # One of: + # - opaque + # - jwt + # + # Set this value using environment variables on + # - Linux/macOS: + # $ export STRATEGIES_ACCESS_TOKEN= + # - Windows Command Line (CMD): + # > set STRATEGIES_ACCESS_TOKEN= + # + access_token: jwt diff --git a/examples/samplejs.txt.ntdf b/examples/samplejs.txt.ntdf new file mode 100644 index 0000000000000000000000000000000000000000..d9e19de9fb5da6708c476402fa56dcd88d631baa GIT binary patch literal 194 zcmV;z06qUqF-!mwY;R*>Y-n$DbUHXNI5013VRHZh0sv$&FiU2UYOK+oIE@0`S^ zZ6Q|d>6F=nhLeMbQauc3)naZ@p4^XQooS%vjc6qg<7WGNJ4(}Xn;-hW41zoRK5DUq z`lp0UgeRp>kmVF$CvMM?i wn;MtQ%lcmh(aFJMkh~tZ{}hX$000&M00Anm?6tCxt|~S>%Kha=VcRO0J+dfR6#xJL literal 0 HcmV?d00001 diff --git a/lib/ocrypto/aes_gcm.go b/lib/ocrypto/aes_gcm.go index 7e789b017..74296dc32 100644 --- a/lib/ocrypto/aes_gcm.go +++ b/lib/ocrypto/aes_gcm.go @@ -68,7 +68,7 @@ func (aesGcm AesGcm) EncryptWithIVAndTagSize(iv, data []byte, authTagSize int) ( return nil, errors.New("invalid nonce size, expects GcmStandardNonceSize") } if authTagSize < 12 || authTagSize > 16 { - return nil, errors.New("invalid auth tag size, expects 12 or 16") + return nil, errors.New("invalid auth tag size, must be 12 to 16") } gcm, err := cipher.NewGCMWithTagSize(aesGcm.block, authTagSize) diff --git a/mockpublickey.pem b/mockpublickey.pem new file mode 100644 index 000000000..2de221471 --- /dev/null +++ b/mockpublickey.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMLUYYICsSIDQJ+XnrAsM3x3jdNf2 +wJhIy/958wUXewDgZ6No/ndUr3G36wDpZHtuYaBXsZoC4jIBxb4+9hALSw== +-----END PUBLIC KEY----- diff --git a/opentdf-bkp.yaml b/opentdf-bkp.yaml new file mode 100644 index 000000000..680df22cd --- /dev/null +++ b/opentdf-bkp.yaml @@ -0,0 +1,65 @@ +logger: + level: debug + type: text + output: stdout +# DB and Server confgurations are defaulted for local development +# db: +# host: localhost +# port: 5432 +# user: postgres +# password: changeme +services: + kas: + enabled: true + policy: + enabled: true + entitlements: + providers: + # - type: keycloak + # name: gcp + # keycloak: + # host: "https://keycloak.example.com/auth" + # realm: "test" + # clientId: "test" + # clientSecret: + # fromEnv: "KEYCLOAK_CLIENT_SECRET" + + - type: ldap + name: ad-1 + ldap: + baseDN: "dc=dev,dc=example,dc=com" + host: "" + port: 389 + bindUsername: "" + bindPassword: + fromEnv: "LDAP_BIND_PASSWORD" + attributeFilters: + exclude: + - "objectSid" + - "objectGUID" + - "msExchMailboxGuid" + - "msExchMailboxSecurityDescriptor" +server: + auth: + enabled: false + audience: "http://localhost:8080" + issuer: http://localhost:8888/auth/realms/opentdf + clients: + - "opentdf" + grpc: + port: 8080 + reflectionEnabled: true # Default is false + hsm: + enabled: true + # As configured by hsm-init-temporary-keys.sh + pin: "12345" + slotlabel: "dev-token" + keys: + rsa: + label: development-rsa-kas + ec: + label: development-ec-kas + http: + port: 8080 +opa: + embedded: true # Only for local development diff --git a/opentdf-idp.yaml b/opentdf-idp.yaml new file mode 100644 index 000000000..0c8839a1a --- /dev/null +++ b/opentdf-idp.yaml @@ -0,0 +1,88 @@ +logger: + level: debug + type: text + output: stdout +# DB and Server configurations are defaulted for local development +# db: +# host: localhost +# port: 5432 +# user: postgres +# password: changeme +services: + kas: + enabled: true + issuer: http://127.0.0.1:4444 + policy: + enabled: true + authorization: + enabled: true + url: http://localhost:8888 + client: "tdf-entity-resolution" + secret: "secret" + realm: "opentdf" + legacy: true +server: + auth: + enabled: true + audience: "http://localhost:8080" + issuer: http://127.0.0.1:4444 + clients: + - "e2465b5a-5371-4fe3-8f21-8b7938d928df" + - "e213c08f-9327-4778-80da-a590671fcf3c" + - "75a697cd-2770-4cbb-bd1b-e3cf9b3c5f2d" + policy: + ## Default policy for all requests + default: #"role:readonly" + ## Dot notation is used to access nested claims (i.e. realm_access.roles) + claim: # realm_access.roles + ## Maps the external role to the opentdf role + ## Note: left side is used in the policy, right side is the external role + map: + # readonly: opentdf-readonly + # admin: opentdf-admin + # org-admin: opentdf-org-admin + + ## Custom policy (see examples https://github.com/casbin/casbin/tree/master/examples) + csv: #| + # p, role:org-admin, policy:attributes, *, *, allow + # p, role:org-admin, policy:subject-mappings, *, *, allow + # p, role:org-admin, policy:resource-mappings, *, *, allow + # p, role:org-admin, policy:kas-registry, *, *, allow + ## Custom model (see https://casbin.org/docs/syntax-for-models/) + model: #| + # [request_definition] + # r = sub, res, act, obj + # + # [policy_definition] + # p = sub, res, act, obj, eft + # + # [role_definition] + # g = _, _ + # + # [policy_effect] + # e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) + # + # [matchers] + # m = g(r.sub, p.sub) && globOrRegexMatch(r.res, p.res) && globOrRegexMatch(r.act, p.act) && globOrRegexMatch(r.obj, p.obj) + + grpc: + reflectionEnabled: true # Default is false + cryptoProvider: + hsm: + enabled: false + pin: 12345 + standard: + rsa: + 123: + privateKeyPath: ../kas-private.pem + publicKeyPath: ../kas-cert.pem + 456: + privateKeyPath: ../kas-private.pem + publicKeyPath: ../kas-cert.pem + ec: + 123: + privateKeyPath: kas-ec-private.pem + publicKeyPath: kas-ec-cert.pem + port: 8080 +opa: + embedded: true # Only for local development diff --git a/policies/entitlements/conditions.rego b/policies/entitlements/conditions.rego new file mode 100644 index 000000000..dc812389a --- /dev/null +++ b/policies/entitlements/conditions.rego @@ -0,0 +1,31 @@ +package opentdf.conditions + +import rego.v1 + +# condition_group +condition_group_evaluate(payload, boolean_operator, conditions) if { + # AND + boolean_operator == 1 + some condition in conditions + condition_evaluate(payload[condition.subject_external_field], condition.operator, condition.subject_external_values) +} else if { + # OR + boolean_operator == 2 + payload[key] + some condition in conditions + condition_evaluate(payload[condition.subject_external_field], condition.operator, condition.subject_external_values) +} + +# condition +condition_evaluate(property_values, operator, values) if { + # IN + operator == 1 + some property_value in property_values + property_value in values +} else if { + # NOT IN + operator == 2 + some property_value in property_values + not property_value in values +} + diff --git a/policies/entitlements/idp-client.json b/policies/entitlements/idp-client.json new file mode 100644 index 000000000..19e42dc91 --- /dev/null +++ b/policies/entitlements/idp-client.json @@ -0,0 +1,121 @@ +{ + "entity_representations": [ + { + "additional_props": [ + { + "roles": [ + "uma_protection", + "vice_president" + ] + }, + { + "access": { + "configure": false, + "manage": false, + "view": true + }, + "adminUrl": "", + "attributes": { + "backchannel.logout.revoke.offline.tokens": "false", + "backchannel.logout.session.required": "true", + "client.secret.creation.time": "1710873216", + "display.on.consent.screen": "false", + "oauth2.device.authorization.grant.enabled": "false", + "oidc.ciba.grant.enabled": "false" + }, + "authenticationFlowBindingOverrides": {}, + "authorizationServicesEnabled": true, + "baseUrl": "", + "bearerOnly": false, + "clientAuthenticatorType": "client-secret", + "clientId": "opentdf", + "consentRequired": false, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "description": "", + "directAccessGrantsEnabled": true, + "enabled": true, + "frontchannelLogout": true, + "fullScopeAllowed": true, + "id": "485d5e66-23f7-47f6-9d37-3dbf20e493fb", + "implicitFlowEnabled": true, + "name": "", + "nodeReRegistrationTimeout": -1, + "notBefore": 0, + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ], + "protocol": "openid-connect", + "protocolMappers": [ + { + "config": { + "access.token.claim": "true", + "claim.name": "client_id", + "id.token.claim": "true", + "introspection.token.claim": "true", + "jsonType.label": "String", + "user.session.note": "client_id" + }, + "consentRequired": false, + "id": "296dc3bc-664b-49ce-9868-9a0152fc61e5", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper" + }, + { + "config": { + "access.token.claim": "true", + "claim.name": "clientHost", + "id.token.claim": "true", + "introspection.token.claim": "true", + "jsonType.label": "String", + "user.session.note": "clientHost" + }, + "consentRequired": false, + "id": "16e0a65d-fcab-4110-86b1-dacc7e149456", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper" + }, + { + "config": { + "access.token.claim": "true", + "claim.name": "clientAddress", + "id.token.claim": "true", + "introspection.token.claim": "true", + "jsonType.label": "String", + "user.session.note": "clientAddress" + }, + "consentRequired": false, + "id": "73d566d3-1ed4-4a69-ae1d-9310f8c815bb", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper" + } + ], + "publicClient": false, + "redirectUris": [ + "/*" + ], + "rootUrl": "", + "secret": "7iocVo2WBLIzPSaZtHZwCLL630L2V7lE", + "serviceAccountsEnabled": true, + "standardFlowEnabled": true, + "surrogateAuthRequired": false, + "webOrigins": [ + "/*" + ] + } + ], + "original_id": "1234" + } + ] +} diff --git a/policies/entitlements/in-kas-simplified.json b/policies/entitlements/in-kas-simplified.json new file mode 100644 index 000000000..8ccdb69d1 --- /dev/null +++ b/policies/entitlements/in-kas-simplified.json @@ -0,0 +1,88 @@ +{ + "attribute_mappings": { + "https://example.com/attr/attr1/value/value1": { + "attribute": { + "id": "6a261d68-0899-4e17-bb2f-124abba7c09c", + "namespace": { + "id": "8f1d8839-2851-4bf4-8bf4-5243dbfe517d", + "name": "example.com" + }, + "name": "attr1", + "rule": 2, + "values": [ + { + "id": "74babca6-016f-4f3e-a99b-4e46ea8d0fd8", + "value": "value1", + "fqn": "https://example.com/attr/attr1/value/value1", + "active": { + "value": true + } + }, + { + "id": "2fe8dea1-3555-498c-afe9-99724f35f3d3", + "value": "value2", + "active": { + "value": true + } + } + ], + "active": { + "value": true + }, + "metadata": {} + }, + "value": { + "id": "74babca6-016f-4f3e-a99b-4e46ea8d0fd8", + "value": "value1", + "fqn": "https://example.com/attr/attr1/value/value1", + "active": { + "value": true + }, + "subject_mappings": [ + { + "id": "3c623ede-df88-4906-8a78-ebdfacadcd57", + "subject_condition_set": { + "id": "3c623ede-df88-4906-8a78-ebdfacadcd57", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "jwt.resource_access.${jwt.client_id}.roles", + "operator": 1, + "subject_external_values": [ + "writer" + ] + } + ], + "boolean_operator": 2 + } + ] + } + ] + }, + "actions": [ + { + "Value": { + "Standard": 1 + } + } + ] + } + ] + } + } + }, + "entity": { + "id": "", + "jwt": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqOW1hTVRaUDdBMG9qSVFYNkJiOTRIdVlYeW5fdC1RR0VJQjJ1akpheDI4In0.eyJleHAiOjE3MTExMTQ4NDAsImlhdCI6MTcxMTExNDU0MCwianRpIjoiMTA5ZjhkZDctNjczYy00NDI2LThhY2EtOTFkMjVlZjU1NGE3IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4ODg4L2F1dGgvcmVhbG1zL3RkZiIsInN1YiI6IjQ4ZGU2MzRkLWMyZjAtNDhiZS04NjYxLTM4NTQ3ODMyNDAzNiIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9wZW50ZGYiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIi8qIl0sInJlc291cmNlX2FjY2VzcyI6eyJvcGVudGRmIjp7InJvbGVzIjpbIndyaXRlciJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCBncm91cHMiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImNsaWVudEhvc3QiOiIxOTIuMTY4LjY1LjEiLCJncm91cHMiOlsiL2VuZ2luZWVyaW5nIiwiL3Byb2plY3R4Il0sInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1vcGVudGRmIiwiY2xpZW50QWRkcmVzcyI6IjE5Mi4xNjguNjUuMSIsImNsaWVudF9pZCI6Im9wZW50ZGYifQ.FxbPmw92rgIJus6kbqQO5TcFMtut0Y69pBKPqhLXYF5jqHYeo83p5J4CxZ2iUS04tdnw6bpKF-PC6V_M-O_NCEAlBeCLWbsU8ccWM4847tRIML1xRGQELedqUv8KsgDroYQwLpG6J5mf7I5oCEQ4RZR6Njq30BIK0XbvrTkdV3YB4e9l56WIK2j_W5_v4p2L4PzflCsqLkF4d8t8Ggjc3yXV8j_HL1HVDwPIzShNdaZJduT1Bw_j6K9dZqxhTCJEOmie0seZenKySif4ruc5kDdXj-ePpRJYLq_yD_ViVgZWLldjvGwuaqNhKVugctkO14C4OgsHBjUeMHoO6luOTg" + }, + "idp": { + "client": "tdf-entity-resolution-service", + "legacy": true, + "realm": "tdf", + "secret": "5Byk7Hh6l0E1hJDZfF8CQbG9vqh2FeIe", + "url": "http://localhost:8888" + } +} diff --git a/policies/entitlements/in-kas.json b/policies/entitlements/in-kas.json new file mode 100644 index 000000000..8e9114b00 --- /dev/null +++ b/policies/entitlements/in-kas.json @@ -0,0 +1,280 @@ +{ + "attribute_mappings": { + "https://example.com/attr/attr1/value/value1": { + "attribute": { + "id": "6a261d68-0899-4e17-bb2f-124abba7c09c", + "namespace": { + "id": "8f1d8839-2851-4bf4-8bf4-5243dbfe517d", + "name": "example.com" + }, + "name": "attr1", + "rule": 2, + "values": [ + { + "id": "74babca6-016f-4f3e-a99b-4e46ea8d0fd8", + "value": "value1", + "fqn": "https://example.com/attr/attr1/value/value1", + "active": { + "value": true + } + }, + { + "id": "2fe8dea1-3555-498c-afe9-99724f35f3d3", + "value": "value2", + "active": { + "value": true + } + } + ], + "active": { + "value": true + }, + "metadata": {} + }, + "value": { + "id": "74babca6-016f-4f3e-a99b-4e46ea8d0fd8", + "value": "value1", + "fqn": "https://example.com/attr/attr1/value/value1", + "active": { + "value": true + }, + "subject_mappings": [ + { + "id": "9d06c757-06b9-4713-8fbd-5ef007b1afe2", + "subject_condition_set": { + "id": "eaf866c0-327f-4826-846a-5041c3c22f06", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "fave_sport", + "operator": 1, + "subject_external_values": [ + "futbol", + "soccer" + ] + }, + { + "subject_external_field": "fave_dessert", + "operator": 2, + "subject_external_values": [ + "ice_cream" + ] + } + ], + "boolean_operator": 2 + }, + { + "conditions": [ + { + "subject_external_field": "department", + "operator": 1, + "subject_external_values": [ + "engineering" + ] + }, + { + "subject_external_field": "role", + "operator": 2, + "subject_external_values": [ + "manager", + "director", + "vice_president" + ] + } + ], + "boolean_operator": 1 + } + ] + } + ] + }, + "actions": [ + { + "Value": { + "Standard": 2 + } + }, + { + "Value": { + "Custom": "custom_action_1" + } + } + ] + }, + { + "id": "3c623ede-df88-4906-8a78-ebdfacadcd57", + "subject_condition_set": { + "id": "3c623ede-df88-4906-8a78-ebdfacadcd57", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "some_field", + "operator": 1, + "subject_external_values": [ + "some_value" + ] + } + ], + "boolean_operator": 2 + } + ] + } + ] + }, + "actions": [ + { + "Value": { + "Standard": 1 + } + } + ] + }, + { + "id": "812fab35-9aa4-4e73-bf22-c96638d58ea4", + "subject_condition_set": { + "id": "b3903282-06f9-41a4-924a-7b8eb43dffe0", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "superhero_name", + "operator": 1, + "subject_external_values": [ + "thor", + "captain_america" + ] + }, + { + "subject_external_field": "superhero_group", + "operator": 1, + "subject_external_values": [ + "avengers" + ] + } + ], + "boolean_operator": 1 + } + ] + } + ] + }, + "actions": [ + { + "Value": { + "Standard": 1 + } + } + ] + } + ] + } + }, + "https://example.com/attr/attr1/value/value2": { + "attribute": { + "id": "6a261d68-0899-4e17-bb2f-124abba7c09c", + "namespace": { + "id": "8f1d8839-2851-4bf4-8bf4-5243dbfe517d", + "name": "example.com" + }, + "name": "attr1", + "rule": 2, + "values": [ + { + "id": "74babca6-016f-4f3e-a99b-4e46ea8d0fd8", + "value": "value1", + "active": { + "value": true + } + }, + { + "id": "2fe8dea1-3555-498c-afe9-99724f35f3d3", + "value": "value2", + "fqn": "https://example.com/attr/attr1/value/value2", + "active": { + "value": true + } + } + ], + "active": { + "value": true + }, + "metadata": {} + }, + "value": { + "id": "2fe8dea1-3555-498c-afe9-99724f35f3d3", + "value": "value2", + "fqn": "https://example.com/attr/attr1/value/value2", + "active": { + "value": true + }, + "subject_mappings": [ + { + "id": "e6a3f940-e24f-4383-8763-718a1a304948", + "subject_condition_set": { + "id": "798aacd2-abaf-4623-975e-3bb8ca43e318", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "department", + "operator": 1, + "subject_external_values": [ + "marketing", + "sales" + ] + }, + { + "subject_external_field": "role", + "operator": 1, + "subject_external_values": [ + "senior_vice_president", + "vice_president", + "director" + ] + } + ], + "boolean_operator": 1 + } + ] + } + ] + }, + "actions": [ + { + "Value": { + "Standard": 2 + } + }, + { + "Value": { + "Standard": 1 + } + } + ] + } + ] + } + } + }, + "entity": { + "id": "0", + "jwt": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqOW1hTVRaUDdBMG9qSVFYNkJiOTRIdVlYeW5fdC1RR0VJQjJ1akpheDI4In0.eyJleHAiOjE3MTExMTQ4NDAsImlhdCI6MTcxMTExNDU0MCwianRpIjoiMTA5ZjhkZDctNjczYy00NDI2LThhY2EtOTFkMjVlZjU1NGE3IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4ODg4L2F1dGgvcmVhbG1zL3RkZiIsInN1YiI6IjQ4ZGU2MzRkLWMyZjAtNDhiZS04NjYxLTM4NTQ3ODMyNDAzNiIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9wZW50ZGYiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIi8qIl0sInJlc291cmNlX2FjY2VzcyI6eyJvcGVudGRmIjp7InJvbGVzIjpbIndyaXRlciJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCBncm91cHMiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImNsaWVudEhvc3QiOiIxOTIuMTY4LjY1LjEiLCJncm91cHMiOlsiL2VuZ2luZWVyaW5nIiwiL3Byb2plY3R4Il0sInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1vcGVudGRmIiwiY2xpZW50QWRkcmVzcyI6IjE5Mi4xNjguNjUuMSIsImNsaWVudF9pZCI6Im9wZW50ZGYifQ.FxbPmw92rgIJus6kbqQO5TcFMtut0Y69pBKPqhLXYF5jqHYeo83p5J4CxZ2iUS04tdnw6bpKF-PC6V_M-O_NCEAlBeCLWbsU8ccWM4847tRIML1xRGQELedqUv8KsgDroYQwLpG6J5mf7I5oCEQ4RZR6Njq30BIK0XbvrTkdV3YB4e9l56WIK2j_W5_v4p2L4PzflCsqLkF4d8t8Ggjc3yXV8j_HL1HVDwPIzShNdaZJduT1Bw_j6K9dZqxhTCJEOmie0seZenKySif4ruc5kDdXj-ePpRJYLq_yD_ViVgZWLldjvGwuaqNhKVugctkO14C4OgsHBjUeMHoO6luOTg" + }, + "idp": { + "client": "tdf-entity-resolution-service", + "legacy": true, + "realm": "tdf", + "secret": "5Byk7Hh6l0E1hJDZfF8CQbG9vqh2FeIe", + "url": "http://localhost:8888" + } +} diff --git a/policies/entitlements/in-keycloak.json b/policies/entitlements/in-keycloak.json new file mode 100644 index 000000000..c4473d5cb --- /dev/null +++ b/policies/entitlements/in-keycloak.json @@ -0,0 +1,35 @@ + + + +[additional_props:{fields:{key:\"access\" value:{struct_value:{fields:{key:\"impersonate\" value:{bool_value:false}} fields:{key:\"manage\" value:{bool_value:false}} fields:{key:\"manageGroupMembership\" value:{bool_value:false}} fields:{key:\"mapRoles\" value:{bool_value:false}} fields:{key:\"view\" value:{bool_value:true}}}}} fields:{key:\"attributes\" value:{struct_value:{fields:{key:\"fave_sport\" value:{list_value:{values:{string_value:\"futbol\"}}}} fields:{key:\"superhero_name\" value:{list_value:{values:{string_value:\"thor\"}}}}}}} fields:{key:\"createdTimestamp\" value:{number_value:1.710339863913e+12}} fields:{key:\"disableableCredentialTypes\" value:{list_value:{}}} fields:{key:\"email\" value:{string_value:\"a@a.af\"}} fields:{key:\"emailVerified\" value:{bool_value:true}} fields:{key:\"enabled\" value:{bool_value:true}} fields:{key:\"firstName\" value:{string_value:\"A\"}} fields:{key:\"id\" value:{string_value:\"e3d95832-b539-43b6-b6ac-b9c781e46735\"}} fields:{key:\"lastName\" value:{string_value:\"F\"}} fields:{key:\"requiredActions\" value:{list_value:{}}} fields:{key:\"totp\" value:{bool_value:false}} fields:{key:\"username\" value:{string_value:\"a@a.af\"}}} original_id:\"e1\"] + + +map[entityRepresentations:[ + map[additionalProps:[ + map[ + access:map[ + impersonate:false + manage:false + manageGroupMembership:false + mapRoles:false + view:true + ] + attributes:map[ + fave_sport:[futbol] + superhero_name:[thor] + ] + createdTimestamp:1710339863913 + disableableCredentialTypes:[] + email:a@a.af emailVerified:true + enabled:true + firstName:A + id:e3d95832-b539-43b6-b6ac-b9c781e46735 + lastName:F + requiredActions:[] + totp:false + username:a@a.af + ] + ] originalId:e1 + ] +] +] diff --git a/policies/entitlements/in-simplified.json b/policies/entitlements/in-simplified.json new file mode 100644 index 000000000..aa8573509 --- /dev/null +++ b/policies/entitlements/in-simplified.json @@ -0,0 +1,60 @@ +{ + "entity": { + "claims": [ + "marketing", + "CoolTool", + "futbol" + ], + "id": "0" + }, + "subject_mappings": [ + { + "id": "1748761a-bd8c-4b23-8560-16ba7a181f19", + "attribute_value": { + "id": "c2140825-0969-44c9-8dd6-5d7e0a856b9c", + "value": "blue", + "active": { + "value": true + } + }, + "subject_condition_set": { + "id": "10d03422-7eae-43b9-ac3b-d10400171858", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "team_name", + "operator": 1, + "subject_external_values": [ + "CoolTool", + "RadService", + "ShinyThing" + ] + }, + { + "subject_external_field": "org_name", + "operator": 1, + "subject_external_values": [ + "marketing" + ] + } + ], + "boolean_operator": 1 + } + ] + } + ] + }, + "actions": [ + { + "Value": { + "Standard": 1 + } + } + ], + "metadata": {} + } + ] +} diff --git a/policies/entitlements/in.json b/policies/entitlements/in.json new file mode 100644 index 000000000..a29e897f0 --- /dev/null +++ b/policies/entitlements/in.json @@ -0,0 +1,222 @@ +{ + "attribute_mappings": { + "https://example.com/attr/attr1/value/value1": { + "attribute": { + "id": "6a261d68-0899-4e17-bb2f-124abba7c09c", + "namespace": { + "id": "8f1d8839-2851-4bf4-8bf4-5243dbfe517d", + "name": "example.com" + }, + "name": "attr1", + "rule": 2, + "values": [ + { + "id": "74babca6-016f-4f3e-a99b-4e46ea8d0fd8", + "value": "value1", + "fqn": "https://example.com/attr/attr1/value/value1", + "active": { + "value": true + } + }, + { + "id": "2fe8dea1-3555-498c-afe9-99724f35f3d3", + "value": "value2", + "members": [ + "0fd363db-27b1-4210-b77b-8c82fe044d41", + "532e5957-28f7-466d-91e2-493e9431cd83" + ], + "active": { + "value": true + } + } + ], + "active": { + "value": true + }, + "metadata": {} + }, + "value": { + "id": "74babca6-016f-4f3e-a99b-4e46ea8d0fd8", + "value": "value1", + "fqn": "https://example.com/attr/attr1/value/value1", + "active": { + "value": true + }, + "subject_mappings": [ + { + "id": "9d06c757-06b9-4713-8fbd-5ef007b1afe2", + "subject_condition_set": { + "id": "eaf866c0-327f-4826-846a-5041c3c22f06", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "group", + "operator": 1, + "subject_external_values": [ + "mygroup" + ] + }, + { + "subject_external_field": "role", + "operator": 2, + "subject_external_values": [ + "myrole" + ] + } + ], + "boolean_operator": 2 + }, + { + "conditions": [ + { + "subject_external_field": "department", + "operator": 1, + "subject_external_values": [ + "engineering" + ] + }, + { + "subject_external_field": "role", + "operator": 2, + "subject_external_values": [ + "manager", + "director", + "vice_president" + ] + } + ], + "boolean_operator": 1 + } + ] + } + ] + }, + "actions": [ + { + "Value": { + "Standard": 2 + } + }, + { + "Value": { + "Custom": "custom_action_1" + } + } + ] + }, + { + "id": "3c623ede-df88-4906-8a78-ebdfacadcd57", + "subject_condition_set": { + "id": "3c623ede-df88-4906-8a78-ebdfacadcd57", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "some_field", + "operator": 1, + "subject_external_values": [ + "some_value" + ] + } + ], + "boolean_operator": 2 + } + ] + } + ] + }, + "actions": [ + { + "Value": { + "Standard": 1 + } + } + ] + }, + { + "id": "812fab35-9aa4-4e73-bf22-c96638d58ea4", + "subject_condition_set": { + "id": "b3903282-06f9-41a4-924a-7b8eb43dffe0", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "superhero_name", + "operator": 1, + "subject_external_values": [ + "thor", + "captain_america" + ] + }, + { + "subject_external_field": "superhero_group", + "operator": 1, + "subject_external_values": [ + "avengers" + ] + } + ], + "boolean_operator": 1 + } + ] + } + ] + }, + "actions": [ + { + "Value": { + "Standard": 1 + } + } + ] + } + ] + } + } + }, + "entity": { + "id": "0" + }, + "entityRepresentations": [ + { + "additionalProps": [ + { + "access": { + "impersonate": false, + "manage": false, + "manageGroupMembership": false, + "mapRoles": false, + "view": true + }, + "attributes": { + "fave_sport": [ + "futbol" + ], + "superhero_name": [ + "thor" + ] + }, + "createdTimestamp": 1710339863913, + "disableableCredentialTypes": [], + "email": "a@a.af", + "emailVerified": true, + "enabled": true, + "firstName": "A", + "id": "e3d95832-b539-43b6-b6ac-b9c781e46735", + "lastName": "F", + "requiredActions": [], + "totp": false, + "username": "a@a.af" + } + ], + "originalId": "e1" + } + ] +} diff --git a/policies/entitlements/input-condition-test.json b/policies/entitlements/input-condition-test.json new file mode 100644 index 000000000..70dae18b0 --- /dev/null +++ b/policies/entitlements/input-condition-test.json @@ -0,0 +1,12 @@ +{ + "payload": { + "groups": ["groupA"] + }, + "condition": { + "subject_external_field": "groups", + "operator": 1, + "subject_external_values": [ + "groupA" + ] + } +} diff --git a/policies/entitlements/input-jwt.json b/policies/entitlements/input-jwt.json new file mode 100644 index 000000000..07d24f305 --- /dev/null +++ b/policies/entitlements/input-jwt.json @@ -0,0 +1,85 @@ +{ + "attribute_mappings": { + "https://brightonhealthclinic.org/attr/healthrecordtype/value/basicpatientinfo": { + "attribute": { + "id": "46fd500e-6839-4cc0-8b29-75665bf98e3a", + "namespace": { + "id": "f1f12166-8b22-47d6-829a-66e68b533eb2", + "name": "brightonhealthclinic.org" + }, + "name": "healthrecordtype", + "rule": 2, + "values": [ + { + "id": "356b7dd3-6abb-453c-8354-6915705fabcb", + "value": "basicpatientinfo", + "fqn": "https://brightonhealthclinic.org/attr/healthrecordtype/value/basicpatientinfo", + "active": { + "value": true + } + } + ], + "active": { + "value": true + }, + "metadata": {} + }, + "value": { + "id": "356b7dd3-6abb-453c-8354-6915705fabcb", + "value": "basicpatientinfo", + "fqn": "https://brightonhealthclinic.org/attr/healthrecordtype/value/basicpatientinfo", + "active": { + "value": true + }, + "subject_mappings": [ + { + "id": "5fb9f643-b7ea-4d53-8b23-f2b61a7ca38a", + "subject_condition_set": { + "id": "eb688795-fa0a-4014-8f4e-0f770c7bdab6", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "groups", + "operator": 1, + "subject_external_values": [ + "/medical" + ] + }, + { + "subject_external_field": "roles", + "operator": 1, + "subject_external_values": [ + "nurse", + "doctor" + ] + } + ], + "boolean_operator": 1 + } + ] + } + ], + "metadata": {} + }, + "actions": [ + { + "Value": { + "Standard": 1 + } + } + ], + "metadata": {} + } + ] + } + } + }, + "entity": { + "jwt": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI3bEN5Vlo4Zm9jaTBOSk1JclZGZ01jZFc1cGlRQVUwVV9KZ1BNZ2V1RFI0In0.eyJleHAiOjE3MTE1MTI2MjEsImlhdCI6MTcxMTQ3NjYyMSwiYXV0aF90aW1lIjoxNzExNDc2NjIxLCJqdGkiOiI2YzJlMjk0Mi1iNmE4LTQ2MTYtOTYwZi0xN2I2NzA3MDFiMzgiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODgvYXV0aC9yZWFsbXMvYnJpZ2h0b25oZWFsdGhjbGluaWMiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiNWJkOTllODgtMDkyMS00MzVjLTkxOTktYjZmZTZmYjU3MGY4IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWNjb3VudC1jb25zb2xlIiwibm9uY2UiOiJlYTMwMjk1OC1jMzgyLTQ0NGItYjNlNy0zODFhMjAzODE4YTciLCJzZXNzaW9uX3N0YXRlIjoiODQ0NjMxMjgtYjg0NS00ZDM1LTljM2MtNmJhNjFiZWEwZTYyIiwiYWNyIjoiMSIsInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwic2lkIjoiODQ0NjMxMjgtYjg0NS00ZDM1LTljM2MtNmJhNjFiZWEwZTYyIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJyb2xlcyI6Im51cnNlIiwibmFtZSI6Ik9saXZpYSBHcmVlbiIsImdyb3VwcyI6WyIvbWVkaWNhbCJdLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJvZ3JlZW4iLCJnaXZlbl9uYW1lIjoiT2xpdmlhIiwiZmFtaWx5X25hbWUiOiJHcmVlbiIsImVtYWlsIjoib2dyZWVuQGJyaWdodG9uaGVhbHRoY2xpbmljLm9yZyJ9.2MWhwiCdUChYPCvP-Q08Z1RvwO2a0Q3axR0agWAA-KToirV7bRdcMGJUDxEYzxCKmZYTcR3nNjeTF-WzK1ZsiasimsOVeqoCv8Q2YbA19KqxusaTrRtHcSLCVM8BWYGMBbrEzgn06MDYXjHVhAjAYytl20dImRjUPukFhG6XI8LIoX8sCRwb4gKb0fweIUaX6lj3TCvRw9NakIyN1Jotd3CBBZMFJ7fid1_HX8rWJ7HWZNdvILLMxv3euLO2IFFnnCaq6_etMCNeSzEqg3jei5gwOIcbPzERt2fpmrTX4OhFbUOJvI3MU-4uMJ68muKNCW0nlMEjUbYfCcjbSRUDjA", + "id": "" + }, + "idp": null +} diff --git a/policies/entitlements/input-simple.json b/policies/entitlements/input-simple.json new file mode 100644 index 000000000..78687e047 --- /dev/null +++ b/policies/entitlements/input-simple.json @@ -0,0 +1,134 @@ +{ + "attribute_mappings": { + "https://example.com/attr/attr1/value/value1": { + "attribute": { + "id": "6a261d68-0899-4e17-bb2f-124abba7c09c", + "namespace": { + "id": "8f1d8839-2851-4bf4-8bf4-5243dbfe517d", + "name": "example.com" + }, + "name": "attr1", + "rule": 2, + "values": [ + { + "id": "74babca6-016f-4f3e-a99b-4e46ea8d0fd8", + "value": "value1", + "fqn": "https://example.com/attr/attr1/value/value1", + "active": { + "value": true + } + }, + { + "id": "2fe8dea1-3555-498c-afe9-99724f35f3d3", + "value": "value2", + "members": [ + "0fd363db-27b1-4210-b77b-8c82fe044d41", + "532e5957-28f7-466d-91e2-493e9431cd83" + ], + "active": { + "value": true + } + } + ], + "active": { + "value": true + }, + "metadata": {} + }, + "value": { + "id": "74babca6-016f-4f3e-a99b-4e46ea8d0fd8", + "value": "value1", + "fqn": "https://example.com/attr/attr1/value/value1", + "active": { + "value": true + }, + "subject_mappings": [ + { + "id": "9d06c757-06b9-4713-8fbd-5ef007b1afe2", + "subject_condition_set": { + "id": "eaf866c0-327f-4826-846a-5041c3c22f06", + "subject_sets": [ + { + "condition_groups": [ + { + "conditions": [ + { + "subject_external_field": "fave_sport", + "operator": 1, + "subject_external_values": [ + "futbol" + ] + }, + { + "subject_external_field": "fave_dessert", + "operator": 1, + "subject_external_values": [ + "ice_cream" + ] + } + ], + "boolean_operator": 2 + } + ] + } + ] + }, + "actions": [ + { + "Value": { + "Standard": 2 + } + }, + { + "Value": { + "Custom": "custom_action_1" + } + } + ] + } + ] + } + } + }, + "entity": { + "id": "0" + }, + "entityRepresentations": [ + { + "additionalProps": [ + { + "access": { + "impersonate": false, + "manage": false, + "manageGroupMembership": false, + "mapRoles": false, + "view": true + }, + "attributes": { + "fave_sport": [ + "futbol" + ], + "fave_dessert": [ + "ice_cream" + ], + "superhero_name": [ + "thor" + ] + }, + "createdTimestamp": 1710339863913, + "disableableCredentialTypes": [], + "email": "a@a.af", + "emailVerified": true, + "enabled": true, + "firstName": "A", + "id": "e3d95832-b539-43b6-b6ac-b9c781e46735", + "lastName": "F", + "requiredActions": [], + "totp": false, + "username": "a@a.af" + } + ], + "originalId": "e1" + } + ] +} From 975c75a04221809e5281d86c6c91c43a818981c4 Mon Sep 17 00:00:00 2001 From: Paul Flynn Date: Thu, 5 Sep 2024 16:26:01 -0400 Subject: [PATCH 4/4] Update expected error message for AES-GCM tag size Adjusted the error message in nanotdf_test.go to specify valid tag sizes from 12 to 16 instead of 12 or 16, ensuring consistency with encryption standards. --- sdk/nanotdf_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/nanotdf_test.go b/sdk/nanotdf_test.go index c01e7a984..faebd3838 100644 --- a/sdk/nanotdf_test.go +++ b/sdk/nanotdf_test.go @@ -353,7 +353,7 @@ func TestCreateNanoTDF(t *testing.T) { publicKeyFetcher: MockECPublicKeyFetcher{ MockPublicKey: validMockPublicKey, }, - expectedError: errors.New("writeNanoTDFHeader failed:AesGcm.EncryptWithIVAndTagSize failed:invalid auth tag size, expects 12 or 16"), + expectedError: errors.New("writeNanoTDFHeader failed:AesGcm.EncryptWithIVAndTagSize failed:invalid auth tag size, must be 12 to 16"), }, { name: "Invalid Curve match P256-P384",