Skip to content

eql-bindings: EQL v4 types implement only the old EncryptFrom and DecryptInto — add EncryptTarget and DecryptSource #1585

Closed

Description

@coderdan

Background

Stack Encrypt (packages/stack-encrypt) is the Rust engine that every CipherStash language SDK runs on. EQL (Encrypt Query Language) is the SQL library we install into a customer's PostgreSQL database. It stores each encrypted value as a JSON payload in a typed column, such as public.eql_v4_text_eq. The eql-bindings crate (packages/eql/crates/eql-bindings) has one Rust type for each EQL v4 column type, such as TextEq or IntegerOrdOre, and one for each query operand, such as TextEqQuery. The eql-codegen crate generates these types from the EQL catalog.

Stack Encrypt has two sets of encryption traits. The old pair is EncryptFrom and DecryptInto, which encrypt from a borrowed plaintext. The new pair is EncryptTarget and DecryptSource, which take the plaintext by value and wipe it (packages/stack-encrypt/src/target/conversion.rs:18). The Stack Encrypt API plan removes the old pair in #1488.

Problem

Every EQL v4 type implements only the old pair. The generated types derive EncryptFrom and DecryptInto (eql-codegen/src/bindings.rs). The hand-written term types in eql-bindings/src/encryption.rs implement only the old traits too: Ciphertext (lines 211 and 232), Hmac256 (line 275), BloomFilter (line 395) and SearchBloomFilter (line 447). The order terms are in the same position.

So a Rust program cannot encrypt or decrypt an EQL value through the new API. It cannot call value.encrypt_into(&keyset) for a TextEq, or String::decrypt_from(stored, &keyset). Three things wait on this:

Pull request #1504 adds the EncryptTarget and DecryptSource derives, and leaves the EQL implementations for later. No issue tracks them.

Proposal

  1. Have eql-codegen emit #[derive(EncryptTarget, DecryptSource)] next to the existing derives on every scalar EQL v4 type and every query type. Keep the old derives until stack-encrypt refactor: remove transitional traits and redundant execution APIs #1488: the Go guest and the dynamic targets still use them.
  2. Implement EncryptTarget and DecryptSource on the hand-written term types in eql-bindings/src/encryption.rs.
  3. Encrypt takes the column as a required context: RequiredContext<NonEmpty<Identifier>>, built with Identifier::for_column(table, column) and stored in i. Decrypt takes the stored column, and checks it against a column the caller passes, if any (ExpectedContext<Identifier>). A value from another column is refused before any key is requested.
  4. Keep both null forms. A field typed Option<TextEq> maps None to SQL NULL. TextEq over Option<String> seals a null, as Go v0.1.0-rc.3 does.
  5. Run mise run types:generate in packages/eql, and commit the result.

Out of scope: the JSON tree types (JsonTreeDocument and the types that open it), and EQL types as fields of a derived record.

Acceptance

  • Every scalar EQL v4 type and query type implements EncryptTarget, and every stored type implements DecryptSource.
  • For every type, a test shows that the new path writes the same bytes as the old path, for the same plaintext, column and keyset.
  • Both null forms round-trip through the new path, with the same bytes as the old one.
  • Decrypting with a different column is refused before any key is requested.
  • types:check and cargo test -p eql-codegen pass.

Relationship to other work

Metadata

Metadata

Assignees

Labels

SDKrustPull requests that update Rust code

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions