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

Summary by CodeRabbit

  • New Features
    • Added an opt-in hexless policy-binding format using Base64-encoded HMAC values.
    • Added support for enabling this format in standard and experimental TDF writers.
  • Compatibility
    • Existing legacy policy-binding encoding remains the default.
    • Older target modes continue to enforce legacy encoding, regardless of option order.
    • Rewrap operations normalize supported policy-binding values for legacy compatibility while preserving invalid or unexpected values unchanged.

@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

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: facbc947-872e-419f-b8a9-f5734b70c914
📥 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

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

The SDK adds an opt-in hexless policy-binding format, Base64(HMAC), alongside the existing Base64(hex(HMAC)) format. Pre-4.3.0 target modes retain legacy encoding. Rewrap requests convert valid raw HMAC bindings to the legacy representation.

Changes: Policy-binding encoding

Layer / File(s) Summary
Configure and wire hexless bindings
sdk/chunked_options.go, sdk/chunked_writer.go, sdk/tdf_config.go, sdk/experimental/tdf/options.go, sdk/experimental/tdf/writer.go
Writer options and configuration now expose the hexless policy-binding setting. The experimental writer passes the setting to the chunked writer. Pre-4.3.0 target modes retain legacy encoding.
Generate policy bindings
sdk/key_splitter.go, sdk/tdf.go, sdk/tdf_helpers_test.go, sdk/chunked_test.go, sdk/tdf_test.go, sdk/experimental/tdf/writer_test.go
Key-access resolution passes the encoding setting to policy-binding generation. Tests cover legacy and hexless output, target-mode precedence, and generated TDFs.
Normalize bindings for rewrap
sdk/tdf.go, sdk/tdf_helpers_test.go
Rewrap requests convert valid Base64-encoded 32-byte HMAC values to the legacy representation. Tests cover conversion and passthrough cases.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant WriterConfig
  participant NewWriter
  participant ChunkedWriter
  participant splitterKeyAccess
  participant createPolicyBinding
  WriterConfig->>NewWriter: hexlessPolicyBinding setting
  NewWriter->>ChunkedWriter: hexless writer option
  ChunkedWriter->>splitterKeyAccess: encoding configuration
  splitterKeyAccess->>createPolicyBinding: resolve with hexless setting
  createPolicyBinding-->>splitterKeyAccess: Base64(HMAC) or Base64(hex(HMAC))
Loading

Suggested reviewers: dmihalcik-virtru

Merge Risk: ⚪ Minimal · up to b7dfb

The reviewed binding-encoding paths show no outstanding issue that should block merging after normal checks.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: adding opt-in hexless policy binding support to the SDK.
Docstring Coverage Passed Docstring coverage is 88.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 11 files.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • 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

A rabbit checks the bindings twice,
As hashes hop from old to nice.
Raw bytes ride in Base64,
Old target modes keep their way.
A rewrap turns the format back,
Then bounds away along the track.

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.

@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 351.482268ms
Throughput 284.51 requests/second

TDF3 Benchmark Results

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 35.510913969s
Average Latency 354.208636ms
Throughput 140.80 requests/second

@pflynn-virtru

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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