Skip to content

[Detail Bug] Python SDK: Encryption key validation accepts wrong-length keys, causing server-side 422 errors #46

@detail-app

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_89d327b3-b883-4365-b6a3-46b6701342a9/bugs/bug_d22aba2e-b5b1-4f13-819b-392d704f8c9b

Introduced in #37 by @infiniteregrets on Apr 22, 2026

Summary

  • Context: The validate_encryption_key function validates encryption keys passed when accessing encrypted streams in the S2 SDK.
  • Bug: The validator only checks that the key is valid base64, but does not verify that the decoded key is 32 bytes as required by the encryption algorithms (AEGIS-256 and AES-256-GCM).
  • Actual vs. expected: Currently accepts any valid base64 string of any decoded length; should reject keys that don't decode to exactly 32 bytes.
  • Impact: Users get an uninformative 422 invalid error from the server instead of a precise client-side error message.

Code with Bug

# src/s2_sdk/_validators.py
def validate_encryption_key(key: str) -> None:
    try:
        base64.b64decode(key, validate=True)  # <-- BUG 🔴 only validates base64; does not enforce 32-byte decoded length
    except Exception:
        raise S2ClientError("encryption_key must be a base64 encoded str")
# src/s2_sdk/_ops.py
elif isinstance(encryption_key, bytes):
    encryption_key = base64.b64encode(encryption_key).decode()  # <-- BUG 🔴 bytes key length not validated; non-32B keys get encoded and sent

Explanation

Both supported ciphers require a 32-byte key, but the SDK accepts any base64 string (and any bytes length) and only fails later when the server rejects it with 422 invalid. This prevents the SDK from providing an immediate, actionable client-side error (e.g., reporting the decoded length).

Codebase Inconsistency

Other validators (e.g. validate_basin, validate_append_input) enforce documented constraints client-side even when the server also validates, to provide clearer errors. validate_encryption_key only enforces one of the documented requirements (base64 format) and omits the 32-byte requirement.

Recommended Fix

# src/s2_sdk/_validators.py
def validate_encryption_key(key: str) -> None:
    try:
        decoded = base64.b64decode(key, validate=True)
    except Exception:
        raise S2ClientError("encryption_key must be a base64 encoded str")
    if len(decoded) != 32:
        raise S2ClientError(
            f"encryption_key must decode to 32 bytes, got {len(decoded)} bytes"
        )
# src/s2_sdk/_ops.py
elif isinstance(encryption_key, bytes):
    if len(encryption_key) != 32:
        raise S2ClientError(
            f"encryption_key must be 32 bytes, got {len(encryption_key)} bytes"
        )
    encryption_key = base64.b64encode(encryption_key).decode()

History

This bug was introduced in commit b7df199. The original EncryptionKey class validated only that the base64-encoded string length was <= 44 characters, missing that the decoded key must be exactly 32 bytes for AEGIS-256 and AES-256-GCM ciphers. The incomplete validation (checking upper bound but not exact length) persisted through subsequent refactoring: commit 1958253 moved the class to _types.py, and commit 2106eda replaced it with the simpler validate_encryption_key function that only checks base64 format.

Metadata

Metadata

Assignees

Labels

detail-bugbug flagged by https://detail.dev/

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions