Skip to content

chore(sdk): calculate encrypted segment size instead of measuring it (DSPX-4496) - #1061

Merged
dmihalcik-virtru merged 1 commit into
mainfrom
DSPX-4496-encrypted-segment-size
Oct 8, 2026
Merged

dmihalcik-virtru merged 1 commit into
mainfrom
DSPX-4496-encrypted-segment-size

Conversation

@dmihalcik-virtru

@dmihalcik-virtru dmihalcik-virtru commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

What

  • writeStream now calculates encryptedSegmentSizeDefault instead of measuring it. Before, it encrypted a throwaway full-size segment under the payload key and measured the output.
  • New SymmetricCipher.encryptedPayloadSize(plaintextSize):
    • AesGcmCipher implements it exactly: 12-byte IV + plaintext + 16-byte tag.
    • The base class throws a named ConfigurationError, so a duck-typed cipher from an older release fails clearly instead of with a TypeError.
  • Every written segment is checked against that number. A mismatch, such as an injected CryptoService returning a short tag, now fails the write instead of being absorbed by the per-segment encryptedSegmentSize.

Why

  • The measurement spent one AES-GCM call, and one extra encryption under the caller's key, to learn a constant. That matters for the IV work later in this series, where every call under a key counts against an invocation limit.
  • A cipher whose output length drifts from the manifest's default only showed up later, in readers that trust encryptedSegmentSizeDefault over the per-segment sizes.

How to test

cd lib && npm test. New tests:

  • AesGcmCipher predicts the exact ciphertext length for several sizes, from 0 to 1 MiB.
  • The base class names the cipher when a subclass has no size calculation.
  • A writer whose CryptoService truncates the tag fails mid-stream with ConfigurationError.

Risk

Low to moderate. This touches the write path. The manifest values are unchanged for AesGcmCipher, which is the only cipher the SDK ships.

DSPX-4496 series

Part PR Base Depends on
1 of 6 #1059 reject non-positive segment sizes main —
2 of 6 #1060 require 12-byte AES-GCM IVs main —
3 of 6 #1061 calculate encrypted segment size main —
4 of 6 #1062 default segment size 2 MiB main —
5 of 6 #1063 AesGcmEncryptor SPI DSPX-4496-encrypted-segment-size #1061
6 of 6 #1018 writer IVs from the CryptoService encryptor DSPX-4496-aes-gcm-encryptor #1063

#1059, #1060 and #1062 stand alone. #1061 ← #1063 ← #1018 is a stack.

Summary by CodeRabbit

  • Bug Fixes
    • Encrypted streams now calculate segment sizes using the configured cipher and verify that each encrypted segment matches the expected size. A mismatch reports a configuration error instead of producing an invalid stream.
    • AES-GCM payload size estimates now account for the initialization vector and authentication tag.
  • Tests
    • Added coverage for payload size calculations and for handling encrypted segments that differ from the expected size.

@coderabbitai

coderabbitai Bot commented Oct 6, 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: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c7fa066a-4199-4b5c-8532-e55feb72c57a
📥 Commits

Reviewing files that changed from the base of the PR and between cae9a68 and e6951e2.

📒 Files selected for processing (5)
  • lib/tdf3/src/ciphers/aes-gcm-cipher.ts
  • lib/tdf3/src/ciphers/symmetric-cipher-base.ts
  • lib/tdf3/src/tdf.ts
  • lib/tests/mocha/encrypt-decrypt.spec.ts
  • lib/tests/mocha/unit/aes-gcm-cipher.spec.ts

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

Walkthrough

The cipher API now reports encrypted payload sizes. TDF stream writing uses this calculation to choose its default encrypted segment size and rejects encrypted segments whose actual size does not match the reported size.

Changes

Cipher Payload Sizing

Layer / File(s) Summary
Cipher size contract
lib/tdf3/src/ciphers/symmetric-cipher-base.ts, lib/tdf3/src/ciphers/aes-gcm-cipher.ts, lib/tests/mocha/unit/aes-gcm-cipher.spec.ts
SymmetricCipher provides a default encryptedPayloadSize method that throws ConfigurationError. AesGcmCipher reports plaintext size plus its 12-byte IV and 16-byte authentication tag. Unit tests check the size calculation and default error.
TDF stream size validation
lib/tdf3/src/tdf.ts, lib/tests/mocha/encrypt-decrypt.spec.ts
writeStream uses the cipher’s size calculation instead of encrypting a zero-filled buffer to determine the default segment size. Segment encryption checks actual output length against the reported size. A stream test verifies that a mismatch rejects with ConfigurationError.

Priority: ⬇️ Low

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

Change: Refactor

Sequence Diagram(s)

sequenceDiagram
  participant TDF
  participant Cipher
  participant CryptoService
  TDF->>Cipher: Get encryptedPayloadSize for the default plaintext size
  TDF->>CryptoService: Encrypt each plaintext segment
  CryptoService-->>TDF: Return encrypted segment bytes
  TDF->>Cipher: Get expected size for the plaintext segment
  TDF->>TDF: Compare actual and expected encrypted sizes
Loading

Suggested reviewers: elizabethhealy

Merge Risk: ⚪ Minimal · up to e6951

No actionable merge-blocking risk was identified; the change is ready for normal test checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the main change: calculate encrypted segment size instead of measuring it. It is specific, concise, and includes the relevant SDK scope.
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.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · 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 each cipher’s size,
And watches bytes as segments rise.
The IV and tag join every stream,
While errors guard a mismatched seam.
Then hops away, pleased with the scheme.

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

@dmihalcik-virtru dmihalcik-virtru changed the title refactor(sdk): calculate encrypted segment size instead of measuring it (DSPX-4496) chore(sdk): calculate encrypted segment size instead of measuring it (DSPX-4496) Oct 7, 2026
@dmihalcik-virtru
dmihalcik-virtru marked this pull request as ready for review October 7, 2026 13:27
@dmihalcik-virtru
dmihalcik-virtru requested a review from a team as a code owner October 7, 2026 13:27
@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-4496-encrypted-segment-size branch from 32d08b0 to 5281f17 Compare October 7, 2026 13:30
…it (DSPX-4496)

writeStream learned `encryptedSegmentSizeDefault` by encrypting a throwaway
full-size segment under the payload key and measuring the result. That
spent an AES-GCM invocation, and an extra encryption under the caller's key,
to learn a constant.

Add `SymmetricCipher.encryptedPayloadSize`, implement it exactly for
AesGcmCipher (IV + plaintext + 16-byte tag), and derive the default from it.
The base implementation throws a named ConfigurationError, so a duck-typed
cipher from an older release fails legibly rather than with a TypeError.

Every written segment is checked against the arithmetic. A mismatch, such
as an injected CryptoService returning a short tag, now fails the write
instead of being absorbed by the per-segment `encryptedSegmentSize`.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-4496-encrypted-segment-size branch from 5281f17 to e6951e2 Compare October 7, 2026 17:04
@sonarqubecloud

sonarqubecloud Bot commented Oct 7, 2026

Copy link
Copy Markdown

@dmihalcik-virtru
dmihalcik-virtru added this pull request to the merge queue Oct 8, 2026
Merged via the queue into main with commit 60c29de Oct 8, 2026
31 checks passed
@dmihalcik-virtru
dmihalcik-virtru deleted the DSPX-4496-encrypted-segment-size branch October 8, 2026 15:31
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