Skip to content

fix: use StringDecoder for cipher string encoding remainder buffering#949

Merged
boorad merged 2 commits intomainfrom
fix/cipher-encoding-remainder
Feb 17, 2026
Merged

fix: use StringDecoder for cipher string encoding remainder buffering#949
boorad merged 2 commits intomainfrom
fix/cipher-encoding-remainder

Conversation

@boorad
Copy link
Collaborator

@boorad boorad commented Feb 17, 2026

Summary

Fixes incorrect cipher output when using string encoding (e.g., 'base64') with update() + final() string concatenation.

As reported by @vic614, longer inputs where the encrypted byte count isn't divisible by 3 produce different base64 output when using string concat vs Buffer concat. The root cause: Buffer.toString('base64') encodes each chunk independently, producing trailing = padding mid-stream that corrupts the concatenated result.

Changes

  • Replace ab2str() with StringDecoder in cipher update() and final()StringDecoder.write() buffers incomplete base64 groups across calls, and StringDecoder.end() flushes the remainder with proper padding. This matches Node.js's own cipher implementation.
  • Add encoding consistency validation — throws if different output encodings are passed to update() and final() on the same cipher instance, preventing silent data corruption.
  • Add string_decoder dependency (already in the dep tree via readable-stream).
  • Add 3 test cases covering:
    • Multi-block plaintext (32 bytes, 32 % 3 = 2 remainder)
    • Single block boundary (16 bytes, 16 % 3 = 1 remainder)
    • Full encrypt/decrypt roundtrip with long input

Testing

Run the cipher test suite in the example app. The 3 new tests validate that Buffer concat and string concat produce identical results for various input sizes.

Fixes #945

Restores StringDecoder usage (removed during Nitro rewrite in #573) to
properly handle base64/utf8 remainder bytes across update() and final()
calls. Without this, base64-encoding each chunk independently produces
premature padding, causing string concat to differ from Buffer concat
for inputs >= 16 bytes (one AES block).

Fixes #945
Prevent silent data corruption when different output encodings are passed
to update() and final(). Matches Node.js cipher behavior which asserts
the encoding cannot change once a StringDecoder is initialized.
@boorad boorad self-assigned this Feb 17, 2026
@github-actions
Copy link
Contributor

🤖 End-to-End Test Results - iOS

Status: ✅ Passed
Platform: iOS
Run: 22107813667

📸 Final Test Screenshot

Maestro Test Results - ios

Screenshot automatically captured from End-to-End tests and will expire in 30 days


This comment is automatically updated on each test run.

@github-actions
Copy link
Contributor

🤖 End-to-End Test Results - Android

Status: ✅ Passed
Platform: Android
Run: 22107813657

📸 Final Test Screenshot

Maestro Test Results - android

Screenshot automatically captured from End-to-End tests and will expire in 30 days


This comment is automatically updated on each test run.

@boorad boorad merged commit 4ef8b1f into main Feb 17, 2026
7 checks passed
@boorad boorad deleted the fix/cipher-encoding-remainder branch February 17, 2026 17:25
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.

🐛 Cipher behaviour change from 0.7+ to 1+

1 participant

Comments