Skip to content

fix(sdk): emit spec-compliant key access in experimental/tdf and delegate Writer - #3944

Open
dmihalcik-virtru wants to merge 1 commit into
dspx-2604-14-type-aliasesfrom
dspx-2604-15-delegate-writer
Open

dmihalcik-virtru wants to merge 1 commit into
dspx-2604-14-type-aliasesfrom
dspx-2604-15-delegate-writer

Conversation

@dmihalcik-virtru

@dmihalcik-virtru dmihalcik-virtru commented Sep 1, 2026

Copy link
Copy Markdown
Member

Part 15 of 20 in the DSPX-2604 re-cut. Base branch: dspx-2604-14-type-aliases.

This stack replaces #3782 / #3865 / #3921, which stay open and untouched
until it lands. Nothing here is a rebase of those branches — the work was
re-cut from the ticket so each PR stands on its own.

Proposed Changes

The experimental writer built its own key access objects, and for EC KAS
keys it built them wrong in three ways at once. It set keyType
"eccWrapped", but service/kas/access/rewrap.go dispatches on the exact
string "ec-wrapped" and has no case for the other spelling. It derived
the wrapping key with HKDF and then XORed the DEK, where the spec and every
KAS expect AES-GCM under that derived key. And it omitted schemaVersion
from the KAO entirely. Any TDF this package produced against an EC KAS was
undecryptable, and nothing in the repo caught it because the package tested
its own output against its own expectations.

The fix is not a patch to that code but its deletion. key_access.go (-266)
goes away and Writer delegates to sdk.NewChunkedWriter, so key access
objects come from sdk.createKeyAccess -- the same code path
SDK.CreateTDF has always used and that the cross-SDK tests exercise. RSA,
EC, ML-KEM and hybrid wrapping now have exactly one implementation.
key_access_test.go (-652) goes with it; equivalent coverage against the
sdk functions landed earlier in this stack, so nothing is lost.

writer.go drops from 680 lines to ~292: Writer becomes its config plus
an inner sdk.ChunkedWriter and a finalized flag. Manifest assembly,
segment encryption, integrity hashing and assertion signing all move to the
one implementation. manifest.go sheds the calculateSignature copy and
the three constants that only its callers needed.

keysplit_adapter.go (+60) is why this is a delegation rather than a
rename. sdk.DefaultKeySplitter is single-KAS and ignores attributes;
keysplit.XORSplitter evaluates the full ABAC boolean expression and
XOR-splits the DEK across every KAS the resulting clauses require. The two
result shapes are field-identical, so the adapter is a straight copy. The
one structural mismatch is where the default KAS enters -- sdk passes it per
Split call, keysplit takes it at construction -- so the splitter is built
inside Split.

API changes callers will notice

Finalize now returns a single *FinalizeResult instead of
(finalBytes, manifest, error). Error values are aliases of their sdk
counterparts rather than copies, so errors.Is matches under either name.

WithSegments no longer requires a contiguous prefix starting at 0.
Indices may be sparse -- a caller mapping fixed index blocks onto S3
multipart uploads writes gaps by construction -- but must still name written
segments in ascending order and may only drop from the end, because that is
the order the payload is laid out in.

WithExcludeVersionFromManifest is deprecated. It was always a no-op: the
manifest builder never read the flag. Omitting schemaVersion is how a
reader is told the TDF predates 4.3.0, and such a reader then expects
hex-then-base64 signatures, which are decided per segment at write time,
long before Finalize sees the option. WithTargetMode sets both together
and is the replacement.

WithIntegrityAlgorithm and WithSegmentIntegrityAlgorithm no longer take
effect, and asking for anything but the default is now an error from
NewWriter rather than a silent substitution. #3940 (revised) removed the
corresponding knobs from sdk.ChunkedWriter, which emits an HS256 root and
GMAC segments unconditionally; an Option returns no error, so NewWriter
is the only place a request the delegate cannot satisfy can be caught.
WithIntegrityAlgorithm(RootHS256) and
WithSegmentIntegrityAlgorithm(SegmentGMAC) still succeed and are no-ops.

That moves this package's segment default from SegmentHS256 to
SegmentGMAC. The old default was never right here: HS256 segment hashes
only make sense over bytes that are not AEAD output, and every segment this
writer produces is AES-GCM ciphertext with a tag already computed over
exactly those bytes. A caller that took the default was paying for a second
MAC over the same data and writing a manifest that named it. NewWriter
now refuses SegmentHS256 outright with
sdk.ErrUnsupportedSegmentIntegrityAlgorithm, and RootIntegrityAlg(GMAC)
-- reachable by conversion even though the type names no such constant --
with sdk.ErrUnsupportedRootIntegrityAlgorithm. See #4030 for why a GMAC
root is unsound: the aggregate hash never passes through the AEAD, so the
"tag" is a copy of the last segment hash and forgeable with no key.

This package's "application/octet-stream" MIME default is preserved
independently of the sdk default. examples/cmd/benchmark_experimental.go,
the only non-test consumer in the repo, compiles unchanged.

Because this changes the KAS wire format for EC keys, it wants a cross-SDK
run before merge — but not against this branch. xtest drives the Go side
through otdfctl, which only reaches SDK.CreateTDF; nothing in the CLI
imports sdk/experimental/tdf, and CreateTDF does not delegate to the
chunked writer until #3946. A run pinned here exercises none of this. Run it
against #3946 (or the stack tip), and set otdfctl-ref to the same branch —
left at main the CLI is built against main's sdk/ and the run passes
vacuously:

gh workflow run xtest.yml --repo opentdf/tests --ref main
-f platform-ref=dspx-2604-17-createtdf-delegates
-f otdfctl-ref=dspx-2604-17-createtdf-delegates
-f java-ref=main -f js-ref=main

Checklist

  • I have added or updated unit tests
  • I have added or updated integration tests (if appropriate)
  • I have added or updated documentation

Testing Instructions

cd sdk && go test ./... -race
cd examples && go build ./...

This changes the KAS wire format for EC keys, so it wants a cross-SDK run
before merge. Pin it to #3946, not to this branch — that is the first point
where CreateTDF goes through the changed code, and otdfctl is the only
Go consumer xtest drives:

gh workflow run xtest.yml --repo opentdf/tests --ref main \
  -f platform-ref=dspx-2604-17-createtdf-delegates \
  -f otdfctl-ref=dspx-2604-17-createtdf-delegates \
  -f java-ref=main -f js-ref=main

otdfctl-ref must name the branch too: it defaults to main, which builds
the CLI against main's sdk/ and makes the run vacuous. Check the job label
reads go@<branch> rather than go@main.

The full DSPX-2604 stack — 20 PRs
# PR Based on
01 #3930 chore: bump go.work toolchain to go1.25.12 and simplify an rt_test condition main
02 #3931 feat(sdk): make the zipstream clock injectable for deterministic ZIP output main
03 #3932 fix(sdk): reject a zipstream write set that omits segment 0 #3931
04 #3933 fix(sdk): map ReadAt plaintext offsets from cumulative segment sizes main
05 #3934 chore(sdk): extract integrityAlgorithmString, createPolicyBinding, signAssertions main
06 #3935 chore(sdk): add direct tests for createKeyAccess, encryptMetadata and tdfSalt main
07 #3936 fix(sdk): fill each segment with io.ReadFull and size the buffer to the input main
08 #3937 chore(cli): move streaming IO helpers into pkg main
09 #3938 fix(cli): stream encrypt instead of buffering the whole payload #3937
10 #3939 fix(cli): stream decrypt and inspect instead of buffering #3938
11 #3940 feat(sdk): add a chunked segment writer (experimental) dspx-2604-base-11 = #3932 + #3934 + #3935
12 #3941 fix(sdk): stop GetManifest from splitting the key under the lock #3940
13 #3942 fix(sdk): reject a chunked split naming a KAS with no resolved public key #3941
14 #3943 chore(sdk): alias experimental/tdf manifest and assertion types #3942
15 #3944 fix(sdk): emit spec-compliant key access in experimental/tdf and delegate Writer #3943
16 #3945 feat(sdk): accept io.Reader in CreateTDF and drop the 64 GB payload cap #3936
17 #3946 chore(sdk): rewrite CreateTDF on top of the chunked writer dspx-2604-base-17 = #3944 + #3945
18 #3947 chore(sdk): drop dead TDFConfig fields and deprecate the TDFFormat enum #3946
19 #3948 fix(cli): drop the encrypt-side stdin spool dspx-2604-base-19 = #3947 + #3939
20 #3949 feat(sdk): graduate the chunked writer to stable API #3948

Reviewable in parallel right now, since they sit directly on main and depend on
nothing else: 01, 02, 04, 05, 06, 07, 08.

Why three PRs have a dspx-2604-base-* base. A GitHub PR takes one base branch,
but 11, 17 and 19 each build on more than one parent. The base-* branches are empty
merge commits that exist only to join those parents so the PR diff shows exactly its
own change and nothing else. They contain no code, have no PR of their own, and go
away once their parents land — retarget the child onto main at that point.

Wants a cross-SDK xtest run before merge: 15, 17 (and therefore 20). They touch
the KAS wire format. Dispatch it against 17 or 20, never 15 on its own: xtest drives
the Go side through otdfctl -> SDK.CreateTDF, and 17 is the first commit where
that call reaches the rewritten writer. Set otdfctl-ref to the same branch as
platform-ref -- it defaults to main, which builds the CLI against main's sdk/
and makes the run vacuous.

Red checks you may see are network flakes, not this stack. Four distinct ones hit
this batch and all clear on re-run: golangci-lint config verify timing out on
https://golangci-lint.run/.../golangci.v2.8.jsonschema.json (fails the whole go (<module>) job and fail-fast cancels its siblings), the bats installer getting a 403,
Docker Hub timing out on keycloak/keycloak:26.4, and buf reporting "the server
hosted at that remote is unavailable" while the Java SDK generates sources. The
govulncheck step also emits ##[error] annotations against the go1.25.11 stdlib, but
it is continue-on-error: true and never fails a job — 01 bumps the toolchain and
clears those annotations.

@dmihalcik-virtru
dmihalcik-virtru requested review from a team as code owners September 1, 2026 02:57
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 3e74a380-ac36-4819-95ff-1da8316fbfe0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The experimental TDF writer now wraps sdk.ChunkedWriter, uses an XOR key-splitting adapter, accepts GMAC segment integrity, and validates options during construction. Tests and documentation now reflect delegated state, sparse segment handling, manifest results, and concurrency behavior.

Changes

Experimental TDF writer

Layer / File(s) Summary
Writer delegation and key splitting
sdk/experimental/tdf/writer.go, sdk/experimental/tdf/keysplit_adapter.go, sdk/experimental/tdf/key_access.go, sdk/experimental/tdf/manifest.go
Writer delegates segment, finalization, and manifest operations to sdk.ChunkedWriter. The XOR splitter adapts multi-KAS results. The former local key-access implementation and tests were removed.
Writer options and integrity contracts
sdk/experimental/tdf/options.go
GMAC is the default and only supported segment integrity algorithm. Unsupported algorithms fail during NewWriter. Target modes and sparse ascending segment selections are documented.
Behavior and integration validation
sdk/experimental/tdf/*_test.go
Tests validate public errors, delegated segment results, finalized state, manifest algorithms, missing segments, ordering, split IDs, and manifest-based key verification.
Usage and architecture documentation
sdk/experimental/tdf/doc.go, sdk/experimental/tdf/example_test.go
Examples and package documentation describe result data assembly, GMAC integrity, the three-layer architecture, sparse segments, and concurrency rules.

Priority: ⬇️ Low

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix

Merge Risk: 🔵 Low · up to 31354

The code is broadly mergeable, but the tests and public examples should more clearly protect and explain complete TDF assembly and sparse segment selection.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 28 functions across 8 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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the two main changes: spec-compliant key access generation in experimental/tdf and delegation of Writer to the shared SDK implementation.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dspx-2604-15-delegate-writer

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 hops through chunks of data
GMAC tags shine in the morning dew
XOR keys split the path with care
The stable writer carries the load
Tests guard each segment in place

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

@github-actions github-actions Bot added comp:sdk A software development kit, including library, for client applications and inter-service communicati size/xl labels Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 246.07499ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 128.704592ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 422.416813ms
Throughput 236.73 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 45.067918623s
Average Latency 449.885817ms
Throughput 110.94 requests/second

@dmihalcik-virtru
dmihalcik-virtru force-pushed the dspx-2604-15-delegate-writer branch from 7971d6c to daa07d2 Compare September 1, 2026 03:30
@dmihalcik-virtru
dmihalcik-virtru force-pushed the dspx-2604-14-type-aliases branch from 0c00bb2 to a3447fc Compare September 1, 2026 03:30
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@dmihalcik-virtru dmihalcik-virtru changed the title fix(sdk/experimental/tdf): emit spec-compliant key access and delegate Writer fix(sdk): emit spec-compliant key access in experimental/tdf and delegate Writer Sep 1, 2026
@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 174.762983ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 96.855439ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 350.172921ms
Throughput 285.57 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 46.195470357s
Average Latency 461.047212ms
Throughput 108.24 requests/second

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 249.564333ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 135.051187ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 425.875861ms
Throughput 234.81 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 59.512971585s
Average Latency 593.791205ms
Throughput 84.02 requests/second

@dmihalcik-virtru
dmihalcik-virtru force-pushed the dspx-2604-15-delegate-writer branch from 898ee17 to 4294327 Compare September 15, 2026 23:57
@dmihalcik-virtru
dmihalcik-virtru force-pushed the dspx-2604-14-type-aliases branch from 2243a07 to 67cab04 Compare September 15, 2026 23:57
@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 253.061519ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 137.36586ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 430.402752ms
Throughput 232.34 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 58.377683505s
Average Latency 582.506838ms
Throughput 85.65 requests/second

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 223.771057ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 137.451071ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 412.536195ms
Throughput 242.40 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 1m0.90002289s
Average Latency 607.927983ms
Throughput 82.10 requests/second

@dmihalcik-virtru
dmihalcik-virtru force-pushed the dspx-2604-14-type-aliases branch from 67cab04 to 9de9059 Compare September 16, 2026 00:20
@dmihalcik-virtru
dmihalcik-virtru force-pushed the dspx-2604-15-delegate-writer branch from 4294327 to 525cf63 Compare September 16, 2026 00:20
@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 202.664542ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 112.221988ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 406.427758ms
Throughput 246.05 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 51.683737413s
Average Latency 515.605338ms
Throughput 96.74 requests/second

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 230.189001ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 129.267318ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 446.493405ms
Throughput 223.97 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 59.552301103s
Average Latency 593.970651ms
Throughput 83.96 requests/second

@dmihalcik-virtru
dmihalcik-virtru force-pushed the dspx-2604-15-delegate-writer branch from 525cf63 to b005207 Compare September 16, 2026 15:05
@dmihalcik-virtru
dmihalcik-virtru force-pushed the dspx-2604-14-type-aliases branch from 9de9059 to 8127c71 Compare September 16, 2026 15:05
@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 238.421329ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 130.845089ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 426.663385ms
Throughput 234.38 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 58.418968149s
Average Latency 581.703376ms
Throughput 85.59 requests/second

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 232.260103ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 144.771345ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 466.021156ms
Throughput 214.58 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 1m2.237633953s
Average Latency 620.995871ms
Throughput 80.34 requests/second

…gate Writer

The experimental writer built its own key access objects, and for EC KAS
keys it built them wrong in three ways at once. It set keyType
`"eccWrapped"`, but `service/kas/access/rewrap.go` dispatches on the exact
string `"ec-wrapped"` and has no case for the other spelling. It derived
the wrapping key with HKDF and then XORed the DEK, where the spec and every
KAS expect AES-GCM under that derived key. And it omitted `schemaVersion`
from the KAO entirely. Any TDF this package produced against an EC KAS was
undecryptable, and nothing in the repo caught it because the package tested
its own output against its own expectations.

The fix is not a patch to that code but its deletion. `key_access.go` (-266)
goes away and `Writer` delegates to `sdk.NewChunkedWriter`, so key access
objects come from `sdk.createKeyAccess` -- the same code path
`SDK.CreateTDF` has always used and that the cross-SDK tests exercise. RSA,
EC, ML-KEM and hybrid wrapping now have exactly one implementation.
`key_access_test.go` (-652) goes with it; equivalent coverage against the
sdk functions landed earlier in this stack, so nothing is lost.

`writer.go` drops from 680 lines to ~292: `Writer` becomes its config plus
an inner `sdk.ChunkedWriter` and a `finalized` flag. Manifest assembly,
segment encryption, integrity hashing and assertion signing all move to the
one implementation. `manifest.go` sheds the `calculateSignature` copy and
the three constants that only its callers needed.

`keysplit_adapter.go` (+60) is why this is a delegation rather than a
rename. `sdk.DefaultKeySplitter` is single-KAS and ignores attributes;
`keysplit.XORSplitter` evaluates the full ABAC boolean expression and
XOR-splits the DEK across every KAS the resulting clauses require. The two
result shapes are field-identical, so the adapter is a straight copy. The
one structural mismatch is where the default KAS enters -- sdk passes it per
`Split` call, keysplit takes it at construction -- so the splitter is built
inside `Split`.

API changes callers will notice

`Finalize` now returns a single `*FinalizeResult` instead of
`(finalBytes, manifest, error)`. Error values are aliases of their sdk
counterparts rather than copies, so `errors.Is` matches under either name.

`WithSegments` no longer requires a contiguous prefix starting at 0.
Indices may be sparse -- a caller mapping fixed index blocks onto S3
multipart uploads writes gaps by construction -- but must still name written
segments in ascending order and may only drop from the end, because that is
the order the payload is laid out in.

`WithExcludeVersionFromManifest` is deprecated. It was always a no-op: the
manifest builder never read the flag. Omitting `schemaVersion` is how a
reader is told the TDF predates 4.3.0, and such a reader then expects
hex-then-base64 signatures, which are decided per segment at write time,
long before Finalize sees the option. `WithTargetMode` sets both together
and is the replacement.

This package's `"application/octet-stream"` MIME default is preserved
independently of the sdk default. `examples/cmd/benchmark_experimental.go`,
the only non-test consumer in the repo, compiles unchanged.

Because this changes the KAS wire format for EC keys, it wants a cross-SDK
run before merge:

  gh workflow run xtest.yml --repo opentdf/tests --ref main \
    -f platform-ref=<branch> -f otdfctl-ref=main -f java-ref=main -f js-ref=main

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@dmihalcik-virtru
dmihalcik-virtru force-pushed the dspx-2604-15-delegate-writer branch from b005207 to 5b0fa96 Compare September 17, 2026 16:47
@dmihalcik-virtru
dmihalcik-virtru force-pushed the dspx-2604-14-type-aliases branch from 8127c71 to 1abae3e Compare September 17, 2026 16:47
@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 113.579962ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 62.824739ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 226.969182ms
Throughput 440.59 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 30.386731517s
Average Latency 303.114185ms
Throughput 164.55 requests/second

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 181.173726ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 91.879973ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 350.179317ms
Throughput 285.57 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 45.167491399s
Average Latency 450.187745ms
Throughput 110.70 requests/second

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Govulncheck found vulnerabilities ⚠️

The following modules have known vulnerabilities:

  • otdfctl
  • service
  • tests-bdd

See the workflow run for details.

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/xl

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant