Skip to content

feat: encryption support#37

Merged
quettabit merged 7 commits intomainfrom
m/encrypto-sdk
Apr 23, 2026
Merged

feat: encryption support#37
quettabit merged 7 commits intomainfrom
m/encrypto-sdk

Conversation

@infiniteregrets
Copy link
Copy Markdown
Member

No description provided.

@infiniteregrets infiniteregrets requested a review from a team as a code owner April 22, 2026 22:57
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 22, 2026

Greptile Summary

This PR adds end-to-end encryption support: an Encryption enum (AEGIS-256, AES-256-GCM), a stream_cipher field on BasinConfig, a cipher field on StreamInfo, and an encryption_key parameter on S2Basin.stream() that threads the key through every transport layer (unary append/read, streaming append/read session, and the Producer).

  • P1 – no key-length guard: validate_encryption_key checks base64 format but not decoded length, and the bytes branch in _ops.py has no length check at all. Both AEGIS-256 and AES-256-GCM require exactly 32 bytes; a wrong-length key passes client-side validation silently and triggers an opaque server error at runtime.

Confidence Score: 4/5

Safe to merge after adding key-length validation; all transport paths wire the key correctly.

One P1 finding: missing 32-byte length check for both the str and bytes key inputs allows wrong-length keys to bypass client-side validation and produce opaque server errors. Everything else — enum definition, mapper serialisation/deserialisation, header injection across all four transport paths, public API export, and test coverage — is correct.

src/s2_sdk/_validators.py and the bytes branch in src/s2_sdk/_ops.py (lines 646-647) both need a 32-byte length guard.

Important Files Changed

Filename Overview
src/s2_sdk/_validators.py Adds validate_encryption_key which validates base64 format but omits a decoded-length check; wrong-length keys slip through to the server.
src/s2_sdk/_ops.py Adds encryption_key param to stream(), threads it through S2Stream, and adds _request_headers helper; bytes branch has no length guard.
src/s2_sdk/_types.py Adds Encryption enum (AEGIS_256, AES_256_GCM), _S2_ENCRYPTION_KEY_HEADER constant, and stream_cipher/cipher fields to BasinConfig/StreamInfo.
src/s2_sdk/_mappers.py Correctly serialises stream_cipher into the basin config JSON and deserialises cipher back via Encryption(value) in stream_info_from_json.
src/s2_sdk/_s2s/_append_session.py Correctly injects encryption key header into the streaming append request when key is present.
src/s2_sdk/_s2s/_read_session.py Correctly injects encryption key header into the streaming read request when key is present.
tests/test_stream_ops.py Adds integration tests covering roundtrip (unary and session), missing-key rejection, and wrong-key decryption failure for both cipher algorithms.
tests/test_session.py Adds encryption_key param to mock session signatures to match the updated run_append_session interface.
src/s2_sdk/init.py Exports Encryption from the public API surface correctly.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Caller calls basin.stream with key] --> B{key type?}
    B -->|str| C[validate_encryption_key: check base64 format]
    B -->|bytes| D[base64.b64encode the raw bytes]
    B -->|None| E[no key stored]
    C --> F[S2Stream created with b64 key]
    D --> F
    E --> G[S2Stream created without key]
    F --> H[_request_headers merges key into HTTP headers]
    H --> I[append unary POST]
    H --> J[read unary GET]
    H --> K[append session streaming POST]
    H --> L[read session streaming GET]
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/s2_sdk/_validators.py
Line: 57-61

Comment:
**Missing key-length validation for both `str` and `bytes` inputs**

`validate_encryption_key` confirms the string is valid base64 but never checks that it decodes to exactly 32 bytes (required for both AEGIS-256 and AES-256-GCM). A caller who passes a valid base64 string that encodes a 16-byte key will clear client-side validation, have the raw header forwarded, and then receive an opaque server rejection instead of a clear SDK error.

The same gap exists for the `bytes` branch in `_ops.py`: any-length `bytes` is silently encoded and sent. Adding `len(decoded) != 32` / `len(encryption_key) != 32` guards to both paths would surface this immediately with a meaningful `S2ClientError`.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (2): Last reviewed commit: "polishes" | Re-trigger Greptile

Comment thread src/s2_sdk/_encryption.py Outdated
Comment thread src/s2_sdk/_encryption.py Outdated
Comment thread src/s2_sdk/_encryption.py Outdated
Comment thread src/s2_sdk/_encryption.py Outdated
Comment thread src/s2_sdk/_encryption.py Outdated
Comment thread src/s2_sdk/_encryption.py Outdated
Comment thread src/s2_sdk/_encryption.py Outdated
Comment thread src/s2_sdk/_ops.py Outdated
@quettabit quettabit marked this pull request as draft April 23, 2026 05:31
@quettabit quettabit changed the title feat: encryption support [WIP] Apr 23, 2026
@quettabit quettabit changed the title [WIP] feat: encryption support Apr 23, 2026
@quettabit quettabit marked this pull request as ready for review April 23, 2026 20:05
Comment thread src/s2_sdk/_validators.py
@quettabit quettabit merged commit 2106eda into main Apr 23, 2026
11 checks passed
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