diff --git a/lib/ocrypto/algorithm.go b/lib/ocrypto/algorithm.go new file mode 100644 index 0000000000..d5390d9349 --- /dev/null +++ b/lib/ocrypto/algorithm.go @@ -0,0 +1,50 @@ +package ocrypto + +// Algorithm identifiers for BaseTDF v4.4.0 Key Access Objects. +// These are the string values used in the "alg" field of KAOs. +const ( + AlgRSAOAEP = "RSA-OAEP" + AlgRSAOAEP256 = "RSA-OAEP-256" + AlgECDHHKDF = "ECDH-HKDF" + AlgMLKEM768 = "ML-KEM-768" + AlgMLKEM1024 = "ML-KEM-1024" + AlgHybridECDH = "X-ECDH-ML-KEM-768" +) + +// AlgForKeyType returns the BaseTDF algorithm identifier for the given KeyType. +// This maps internal key types to the explicit algorithm strings used in v4.4.0 KAOs. +func AlgForKeyType(kt KeyType) string { + switch kt { + case RSA2048Key, RSA4096Key: + return AlgRSAOAEP + case EC256Key, EC384Key, EC521Key: + return AlgECDHHKDF + default: + return string(kt) + } +} + +// KeyTypeForAlg returns the KeyType for a given BaseTDF algorithm identifier. +// Returns the algorithm string as a KeyType if no specific mapping exists. +func KeyTypeForAlg(alg string) KeyType { + switch alg { + case AlgRSAOAEP, AlgRSAOAEP256: + return RSA2048Key + case AlgECDHHKDF: + return EC256Key + default: + return KeyType(alg) + } +} + +// AlgForLegacyType maps the v4.3.0 "type" field values to algorithm identifiers. +func AlgForLegacyType(legacyType string) string { + switch legacyType { + case "wrapped": + return AlgRSAOAEP + case "ec-wrapped": + return AlgECDHHKDF + default: + return "" + } +} diff --git a/lib/ocrypto/algorithm_test.go b/lib/ocrypto/algorithm_test.go new file mode 100644 index 0000000000..42cdb4266f --- /dev/null +++ b/lib/ocrypto/algorithm_test.go @@ -0,0 +1,58 @@ +package ocrypto + +import "testing" + +func TestAlgForKeyType(t *testing.T) { + tests := []struct { + keyType KeyType + want string + }{ + {RSA2048Key, AlgRSAOAEP}, + {RSA4096Key, AlgRSAOAEP}, + {EC256Key, AlgECDHHKDF}, + {EC384Key, AlgECDHHKDF}, + {EC521Key, AlgECDHHKDF}, + {"", ""}, + {KeyType("unknown"), "unknown"}, + } + for _, tt := range tests { + if got := AlgForKeyType(tt.keyType); got != tt.want { + t.Errorf("AlgForKeyType(%q) = %q, want %q", tt.keyType, got, tt.want) + } + } +} + +func TestKeyTypeForAlg(t *testing.T) { + tests := []struct { + alg string + want KeyType + }{ + {AlgRSAOAEP, RSA2048Key}, + {AlgRSAOAEP256, RSA2048Key}, + {AlgECDHHKDF, EC256Key}, + {"", KeyType("")}, + {"unknown", KeyType("unknown")}, + } + for _, tt := range tests { + if got := KeyTypeForAlg(tt.alg); got != tt.want { + t.Errorf("KeyTypeForAlg(%q) = %q, want %q", tt.alg, got, tt.want) + } + } +} + +func TestAlgForLegacyType(t *testing.T) { + tests := []struct { + legacy string + want string + }{ + {"wrapped", AlgRSAOAEP}, + {"ec-wrapped", AlgECDHHKDF}, + {"", ""}, + {"unknown", ""}, + } + for _, tt := range tests { + if got := AlgForLegacyType(tt.legacy); got != tt.want { + t.Errorf("AlgForLegacyType(%q) = %q, want %q", tt.legacy, got, tt.want) + } + } +} diff --git a/sdk/codegen/runner/generate.go b/sdk/codegen/runner/generate.go index 211cedfdc4..8a24e0735d 100644 --- a/sdk/codegen/runner/generate.go +++ b/sdk/codegen/runner/generate.go @@ -177,12 +177,12 @@ func New%s%s%sConnectWrapper(httpClient connect.HTTPClient, baseURL string, opts func generateInterfaceType(interfaceName string, methods []string, packageName, prefix, suffix string) string { // Generate the interface type definition var builder strings.Builder - builder.WriteString(fmt.Sprintf(` + fmt.Fprintf(&builder, ` type %s%s%s interface { -`, prefix, interfaceName, suffix)) +`, prefix, interfaceName, suffix) for _, method := range methods { - builder.WriteString(fmt.Sprintf(` %s(ctx context.Context, req *%s.%sRequest) (*%s.%sResponse, error) -`, method, packageName, method, packageName, method)) + fmt.Fprintf(&builder, ` %s(ctx context.Context, req *%s.%sRequest) (*%s.%sResponse, error) +`, method, packageName, method, packageName, method) } builder.WriteString("}\n") return builder.String() diff --git a/sdk/kas_client.go b/sdk/kas_client.go index 3dbede6dde..bb2b1f7f3a 100644 --- a/sdk/kas_client.go +++ b/sdk/kas_client.go @@ -425,7 +425,7 @@ type KASKeyFetcher interface { func (s SDK) getPublicKey(ctx context.Context, kasurl, algorithm, kidToFind string) (*KASInfo, error) { if s.kasKeyCache != nil { - if cachedValue := s.kasKeyCache.get(kasurl, algorithm, kidToFind); nil != cachedValue { + if cachedValue := s.get(kasurl, algorithm, kidToFind); nil != cachedValue { return cachedValue, nil } } @@ -459,7 +459,7 @@ func (s SDK) getPublicKey(ctx context.Context, kasurl, algorithm, kidToFind stri PublicKey: resp.Msg.GetPublicKey(), } if s.kasKeyCache != nil { - s.kasKeyCache.store(ki) + s.store(ki) } return &ki, nil } diff --git a/sdk/kas_client_test.go b/sdk/kas_client_test.go index 3dfaae6bab..e00db5fdfe 100644 --- a/sdk/kas_client_test.go +++ b/sdk/kas_client_test.go @@ -141,8 +141,8 @@ func Test_StoreKASKeys(t *testing.T) { ) require.NoError(t, err) - assert.Nil(t, s.kasKeyCache.get("https://localhost:8080", "ec:secp256r1", "e1")) - assert.Nil(t, s.kasKeyCache.get("https://localhost:8080", "rsa:2048", "r1")) + assert.Nil(t, s.get("https://localhost:8080", "ec:secp256r1", "e1")) + assert.Nil(t, s.get("https://localhost:8080", "rsa:2048", "r1")) require.NoError(t, s.StoreKASKeys("https://localhost:8080", &policy.KasPublicKeySet{ Keys: []*policy.KasPublicKey{ @@ -150,10 +150,10 @@ func Test_StoreKASKeys(t *testing.T) { {Pem: "sample", Kid: "r1", Alg: policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048}, }, })) - assert.Nil(t, s.kasKeyCache.get("https://nowhere", "alg:unknown", "")) - assert.Nil(t, s.kasKeyCache.get("https://localhost:8080", "alg:unknown", "")) - ecKey := s.kasKeyCache.get("https://localhost:8080", "ec:secp256r1", "e1") - rsaKey := s.kasKeyCache.get("https://localhost:8080", "rsa:2048", "r1") + assert.Nil(t, s.get("https://nowhere", "alg:unknown", "")) + assert.Nil(t, s.get("https://localhost:8080", "alg:unknown", "")) + ecKey := s.get("https://localhost:8080", "ec:secp256r1", "e1") + rsaKey := s.get("https://localhost:8080", "rsa:2048", "r1") require.NotNil(t, ecKey) require.Equal(t, "e1", ecKey.KID) require.NotNil(t, rsaKey) diff --git a/sdk/manifest.go b/sdk/manifest.go index fc3034fddc..3401ae72a4 100644 --- a/sdk/manifest.go +++ b/sdk/manifest.go @@ -1,5 +1,11 @@ package sdk +import ( + "encoding/json" + + "github.com/opentdf/platform/lib/ocrypto" +) + type Segment struct { Hash string `json:"hash"` Size int64 `json:"segmentSize"` @@ -20,16 +26,33 @@ type IntegrityInformation struct { } type KeyAccess struct { - KeyType string `json:"type"` - KasURL string `json:"url"` - Protocol string `json:"protocol"` - WrappedKey string `json:"wrappedKey"` - PolicyBinding interface{} `json:"policyBinding"` - EncryptedMetadata string `json:"encryptedMetadata,omitempty"` - KID string `json:"kid,omitempty"` - SplitID string `json:"sid,omitempty"` - SchemaVersion string `json:"schemaVersion,omitempty"` - EphemeralPublicKey string `json:"ephemeralPublicKey,omitempty"` + KeyType string `json:"type"` + Algorithm string `json:"alg,omitempty"` + KasURL string `json:"url"` + Protocol string `json:"protocol"` + WrappedKey string `json:"wrappedKey"` + PolicyBinding any `json:"policyBinding"` + EncryptedMetadata string `json:"encryptedMetadata,omitempty"` + KID string `json:"kid,omitempty"` + SplitID string `json:"sid,omitempty"` + SchemaVersion string `json:"schemaVersion,omitempty"` + EphemeralPublicKey string `json:"ephemeralPublicKey,omitempty"` +} + +func (ka *KeyAccess) UnmarshalJSON(data []byte) error { + // Use an alias to avoid infinite recursion + type keyAccessAlias KeyAccess + var raw keyAccessAlias + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + *ka = KeyAccess(raw) + + // If Algorithm not set but KeyType is, infer algorithm from legacy type + if ka.Algorithm == "" && ka.KeyType != "" { + ka.Algorithm = ocrypto.AlgForLegacyType(ka.KeyType) + } + return nil } type PolicyBinding struct { diff --git a/sdk/manifest_test.go b/sdk/manifest_test.go new file mode 100644 index 0000000000..dfdfde70eb --- /dev/null +++ b/sdk/manifest_test.go @@ -0,0 +1,116 @@ +package sdk + +import ( + "encoding/json" + "testing" +) + +func TestKeyAccessUnmarshalJSON_V43Compat(t *testing.T) { + tests := []struct { + name string + input string + wantKeyType string + wantAlg string + }{ + { + name: "v4.3 wrapped infers RSA-OAEP", + input: `{"type":"wrapped","url":"https://kas.example.com","protocol":"kas","wrappedKey":"abc"}`, + wantKeyType: "wrapped", + wantAlg: "RSA-OAEP", + }, + { + name: "v4.3 ec-wrapped infers ECDH-HKDF", + input: `{"type":"ec-wrapped","url":"https://kas.example.com","protocol":"kas","wrappedKey":"abc"}`, + wantKeyType: "ec-wrapped", + wantAlg: "ECDH-HKDF", + }, + { + name: "v4.4 explicit alg preserved", + input: `{"type":"wrapped","alg":"RSA-OAEP-256","url":"https://kas.example.com","protocol":"kas","wrappedKey":"abc"}`, + wantKeyType: "wrapped", + wantAlg: "RSA-OAEP-256", + }, + { + name: "v4.4 ML-KEM-768 preserved", + input: `{"type":"","alg":"ML-KEM-768","url":"https://kas.example.com","wrappedKey":"abc"}`, + wantKeyType: "", + wantAlg: "ML-KEM-768", + }, + { + name: "unknown type does not infer alg", + input: `{"type":"remote","url":"https://kas.example.com","wrappedKey":"abc"}`, + wantKeyType: "remote", + wantAlg: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var ka KeyAccess + if err := json.Unmarshal([]byte(tt.input), &ka); err != nil { + t.Fatalf("UnmarshalJSON() error = %v", err) + } + if ka.KeyType != tt.wantKeyType { + t.Errorf("KeyType = %q, want %q", ka.KeyType, tt.wantKeyType) + } + if ka.Algorithm != tt.wantAlg { + t.Errorf("Algorithm = %q, want %q", ka.Algorithm, tt.wantAlg) + } + }) + } +} + +func TestKeyAccessMarshalJSON_V44Fields(t *testing.T) { + ka := KeyAccess{ + KeyType: "wrapped", + Algorithm: "RSA-OAEP", + KasURL: "https://kas.example.com", + Protocol: "kas", + WrappedKey: "abc", + PolicyBinding: PolicyBinding{Alg: "HS256", Hash: "def"}, + KID: "k1", + SplitID: "s1", + } + + data, err := json.Marshal(ka) + if err != nil { + t.Fatalf("Marshal() error = %v", err) + } + + var roundTrip KeyAccess + if err := json.Unmarshal(data, &roundTrip); err != nil { + t.Fatalf("Unmarshal() error = %v", err) + } + if roundTrip.Algorithm != "RSA-OAEP" { + t.Errorf("Algorithm = %q, want %q", roundTrip.Algorithm, "RSA-OAEP") + } + if roundTrip.KID != "k1" { + t.Errorf("KID = %q, want %q", roundTrip.KID, "k1") + } + if roundTrip.SplitID != "s1" { + t.Errorf("SplitID = %q, want %q", roundTrip.SplitID, "s1") + } +} + +func TestKeyAccessMarshalJSON_OmitEmptyAlg(t *testing.T) { + ka := KeyAccess{ + KeyType: "wrapped", + KasURL: "https://kas.example.com", + Protocol: "kas", + WrappedKey: "abc", + PolicyBinding: "hash", + } + + data, err := json.Marshal(ka) + if err != nil { + t.Fatalf("Marshal() error = %v", err) + } + + // Verify "alg" is not present when empty + var raw map[string]any + if err := json.Unmarshal(data, &raw); err != nil { + t.Fatalf("Unmarshal() error = %v", err) + } + if _, ok := raw["alg"]; ok { + t.Error("expected alg field to be omitted when empty") + } +} diff --git a/sdk/sdk.go b/sdk/sdk.go index ed0bc9e17a..b45339b6e2 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -494,7 +494,7 @@ func getTokenEndpoint(c config) (string, error) { // so only store the most recent known key per url & algorithm pair. func (s *SDK) StoreKASKeys(url string, keys *policy.KasPublicKeySet) error { for _, key := range keys.GetKeys() { - s.kasKeyCache.store(KASInfo{ + s.store(KASInfo{ URL: url, PublicKey: key.GetPem(), KID: key.GetKid(), diff --git a/sdk/tdf.go b/sdk/tdf.go index 753b168830..7d25ad5079 100644 --- a/sdk/tdf.go +++ b/sdk/tdf.go @@ -586,8 +586,15 @@ func (s SDK) prepareManifest(ctx context.Context, t *TDFObject, tdfConfig TDFCon symKeys = append(symKeys, symKey) // policy binding - policyBindingHash := hex.EncodeToString(ocrypto.CalculateSHA256Hmac(symKey, base64PolicyObject)) - pbstring := string(ocrypto.Base64Encode([]byte(policyBindingHash))) + hmacBytes := ocrypto.CalculateSHA256Hmac(symKey, base64PolicyObject) + var pbstring string + if tdfConfig.useHex { + // Legacy format: hex encode then base64 + pbstring = string(ocrypto.Base64Encode([]byte(hex.EncodeToString(hmacBytes)))) + } else { + // v4.4.0 format: direct base64 of HMAC bytes + pbstring = string(ocrypto.Base64Encode(hmacBytes)) + } policyBinding := PolicyBinding{ Alg: "HS256", Hash: pbstring, @@ -662,8 +669,10 @@ func encryptMetadata(symKey []byte, metaData string) (string, error) { } func createKeyAccess(kasInfo KASInfo, symKey []byte, policyBinding PolicyBinding, encryptedMetadata, splitID string) (KeyAccess, error) { + ktype := ocrypto.KeyType(kasInfo.Algorithm) keyAccess := KeyAccess{ KeyType: kWrapped, + Algorithm: ocrypto.AlgForKeyType(ktype), KasURL: kasInfo.URL, KID: kasInfo.KID, Protocol: kKasProtocol, @@ -673,7 +682,6 @@ func createKeyAccess(kasInfo KASInfo, symKey []byte, policyBinding PolicyBinding SchemaVersion: keyAccessSchemaVersion, } - ktype := ocrypto.KeyType(kasInfo.Algorithm) if ocrypto.IsECKeyType(ktype) { mode, err := ocrypto.ECKeyTypeToMode(ktype) if err != nil { diff --git a/sdk/tdf_test.go b/sdk/tdf_test.go index d21e98161e..ad8b91d65c 100644 --- a/sdk/tdf_test.go +++ b/sdk/tdf_test.go @@ -451,7 +451,7 @@ func (s *TDFSuite) Test_SimpleTDF() { if config.useHex { s.InDelta(float64(expectedTdfSizeWithHex), float64(tdfObj.size), 64.0) } else { - s.InDelta(float64(expectedTdfSize), float64(tdfObj.size), 64.0) + s.InDelta(float64(expectedTdfSize), float64(tdfObj.size), 80.0) } // test meta data and build meta data @@ -761,7 +761,7 @@ func (s *TDFSuite) Test_TDFWithAssertion() { }, verifiers: nil, disableAssertionVerification: false, - expectedSize: 2689, + expectedSize: 2656, }, { assertions: []AssertionConfig{ @@ -824,7 +824,7 @@ func (s *TDFSuite) Test_TDFWithAssertion() { DefaultKey: defaultKey, }, disableAssertionVerification: false, - expectedSize: 2689, + expectedSize: 2656, }, { assertions: []AssertionConfig{ @@ -873,7 +873,7 @@ func (s *TDFSuite) Test_TDFWithAssertion() { }, }, disableAssertionVerification: false, - expectedSize: 2988, + expectedSize: 2955, }, { assertions: []AssertionConfig{ @@ -913,7 +913,7 @@ func (s *TDFSuite) Test_TDFWithAssertion() { }, }, disableAssertionVerification: false, - expectedSize: 2689, + expectedSize: 2656, }, { assertions: []AssertionConfig{ @@ -930,7 +930,7 @@ func (s *TDFSuite) Test_TDFWithAssertion() { }, }, disableAssertionVerification: true, - expectedSize: 2180, + expectedSize: 2147, }, } { expectedTdfSize := test.expectedSize @@ -1167,7 +1167,7 @@ func (s *TDFSuite) Test_TDFWithAssertionNegativeTests() { SigningKey: defaultKey, }, }, - expectedSize: 2689, + expectedSize: 2656, }, { assertions: []AssertionConfig{ @@ -1215,7 +1215,7 @@ func (s *TDFSuite) Test_TDFWithAssertionNegativeTests() { }, }, }, - expectedSize: 2988, + expectedSize: 2955, }, { assertions: []AssertionConfig{ @@ -1249,7 +1249,7 @@ func (s *TDFSuite) Test_TDFWithAssertionNegativeTests() { verifiers: &AssertionVerificationKeys{ DefaultKey: defaultKey, }, - expectedSize: 2689, + expectedSize: 2656, }, } { expectedTdfSize := test.expectedSize @@ -1868,7 +1868,7 @@ func (s *TDFSuite) Test_KeySplits() { { n: "shared", fileSize: 5, - tdfFileSize: 2759, + tdfFileSize: 2635, checksum: "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2", splitPlan: []keySplitStep{ {KAS: s.kasTestURLLookup["https://a.kas/"], SplitID: "a"}, @@ -1879,7 +1879,7 @@ func (s *TDFSuite) Test_KeySplits() { { n: "split", fileSize: 5, - tdfFileSize: 2759, + tdfFileSize: 2635, checksum: "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2", splitPlan: []keySplitStep{ {KAS: s.kasTestURLLookup["https://a.kas/"], SplitID: "a"}, @@ -1890,7 +1890,7 @@ func (s *TDFSuite) Test_KeySplits() { { n: "mixture", fileSize: 5, - tdfFileSize: 3351, + tdfFileSize: 3191, checksum: "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2", splitPlan: []keySplitStep{ {KAS: s.kasTestURLLookup["https://a.kas/"], SplitID: "a"}, @@ -2377,7 +2377,7 @@ func (s *TDFSuite) Test_Autoconfigure() { _ = os.Remove(plaintTextFileName) _ = os.Remove(tdfFileName) }() - s.sdk.kasKeyCache.store(KASInfo{}) + s.sdk.store(KASInfo{}) // test encrypt tdo := s.testEncrypt(s.sdk, []TDFOption{WithKasInformation(kasInfoList...)}, plaintTextFileName, tdfFileName, test) diff --git a/sdk/version.go b/sdk/version.go index 27b9f9e51c..2b070891fa 100644 --- a/sdk/version.go +++ b/sdk/version.go @@ -4,7 +4,7 @@ const ( // The latest version of TDF Spec currently targeted by the SDK. // By default, new files will conform to this version of the spec // and, where possible, older versions will still be readable. - TDFSpecVersion = "4.3.0" + TDFSpecVersion = "4.4.0" // The three-part semantic version number of this SDK Version = "0.12.0" // x-release-please-version diff --git a/service/kas/access/accessPdp.go b/service/kas/access/accessPdp.go index 1253bfe765..4b096da090 100644 --- a/service/kas/access/accessPdp.go +++ b/service/kas/access/accessPdp.go @@ -34,10 +34,6 @@ func (p *Provider) canAccess(ctx context.Context, token *entity.Token, policies var resources []*authzV2.Resource idPolicyMap := make(map[string]*Policy) for i, policy := range policies { - if len(policy.Body.Dissem) > 0 { - // TODO: Move dissems check to the getdecisions endpoint - p.Logger.Error("dissems check is not enabled in v2 platform kas") - } if len(policy.Body.DataAttributes) > 0 { id := "rewrap-" + strconv.Itoa(i) attrValueFqns := make([]string, len(policy.Body.DataAttributes)) diff --git a/service/kas/access/rewrap.go b/service/kas/access/rewrap.go index 274e09e256..5c2cc0dca7 100644 --- a/service/kas/access/rewrap.go +++ b/service/kas/access/rewrap.go @@ -774,7 +774,17 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned // Check if policy binding is nil if kao.GetKeyAccessObject().GetPolicyBinding() == nil { p.Logger.WarnContext(ctx, "policy binding is nil", slog.String("kao_id", kao.GetKeyAccessObjectId())) - failedKAORewrap(results, kao, err400("missing policy binding")) + failedKAORewrap(results, kao, err403("forbidden")) + continue + } + + // Validate policyBinding.alg — reject unknown values + bindingAlg := kao.GetKeyAccessObject().GetPolicyBinding().GetAlgorithm() + if bindingAlg != "" && bindingAlg != "HS256" { + p.Logger.WarnContext(ctx, "unsupported policy binding algorithm", + slog.String("alg", bindingAlg), + slog.String("kao_id", kao.GetKeyAccessObjectId())) + failedKAORewrap(results, kao, err403("forbidden")) continue } @@ -798,9 +808,10 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned } // Verify policy binding using the UnwrappedKeyData interface + // Use uniform 403 for binding failures to prevent oracle attacks (BaseTDF-SEC) if err := dek.VerifyBinding(ctx, []byte(req.GetPolicy().GetBody()), policyBinding); err != nil { p.Logger.WarnContext(ctx, "failure to verify policy binding", slog.Any("error", err)) - failedKAORewrap(results, kao, err400("bad request")) + failedKAORewrap(results, kao, err403("forbidden")) continue } @@ -899,6 +910,26 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew return "", results, nil } + // Enforce dissemination list: if dissem is non-empty, entity must be in the list + for i := range pdpAccessResults { + policy := pdpAccessResults[i].Policy + if pdpAccessResults[i].Access && len(policy.Body.Dissem) > 0 { + found := false + for _, d := range policy.Body.Dissem { + if strings.EqualFold(d, entityInfo.EntityID) { + found = true + break + } + } + if !found { + p.Logger.InfoContext(ctx, "entity not in dissemination list", + slog.String("entity_id", entityInfo.EntityID), + slog.String("policy_uuid", policy.UUID.String())) + pdpAccessResults[i].Access = false + } + } + } + asymEncrypt, err := ocrypto.FromPublicPEMWithSalt(clientPublicKey, security.TDFSalt(), nil) if err != nil { p.Logger.WarnContext(ctx, "ocrypto.NewAsymEncryption", slog.Any("error", err))