Skip to content

feat(sdk): add opt-in hexless policy binding - #4232

Open
pflynn-virtru wants to merge 3 commits into
mainfrom
fix/policy-binding-sdk-optin
Open

pflynn-virtru wants to merge 3 commits into
mainfrom
fix/policy-binding-sdk-optin

Conversation

@pflynn-virtru

@pflynn-virtru pflynn-virtru commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Part of #3578. Adds an opt-in hexless policy binding: the spec's Base64(HMAC) form, without the hex layer of the legacy Base64(hex(HMAC)). The default output is unchanged.

Proposed Changes

The spec defines a key access object's policy binding hash as Base64(HMAC), but the Go SDK writes Base64(hex(HMAC)). KAS releases before #4223 can't verify the spec form, so switching the default would break decryption against every deployed KAS. Instead, this adds an opt-in:

  • WithHexlessPolicyBinding() (CreateTDF), WithChunkedHexlessPolicyBinding() (chunked writer), and WithHexlessPolicyBinding() in sdk/experimental/tdf write Base64(HMAC). The name describes the encoding rather than claiming compliance with a particular spec version. Without them, output is byte-for-byte the same as today.
  • A pre-4.3.0 WithTargetMode / WithChunkedTargetMode overrides the opt-in and keeps the legacy form.
  • When reading, the SDK re-encodes a 32-byte (hexless) binding to Base64(hex(HMAC)) in the rewrap request. Both forms carry the same HMAC, and every KAS verifies the legacy form, so this SDK can decrypt a hexless TDF against any KAS. Other bindings are forwarded unchanged for the KAS to judge.

#3597 (now a draft) is the follow-up that makes the spec form the default, once KAS versions with #4223 are broadly deployed.

Checklist

  • I have added or updated unit tests
  • I have added or updated integration tests (if appropriate)
  • I have added or updated documentation (godoc on the new options)

Testing Instructions

  • cd sdk && go test ./... -race: all packages pass, including experimental/tdf.
  • Known-answer vectors pin both encodings in TestCreatePolicyBinding. tdf_test.go, chunked_test.go and experimental/tdf/writer_test.go cover the default, the opt-in, and the opt-in overridden by a pre-4.3.0 target mode.
  • cd sdk && go test -run TestREADMECodeBlocks: passes.
  • golangci-lint run --new-from-rev=origin/main ./... in sdk: 0 issues.

Interop: opentdf/tests#648 checks that a KAS accepts a hexless binding by rewriting one into an otherwise normal TDF. It is expected to fail against platform versions without #4223.

Compatibility

🤖 Generated with Claude Code

@pflynn-virtru
pflynn-virtru requested review from a team as code owners October 15, 2026 19:29
@github-actions github-actions Bot added the comp:sdk A software development kit, including library, for client applications and inter-service communicati label Oct 15, 2026
@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 220d73a8-2274-4d6d-b7bf-91125f26f836
📥 Commits

Reviewing files that changed from the base of the PR and between e1afd30 and b7dfbb1.

📒 Files selected for processing: 11
  • sdk/chunked_options.go
  • sdk/chunked_test.go
  • sdk/chunked_writer.go
  • sdk/experimental/tdf/options.go
  • sdk/experimental/tdf/writer.go
  • sdk/experimental/tdf/writer_test.go
  • sdk/key_splitter.go
  • sdk/tdf.go
  • sdk/tdf_config.go
  • sdk/tdf_helpers_test.go
  • sdk/tdf_test.go
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 583.55086ms
Throughput 171.36 requests/second

TDF3 Benchmark Results

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 39.037258321s
Average Latency 389.509074ms
Throughput 128.08 requests/second

@pflynn-virtru
pflynn-virtru marked this pull request as draft October 15, 2026 19:44
@pflynn-virtru pflynn-virtru changed the title feat(sdk): add opt-in spec-compliant policy binding feat(sdk): add opt-in hexless policy binding Oct 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 425.931304ms
Throughput 234.78 requests/second

TDF3 Benchmark Results

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 45.425418793s
Average Latency 453.490268ms
Throughput 110.07 requests/second

pflynn-virtru and others added 3 commits October 19, 2026 13:00
…egacy KAS

The spec defines a key access object's policy binding hash as
Base64(HMAC). Every SDK writes Base64(hex(HMAC)) instead, and no KAS
released before the dual-accept fix can verify the spec's form: older
ones always hex-decode it, later ones compare an untrimmed decode
buffer. Flipping the writer outright breaks decryption against every
deployed KAS.

Keep the legacy form as the default and add WithSpecCompliantPolicyBinding
(and chunked and experimental/tdf equivalents) to write the spec's
form. A pre-4.3.0 target mode overrides it.

On read, re-encode a 32-byte binding to the legacy form in the rewrap
request. Every KAS verifies that form, so a TDF carrying the spec's
binding decrypts through this reader against any KAS. Older readers
forward the binding unchanged and still need a KAS with the fix, which
is why the default stays legacy until fixed KAS are broadly deployed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Paul Flynn <pflynn-virtru@users.noreply.github.com>
"Spec compliant" depends on which spec version the reader has in mind.
Name the option for what it changes instead: it drops the hex layer.

WithSpecCompliantPolicyBinding -> WithHexlessPolicyBinding
WithChunkedSpecCompliantPolicyBinding -> WithChunkedHexlessPolicyBinding

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Paul Flynn <pflynn-virtru@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Paul Flynn <pflynn-virtru@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Govulncheck found vulnerabilities ⚠️

The following modules have known vulnerabilities:

  • otdfctl
  • sdk
  • service
  • lib/fixtures
  • tests-bdd

See the workflow run for details.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:sdk A software development kit, including library, for client applications and inter-service communicati size/m

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant