Skip to content

feat(proxy): accept inbound EQL ciphertext payloads - #458

Open
freshtonic wants to merge 7 commits into
mainfrom
feat/cip-3822-inbound-eql-payloads
Open

feat(proxy): accept inbound EQL ciphertext payloads#458
freshtonic wants to merge 7 commits into
mainfrom
feat/cip-3822-inbound-eql-payloads

Conversation

@freshtonic

@freshtonic freshtonic commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • detect EQL v3 storage payloads in SQL literals and bind parameters before plaintext decoding
  • validate payload version, inferred destination column, and exact configured SEM-term shape
  • authenticate ciphertext with the connection's active scoped cipher and forward it without re-encryption
  • project stored payloads into query operands where required and fail closed with a generic error
  • cover parameter storage/search, literal storage, decryption, and destination mismatch in cipherstash-proxy-integration
  • demonstrate application-side JSON encryption through parameter and literal inputs in the executable showcase

Testing

  • cargo test -p cipherstash-proxy inbound_eql --lib
  • cargo test -p cipherstash-proxy --lib (135 passed; one environment-sensitive config test passed when rerun alone)
  • cargo test -p cipherstash-proxy config::tandem::tests::missing_database_config --lib -- --exact
  • cargo clippy -p cipherstash-proxy --all-targets -- -D warnings
  • cargo clippy -p cipherstash-proxy-integration -p showcase --all-targets -- -D warnings
  • cargo test -p cipherstash-proxy-integration --no-run
  • cargo test -p showcase --no-run
  • cargo fmt --all -- --check

The runtime integration/showcase cases use the existing credentialed CI tasks. This checkout had no application/ZeroKMS credentials with which to start an isolated current-branch Proxy locally.

Linear: CIP-3822

Signed-off-by: James Sadler <james@cipherstash.com>
Signed-off-by: James Sadler <james@cipherstash.com>
@freshtonic
freshtonic requested a review from tobyhede August 19, 2026 06:59

@tobyhede tobyhede left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the new inbound EQL ciphertext path. One security issue with two exploit paths, one design trade-off to confirm, and one operational gap. Each item was checked against the actual code on this branch and the cipherstash-client 0.42.2 dependency source before being posted here. Two items from an earlier automated pass did not hold up under verification and are not included: the "h" top-level key check is not dead code (it detects SteVec payloads, which carry no top-level c field), and the binary-parameter byte handling in bind.rs does not corrupt data (original bytes stay untouched, and a failed trial parse falls back safely).

See inline comments for details.

Comment thread packages/cipherstash-proxy/src/postgresql/inbound_eql.rs
Comment thread packages/cipherstash-proxy/src/postgresql/inbound_eql.rs Outdated
Comment thread packages/cipherstash-proxy/src/postgresql/inbound_eql.rs Outdated
Comment thread packages/cipherstash-proxy/src/postgresql/frontend.rs Outdated
Signed-off-by: James Sadler <james@cipherstash.com>
Comment thread packages/cipherstash-proxy/src/postgresql/frontend.rs
Scope the ZeroKMS cipher to CS_DEFAULT_KEYSET_ID whenever a connection has not selected an override. Previously Proxy only checked that the setting existed, then passed no identifier to ScopedCipher and could silently use the client's account default instead.

Application-encrypted payloads use the configured keyset explicitly. When the account and configured defaults differ, Proxy derived searchable-encryption metadata with another index key and rejected valid inbound ciphertext during authentication. Preserve connection-level keyset precedence while making the configured fallback effective.

Signed-off-by: James Sadler <james@cipherstash.com>
Inbound EQL authentication independently re-encrypts plaintext and compares its searchable-encryption metadata with the supplied payload. Match-index generation does not guarantee a stable ordering for Bloom-filter bit positions, so comparing serialized query operands rejected valid ciphertext whenever equivalent positions were emitted in another order.

Compare scalar metadata field by field and normalize Bloom-filter positions before equality. Continue comparing identifiers, exact-match terms, ordered terms, versions, and structured SteVec operands exactly so altered metadata still fails closed. Add a regression test covering reordered equivalent Bloom-filter terms.

Signed-off-by: James Sadler <james@cipherstash.com>
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