Skip to content

fix(sdk): allow the same KAS to wrap the same split (DSPX-3379)#967

Merged
dmihalcik-virtru merged 2 commits into
mainfrom
DSPX-3379-multisplit-allowed
Jul 16, 2026
Merged

fix(sdk): allow the same KAS to wrap the same split (DSPX-3379)#967
dmihalcik-virtru merged 2 commits into
mainfrom
DSPX-3379-multisplit-allowed

Conversation

@dmihalcik-virtru

@dmihalcik-virtru dmihalcik-virtru commented Jul 9, 2026

Copy link
Copy Markdown
Member

What

When a TDF's key-access-object (KAO) array has two entries with the same split
id and the same KAS URI
, the web-sdk refused to decrypt, throwing
Unable to decrypt: Multiple keys detected for Key Access Server [...].

This treats the KAOs for a split as a disjunction of alternatives (any one
succeeding unwraps the split) instead of a URL-keyed map that can hold at most
one KAO per KAS — matching how the split is already consumed downstream
(anyPool) and how go-sdk / java-sdk already behave.

Changes (lib/tdf3/src/tdf.ts)

  • splitLookupTableFactory now returns Record<splitId, KeyAccessObject[]> and
    no longer throws on a repeated (splitId, kasUrl); every allowed KAO is kept
    as an alternative. Disallowed-KAS accounting (UnsafeUrlError) is unchanged.
  • unwrapKey feeds each alternative to anyPool with a unique url#kid#index
    key so duplicate KAS entries are each tried until one succeeds.

Why (DSPX-3379)

The same KAS wrapping the same split is legitimate — an authoring mistake (same
key used twice) or intentional multiple keys on one KAS. go/java read these
files fine; the web-sdk (e.g. secure viewer) failed. Expected: decrypt (or
permission-denied) exactly as if the copies lived on distinct URIs.

Tests

  • Unit: splitLookupTableFactory keeps duplicate (sid, kas) and
    same-KAS-different-KID entries as alternatives.
  • In-process decrypt path: a splitPlan with a repeated kas+sid produces a
    duplicate-KAO TDF that decrypts via the full unwrap/anyPool path.
  • Full mocha suite passes (334 passing).
  • Cross-SDK: test_tdf_with_duplicate_kao_same_kas (chore(xtest): cross-SDK test for duplicate KAO on same KAS+split (DSPX-3379) tests#555) — CI is
    green for go, java, and this branch's js.

Spec: spec/DSPX-3379.md.

Depends on / validated by: opentdf/tests#555

Summary by CodeRabbit

  • Bug Fixes

    • Encryption and decryption now support multiple key access alternatives from the same KAS for a single split.
    • Duplicate key access entries are preserved instead of being rejected.
    • Rewrapping correctly handles multiple alternatives, including entries with different key IDs.
  • Tests

    • Added coverage confirming streams decrypt successfully when the same KAS wraps a split more than once.

@dmihalcik-virtru
dmihalcik-virtru requested a review from a team as a code owner July 9, 2026 17:37
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: feab845a-b318-4901-a55e-e9ba30785990

📥 Commits

Reviewing files that changed from the base of the PR and between 0cc6e33 and fab441f.

📒 Files selected for processing (3)
  • lib/tdf3/src/tdf.ts
  • lib/tests/mocha/encrypt-decrypt.spec.ts
  • lib/tests/mocha/unit/tdf.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/tests/mocha/unit/tdf.spec.ts

📝 Walkthrough

Walkthrough

splitLookupTableFactory now preserves multiple KeyAccessObjects per split. unwrapKey processes each alternative independently, and unit and integration tests verify duplicate KAO handling during encryption and decryption.

Changes

Duplicate KAO support

Layer / File(s) Summary
Split lookup data model
lib/tdf3/src/tdf.ts
Maps each split ID to an array of allowed KeyAccessObjects and retains duplicate KAS entries.
Unwrap dispatch
lib/tdf3/src/tdf.ts
Validates potential arrays and creates distinct rewrap attempts using URL, key ID, and index.
Duplicate handling tests
lib/tests/mocha/unit/tdf.spec.ts, lib/tests/mocha/encrypt-decrypt.spec.ts
Updates array-based expectations and verifies decryption when the same KAS wraps a split twice.

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

Sequence Diagram(s)

sequenceDiagram
  participant Encryptor
  participant Manifest
  participant unwrapKey
  participant KAS

  Encryptor->>Manifest: Store repeated KAOs for one split
  Manifest->>unwrapKey: Provide keyAccess entries
  unwrapKey->>unwrapKey: Build and iterate KAO alternatives
  loop Each alternative
    unwrapKey->>KAS: Send rewrap request
    KAS-->>unwrapKey: Return rewrapped key
  end
  unwrapKey-->>Encryptor: Return decrypted plaintext
Loading

Possibly related PRs

  • opentdf/web-sdk#952: Updates duplicate KeyAccessObject error text in the same lookup and test areas.

Suggested reviewers: eugenioenko

Poem

I’m a rabbit with KAOs in a row,
No duplicate hops get told to go.
Each split keeps its keys bright,
Rewraps race through the night,
And plaintext blooms where carrots grow.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the core fix: allowing the same KAS to wrap the same split without rejecting duplicates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch DSPX-3379-multisplit-allowed

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request resolves DSPX-3379 by allowing the same Key Access Server (KAS) to wrap the same split multiple times. It refactors splitLookupTableFactory to return a list of alternative Key Access Objects (KAOs) instead of throwing on duplicates, and updates unwrapKey to handle these duplicates as distinct alternatives. Feedback on the changes highlights a bug in splitLookupTableFactory where accessibleSplits does not normalize undefined sid values to empty strings, which can bypass safety checks. Additionally, a formatting improvement for a stack trace in the markdown specification file was suggested.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread lib/tdf3/src/tdf.ts
Comment thread spec/DSPX-3379.md Outdated
@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-3379-multisplit-allowed branch from 0cc6e33 to d370256 Compare July 10, 2026 14:54
@dmihalcik-virtru dmihalcik-virtru changed the title fix(tdf3): allow the same KAS to wrap the same split (DSPX-3379) fix(sdk): allow the same KAS to wrap the same split (DSPX-3379) Jul 10, 2026
@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-3379-multisplit-allowed branch from d370256 to 8fc59c3 Compare July 10, 2026 14:55
@github-actions

Copy link
Copy Markdown

X-Test Failure Report

opentdf-ctl
opentdf-sdk-lib

@dmihalcik-virtru

Copy link
Copy Markdown
Member Author

Addressed the automated review feedback:

  • Gemini (high, tdf.ts:713): accessibleSplits now normalizes sid ?? to match splitIds, so the disallowed-KAS safety check is not bypassed when both undefined and empty-string split ids are present.
  • Gemini (medium, spec/DSPX-3379.md): reformatted the stack trace as a code block.
  • SonarCloud (S6582, tdf.ts:928): replaced !potentials || !potentials.length with the optional chain !potentials?.length.

make lint (typecheck + eslint across lib/cli/web-app) passes clean.

@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-3379-multisplit-allowed branch from 8fc59c3 to 5636af8 Compare July 16, 2026 14:28
@dmihalcik-virtru
dmihalcik-virtru requested a review from a team as a code owner July 16, 2026 14:28
@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-3379-multisplit-allowed branch 2 times, most recently from cdc4346 to a6c8401 Compare July 16, 2026 14:34
@github-actions

Copy link
Copy Markdown

X-Test Failure Report

opentdf-ctl
opentdf-sdk-lib

splitLookupTableFactory now returns split id -> KeyAccessObject[] (a
disjunction of alternatives) instead of a url-keyed map, and no longer
throws when a KAS repeats within a split. unwrapKey feeds each alternative
to anyPool with a unique key so duplicate KAS entries are each tried until
one succeeds. Matches go-sdk/java-sdk behavior; validated by xtest
test_tdf_with_duplicate_kao_same_kas (opentdf/tests#555).

Adds unit tests for duplicate + same-KAS-different-KID, an in-process
decrypt-path test, and fills spec/DSPX-3379.md.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-3379-multisplit-allowed branch from a6c8401 to c8f7a70 Compare July 16, 2026 15:13
@sonarqubecloud

Copy link
Copy Markdown

@dmihalcik-virtru
dmihalcik-virtru merged commit fab82a2 into main Jul 16, 2026
28 checks passed
@dmihalcik-virtru
dmihalcik-virtru deleted the DSPX-3379-multisplit-allowed branch July 16, 2026 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants