From 4539acbc2483dcf6042a57719d7dc6b14654588e Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Sat, 12 Sep 2026 21:16:50 -0400 Subject: [PATCH 1/3] feat(eql): derive text equality targets Use Stack Encrypt declarations and native readers for TextEq and TextEqQuery. The stored identifier supplies context; Vitamin C owns plaintext encoding and equality input. Encode only encrypted output. Exercise canonical cross-opening, validation before KMS, batching, query-only operation and PostgreSQL equality/index use. Registry CI requires the unpublished suite crates and a refreshed registry lock. Refs cipherstash/cipherstash-suite#2215 --- .changeset/eql-text-equality-transcoding.md | 5 + .github/workflows/test-eql.yml | 30 + packages/eql/Cargo.lock | 686 ++++++++++++++++-- packages/eql/Cargo.toml | 1 + packages/eql/crates/eql-bindings/Cargo.toml | 10 + packages/eql/crates/eql-bindings/README.md | 61 ++ .../eql-bindings/bindings/v3/Ciphertext.ts | 2 +- .../eql-bindings/schema/v3/eql_v3_bigint.json | 2 +- .../schema/v3/eql_v3_bigint_eq.json | 2 +- .../schema/v3/eql_v3_bigint_ord.json | 2 +- .../schema/v3/eql_v3_bigint_ord_ope.json | 2 +- .../schema/v3/eql_v3_bigint_ord_ore.json | 2 +- .../schema/v3/eql_v3_boolean.json | 2 +- .../eql-bindings/schema/v3/eql_v3_date.json | 2 +- .../schema/v3/eql_v3_date_eq.json | 2 +- .../schema/v3/eql_v3_date_ord.json | 2 +- .../schema/v3/eql_v3_date_ord_ope.json | 2 +- .../schema/v3/eql_v3_date_ord_ore.json | 2 +- .../eql-bindings/schema/v3/eql_v3_double.json | 2 +- .../schema/v3/eql_v3_double_eq.json | 2 +- .../schema/v3/eql_v3_double_ord.json | 2 +- .../schema/v3/eql_v3_double_ord_ope.json | 2 +- .../schema/v3/eql_v3_double_ord_ore.json | 2 +- .../schema/v3/eql_v3_integer.json | 2 +- .../schema/v3/eql_v3_integer_eq.json | 2 +- .../schema/v3/eql_v3_integer_ord.json | 2 +- .../schema/v3/eql_v3_integer_ord_ope.json | 2 +- .../schema/v3/eql_v3_integer_ord_ore.json | 2 +- .../eql-bindings/schema/v3/eql_v3_json.json | 2 +- .../schema/v3/eql_v3_numeric.json | 2 +- .../schema/v3/eql_v3_numeric_eq.json | 2 +- .../schema/v3/eql_v3_numeric_ord.json | 2 +- .../schema/v3/eql_v3_numeric_ord_ope.json | 2 +- .../schema/v3/eql_v3_numeric_ord_ore.json | 2 +- .../eql-bindings/schema/v3/eql_v3_real.json | 2 +- .../schema/v3/eql_v3_real_eq.json | 2 +- .../schema/v3/eql_v3_real_ord.json | 2 +- .../schema/v3/eql_v3_real_ord_ope.json | 2 +- .../schema/v3/eql_v3_real_ord_ore.json | 2 +- .../schema/v3/eql_v3_smallint.json | 2 +- .../schema/v3/eql_v3_smallint_eq.json | 2 +- .../schema/v3/eql_v3_smallint_ord.json | 2 +- .../schema/v3/eql_v3_smallint_ord_ope.json | 2 +- .../schema/v3/eql_v3_smallint_ord_ore.json | 2 +- .../eql-bindings/schema/v3/eql_v3_text.json | 2 +- .../schema/v3/eql_v3_text_eq.json | 2 +- .../schema/v3/eql_v3_text_match.json | 2 +- .../schema/v3/eql_v3_text_ord.json | 2 +- .../schema/v3/eql_v3_text_ord_ope.json | 2 +- .../schema/v3/eql_v3_text_ord_ore.json | 2 +- .../schema/v3/eql_v3_text_search.json | 2 +- .../schema/v3/eql_v3_text_search_ore.json | 2 +- .../schema/v3/eql_v3_timestamp.json | 2 +- .../schema/v3/eql_v3_timestamp_eq.json | 2 +- .../schema/v3/eql_v3_timestamp_ord.json | 2 +- .../schema/v3/eql_v3_timestamp_ord_ope.json | 2 +- .../schema/v3/eql_v3_timestamp_ord_ore.json | 2 +- .../eql/crates/eql-bindings/src/encryption.rs | 193 +++++ packages/eql/crates/eql-bindings/src/lib.rs | 3 + .../eql/crates/eql-bindings/src/v3/terms.rs | 2 +- .../eql/crates/eql-bindings/src/v3/text.rs | 9 + .../eql/crates/eql-codegen/src/bindings.rs | 37 +- .../generated/schema/v3/eql_v3_bigint.json | 2 +- .../generated/schema/v3/eql_v3_bigint_eq.json | 2 +- .../schema/v3/eql_v3_bigint_ord.json | 2 +- .../schema/v3/eql_v3_bigint_ord_ope.json | 2 +- .../schema/v3/eql_v3_bigint_ord_ore.json | 2 +- .../generated/schema/v3/eql_v3_boolean.json | 2 +- .../src/generated/schema/v3/eql_v3_date.json | 2 +- .../generated/schema/v3/eql_v3_date_eq.json | 2 +- .../generated/schema/v3/eql_v3_date_ord.json | 2 +- .../schema/v3/eql_v3_date_ord_ope.json | 2 +- .../schema/v3/eql_v3_date_ord_ore.json | 2 +- .../generated/schema/v3/eql_v3_double.json | 2 +- .../generated/schema/v3/eql_v3_double_eq.json | 2 +- .../schema/v3/eql_v3_double_ord.json | 2 +- .../schema/v3/eql_v3_double_ord_ope.json | 2 +- .../schema/v3/eql_v3_double_ord_ore.json | 2 +- .../generated/schema/v3/eql_v3_integer.json | 2 +- .../schema/v3/eql_v3_integer_eq.json | 2 +- .../schema/v3/eql_v3_integer_ord.json | 2 +- .../schema/v3/eql_v3_integer_ord_ope.json | 2 +- .../schema/v3/eql_v3_integer_ord_ore.json | 2 +- .../src/generated/schema/v3/eql_v3_json.json | 2 +- .../generated/schema/v3/eql_v3_numeric.json | 2 +- .../schema/v3/eql_v3_numeric_eq.json | 2 +- .../schema/v3/eql_v3_numeric_ord.json | 2 +- .../schema/v3/eql_v3_numeric_ord_ope.json | 2 +- .../schema/v3/eql_v3_numeric_ord_ore.json | 2 +- .../src/generated/schema/v3/eql_v3_real.json | 2 +- .../generated/schema/v3/eql_v3_real_eq.json | 2 +- .../generated/schema/v3/eql_v3_real_ord.json | 2 +- .../schema/v3/eql_v3_real_ord_ope.json | 2 +- .../schema/v3/eql_v3_real_ord_ore.json | 2 +- .../generated/schema/v3/eql_v3_smallint.json | 2 +- .../schema/v3/eql_v3_smallint_eq.json | 2 +- .../schema/v3/eql_v3_smallint_ord.json | 2 +- .../schema/v3/eql_v3_smallint_ord_ope.json | 2 +- .../schema/v3/eql_v3_smallint_ord_ore.json | 2 +- .../src/generated/schema/v3/eql_v3_text.json | 2 +- .../generated/schema/v3/eql_v3_text_eq.json | 2 +- .../schema/v3/eql_v3_text_match.json | 2 +- .../generated/schema/v3/eql_v3_text_ord.json | 2 +- .../schema/v3/eql_v3_text_ord_ope.json | 2 +- .../schema/v3/eql_v3_text_ord_ore.json | 2 +- .../schema/v3/eql_v3_text_search.json | 2 +- .../schema/v3/eql_v3_text_search_ore.json | 2 +- .../generated/schema/v3/eql_v3_timestamp.json | 2 +- .../schema/v3/eql_v3_timestamp_eq.json | 2 +- .../schema/v3/eql_v3_timestamp_ord.json | 2 +- .../schema/v3/eql_v3_timestamp_ord_ope.json | 2 +- .../schema/v3/eql_v3_timestamp_ord_ore.json | 2 +- .../eql/src/generated/v3/Ciphertext.ts | 2 +- packages/eql/tasks/dev/with-stack-encrypt.sh | 22 + packages/eql/tests/encryption/Cargo.toml | 16 + .../eql/tests/encryption/tests/common/mod.rs | 68 ++ .../eql/tests/encryption/tests/text_eq.rs | 319 ++++++++ 117 files changed, 1500 insertions(+), 166 deletions(-) create mode 100644 .changeset/eql-text-equality-transcoding.md create mode 100644 packages/eql/crates/eql-bindings/src/encryption.rs create mode 100644 packages/eql/tasks/dev/with-stack-encrypt.sh create mode 100644 packages/eql/tests/encryption/Cargo.toml create mode 100644 packages/eql/tests/encryption/tests/common/mod.rs create mode 100644 packages/eql/tests/encryption/tests/text_eq.rs diff --git a/.changeset/eql-text-equality-transcoding.md b/.changeset/eql-text-equality-transcoding.md new file mode 100644 index 000000000..969aa9653 --- /dev/null +++ b/.changeset/eql-text-equality-transcoding.md @@ -0,0 +1,5 @@ +--- +'@cipherstash/eql': minor +--- + +**Rust `TextEq` and `TextEqQuery` support Stack Encrypt's target-directed API behind the `stack-encrypt` feature.** The stored table and column identifier supplies the encryption context; native ciphertext and equality terms are transcoded into EQL payloads while Vitamin C owns plaintext encoding. This is a new producer profile with exact string equality, independent of existing cipherstash-client ciphertext and terms. Query operands carry no recoverable ciphertext. diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index 5cbaa6608..be171ce16 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -520,6 +520,18 @@ jobs: github.event_name != 'pull_request' || needs.changes.outputs.relevant == 'true' runs-on: blacksmith-16vcpu-ubuntu-2204 + services: + encryption-postgres: + image: postgres:17 + env: + POSTGRES_PASSWORD: postgres + ports: + - 7433:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: @@ -549,6 +561,24 @@ jobs: rustup component add --toolchain "${active_rust_toolchain}" rustfmt clippy mise run test:crates + # Registry builds require the unpublished Stack Encrypt crates from + # cipherstash-suite#2215 to be published before this change merges. + - name: Test Rust text equality with PostgreSQL + env: + PGPASSWORD: postgres + EQL_TEST_DATABASE_URL: "host=localhost port=7433 user=postgres password=postgres dbname=postgres" + run: | + cargo test --locked -p eql-bindings --features stack-encrypt + cargo test --locked -p eql-encryption-tests + cargo clippy --locked -p eql-bindings -p eql-encryption-tests --all-features --all-targets -- -D warnings + psql -h localhost -p 7433 -U postgres -d postgres -v ON_ERROR_STOP=1 -f crates/eql-bindings/sql/cipherstash-encrypt.sql + cargo test --locked -p eql-encryption-tests --test text_eq -- --ignored + + - name: Compile Rust text equality for WASI without HTTP + run: | + rustup target add wasm32-wasip1 + cargo check --locked -p eql-bindings --features stack-encrypt --target wasm32-wasip1 + # Freshness gate for the eql-types codegen output: regenerate the # TypeScript bindings and JSON Schemas and fail if the checked-in # copies differ. Reuses the toolchain from the step above. diff --git a/packages/eql/Cargo.lock b/packages/eql/Cargo.lock index 8e6f936bb..5c2389ddb 100644 --- a/packages/eql/Cargo.lock +++ b/packages/eql/Cargo.lock @@ -345,6 +345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" dependencies = [ "hybrid-array", + "zeroize", ] [[package]] @@ -565,15 +566,15 @@ dependencies = [ "base85", "blake3", "chrono", - "cipherstash-config", + "cipherstash-config 0.42.2", "cipherstash-core", - "cllw-ore", - "cts-common", + "cllw-ore 0.4.2", + "cts-common 0.42.2", "derive_more 1.0.0", "dirs", "futures", "hex", - "hmac", + "hmac 0.12.1", "itertools 0.12.1", "lazy_static", "log", @@ -583,7 +584,7 @@ dependencies = [ "ore-rs", "percent-encoding", "rand 0.8.6", - "recipher", + "recipher 0.2.3", "reqwest", "rmp-serde", "rust-stemmers", @@ -593,9 +594,9 @@ dependencies = [ "serde_cbor", "serde_json", "serdect", - "sha2", - "stack-auth", - "stack-profile", + "sha2 0.10.9", + "stack-auth 0.42.2", + "stack-profile 0.42.2", "static_assertions", "thiserror 1.0.69", "tokio", @@ -603,11 +604,11 @@ dependencies = [ "tracing", "url", "uuid", - "vitaminc", - "vitaminc-protected", + "vitaminc 0.2.0-pre", + "vitaminc-protected 0.2.0-pre", "winnow 0.6.26", "zeroize", - "zerokms-protocol", + "zerokms-protocol 0.12.28", ] [[package]] @@ -622,6 +623,16 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "cipherstash-config" +version = "0.42.3" +dependencies = [ + "bitflags", + "serde", + "serde_json", + "thiserror 1.0.69", +] + [[package]] name = "cipherstash-core" version = "0.42.0" @@ -629,12 +640,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1946988e0b7f9de259d85b10c9c1fd7e1327751103b808c2c6e930b9a87c25c9" dependencies = [ "getrandom 0.2.16", - "hmac", + "hmac 0.12.1", "lazy_static", "num-bigint", "rand 0.8.6", "regex", - "sha2", + "sha2 0.10.9", "thiserror 1.0.69", ] @@ -654,6 +665,18 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "cllw-ore" +version = "0.4.3" +dependencies = [ + "blake3", + "hex", + "subtle", + "thiserror 1.0.69", + "unicode-normalization", + "zeroize", +] + [[package]] name = "cmac" version = "0.7.2" @@ -674,6 +697,12 @@ dependencies = [ "cc", ] +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "combine" version = "4.6.7" @@ -729,6 +758,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "constant_time_eq" version = "0.4.2" @@ -893,7 +928,40 @@ dependencies = [ "url", "utoipa", "uuid", - "vitaminc", + "vitaminc 0.2.0-pre", +] + +[[package]] +name = "cts-common" +version = "0.42.3" +dependencies = [ + "arrayvec", + "base32", + "cached", + "chrono", + "derive_more 2.1.1", + "either", + "getrandom 0.4.2", + "miette", + "nom", + "regex", + "serde", + "serde_json", + "thiserror 1.0.69", + "tracing", + "url", + "utoipa", + "uuid", + "vitaminc 0.4.0", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", ] [[package]] @@ -952,7 +1020,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid", + "const-oid 0.9.6", "pem-rfc7468", "zeroize", ] @@ -1023,7 +1091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid", + "const-oid 0.9.6", "crypto-common 0.1.6", "subtle", ] @@ -1035,7 +1103,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.0", + "const-oid 0.10.2", "crypto-common 0.2.2", + "ctutils", + "zeroize", ] [[package]] @@ -1133,11 +1204,15 @@ dependencies = [ name = "eql-bindings" version = "3.0.5" dependencies = [ + "base64", "eql-domains", + "hex", "schemars", "serde", "serde_json", + "stack-encrypt", "ts-rs", + "vitaminc-prf", ] [[package]] @@ -1162,6 +1237,20 @@ dependencies = [ "proptest", ] +[[package]] +name = "eql-encryption-tests" +version = "0.1.0" +dependencies = [ + "eql-bindings", + "postgres", + "serde", + "serde_json", + "stack-encrypt", + "stack-kms", + "tokio", + "uuid", +] + [[package]] name = "eql-tests-macros" version = "0.1.0" @@ -1249,6 +1338,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fancy-regex" version = "0.18.0" @@ -1482,7 +1577,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1655,7 +1750,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac", + "hmac 0.12.1", ] [[package]] @@ -1667,6 +1762,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + [[package]] name = "home" version = "0.5.11" @@ -2181,13 +2285,14 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "6480ccc157a1389bb2e4891b24751b0f798ba640d22386f23143fbcc89da195a" dependencies = [ "bitflags", "libc", - "redox_syscall", + "plain", + "redox_syscall 0.9.4", ] [[package]] @@ -2249,6 +2354,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "md-5" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" +dependencies = [ + "cfg-if", + "digest 0.11.3", +] + [[package]] name = "memchr" version = "2.7.6" @@ -2316,7 +2431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.61.2", ] @@ -2337,6 +2452,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "mutants" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ac067452ff1aca8c5002111bd6b1c895baee6e45fcbc44e0193aea17be56" + [[package]] name = "nom" version = "8.0.0" @@ -2448,6 +2569,24 @@ dependencies = [ "libm", ] +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", +] + +[[package]] +name = "objc2-system-configuration" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7216bd11cbda54ccabcab84d523dc93b858ec75ecfb3a7d89513fa22464da396" +dependencies = [ + "objc2-core-foundation", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -2543,7 +2682,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", "windows-link", ] @@ -2585,6 +2724,25 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_shared", + "serde", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -2624,6 +2782,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "polyval" version = "0.6.2" @@ -2642,6 +2806,49 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +[[package]] +name = "postgres" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ad20e0aa0b24f5a394eab4f78c781d248982b22b25cecc7e3aa46a681605bd" +dependencies = [ + "bytes", + "fallible-iterator", + "futures-util", + "log", + "tokio", + "tokio-postgres", +] + +[[package]] +name = "postgres-protocol" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08808e3c483c46e999108051c78334f473d5adb59d78bb80a1268c7e6aa6c514" +dependencies = [ + "base64", + "byteorder", + "bytes", + "fallible-iterator", + "hmac 0.13.0", + "md-5 0.11.0", + "memchr", + "rand 0.10.1", + "sha2 0.11.0", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851ca9db4932932d69f3ea811b1abe63087a0f740a47692619dd40d4899b68be" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", +] + [[package]] name = "potential_utf" version = "0.1.3" @@ -2955,7 +3162,26 @@ dependencies = [ "rand_chacha 0.3.1", "serde", "serde_cbor", - "sha2", + "sha2 0.10.9", + "thiserror 1.0.69", + "zeroize", +] + +[[package]] +name = "recipher" +version = "0.3.0" +dependencies = [ + "aes", + "cmac", + "getrandom 0.2.16", + "hex", + "hex-literal", + "opaque-debug", + "rand 0.8.6", + "rand_chacha 0.3.1", + "serde", + "serde_cbor", + "sha2 0.10.9", "thiserror 1.0.69", "zeroize", ] @@ -2969,6 +3195,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "737970939a87c6fa31e7acad13307bccbb017a073b695b6089a2c484f929e20e" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.6" @@ -3172,7 +3407,7 @@ version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ - "const-oid", + "const-oid 0.9.6", "digest 0.10.7", "num-bigint-dig", "num-integer", @@ -3571,6 +3806,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + [[package]] name = "shlex" version = "1.3.0" @@ -3630,6 +3876,12 @@ dependencies = [ "time", ] +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + [[package]] name = "slab" version = "0.4.11" @@ -3714,7 +3966,7 @@ dependencies = [ "rust_decimal", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "smallvec", "thiserror 2.0.20", "tokio", @@ -3751,7 +4003,7 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "sqlx-core", "sqlx-mysql", "sqlx-postgres", @@ -3784,10 +4036,10 @@ dependencies = [ "generic-array", "hex", "hkdf", - "hmac", + "hmac 0.12.1", "itoa", "log", - "md-5", + "md-5 0.10.6", "memchr", "once_cell", "percent-encoding", @@ -3796,13 +4048,13 @@ dependencies = [ "rust_decimal", "serde", "sha1", - "sha2", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", "thiserror 2.0.20", "tracing", - "whoami", + "whoami 1.6.1", ] [[package]] @@ -3824,24 +4076,24 @@ dependencies = [ "futures-util", "hex", "hkdf", - "hmac", + "hmac 0.12.1", "home", "itoa", "log", - "md-5", + "md-5 0.10.6", "memchr", "once_cell", "rand 0.8.6", "rust_decimal", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", "thiserror 2.0.20", "tracing", - "whoami", + "whoami 1.6.1", ] [[package]] @@ -3883,24 +4135,105 @@ checksum = "f1a9ac43060af7605899754daa3de2e26302c2ab4d9249a8bc16157bc53dad65" dependencies = [ "aquamarine", "base64", - "cts-common", + "cts-common 0.42.2", "jsonwebtoken", "miette", "open", "reqwest", "serde", "serde_json", - "stack-profile", + "stack-profile 0.42.2", + "thiserror 1.0.69", + "tokio", + "tracing", + "url", + "uuid", + "vitaminc 0.2.0-pre", + "vitaminc-protected 0.2.0-pre", + "web-time", + "zeroize", + "zerokms-protocol 0.12.28", +] + +[[package]] +name = "stack-auth" +version = "0.42.3" +dependencies = [ + "aquamarine", + "base64", + "cts-common 0.42.3", + "jsonwebtoken", + "miette", + "open", + "serde", + "serde_json", + "stack-profile 0.42.3", "thiserror 1.0.69", "tokio", "tracing", "url", "uuid", - "vitaminc", - "vitaminc-protected", + "vitaminc 0.4.0", + "vitaminc-protected 0.4.0", "web-time", "zeroize", - "zerokms-protocol", + "zerokms-protocol 0.12.30", +] + +[[package]] +name = "stack-encrypt" +version = "0.1.0" +dependencies = [ + "base64ct", + "cllw-ore 0.4.3", + "serde", + "stack-encrypt-derive", + "stack-kms", + "thiserror 1.0.69", + "uuid", + "vitaminc-aead 0.4.0", + "vitaminc-encrypt 0.4.0", + "vitaminc-hmac", + "vitaminc-prf", + "vitaminc-protected 0.4.0", + "zeroize", +] + +[[package]] +name = "stack-encrypt-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "stack-kms" +version = "0.1.0" +dependencies = [ + "base16ct", + "base64ct", + "blake3", + "futures", + "miette", + "opaque-debug", + "recipher 0.3.0", + "serde", + "serde_cbor", + "serde_json", + "serdect", + "sha2 0.10.9", + "stack-auth 0.42.3", + "stack-profile 0.42.3", + "thiserror 1.0.69", + "tracing", + "url", + "uuid", + "vitaminc 0.4.0", + "vitaminc-protected 0.4.0", + "zeroize", + "zerokms-protocol 0.12.30", ] [[package]] @@ -3917,6 +4250,18 @@ dependencies = [ "uuid", ] +[[package]] +name = "stack-profile" +version = "0.42.3" +dependencies = [ + "dirs", + "gethostname", + "serde", + "serde_json", + "thiserror 1.0.69", + "uuid", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -4163,6 +4508,32 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "tokio-postgres" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a528f7d280f6d5b9cd149635c8705b0dd049754bc67d81d31fa25169a93809d3" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand 0.10.1", + "socket2", + "tokio", + "tokio-util", + "whoami 2.1.3", +] + [[package]] name = "tokio-rustls" version = "0.26.4" @@ -4516,7 +4887,7 @@ dependencies = [ "atomic", "getrandom 0.4.2", "js-sys", - "md-5", + "md-5 0.10.6", "serde_core", "sha1_smol", "wasm-bindgen", @@ -4580,11 +4951,24 @@ version = "0.2.0-pre" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec9807fc6a29a9ab4a9cd24f103573661d7e04d48651ce8c752d32e73a686446" dependencies = [ - "vitaminc-aead", - "vitaminc-encrypt", - "vitaminc-protected", - "vitaminc-random", - "vitaminc-traits", + "vitaminc-aead 0.2.0-pre", + "vitaminc-encrypt 0.2.0-pre", + "vitaminc-protected 0.2.0-pre", + "vitaminc-random 0.2.0-pre", + "vitaminc-traits 0.2.0-pre", +] + +[[package]] +name = "vitaminc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b26ab159600161f73a1a519a31562ec4732257991acc6cba180db826c7765035" +dependencies = [ + "vitaminc-aead 0.4.0", + "vitaminc-encrypt 0.4.0", + "vitaminc-protected 0.4.0", + "vitaminc-random 0.4.0", + "vitaminc-traits 0.4.0", ] [[package]] @@ -4595,11 +4979,37 @@ checksum = "3933759550ab4841536e8d019fc73cd77ec2e43dcdaefa3218936534eefabb8d" dependencies = [ "bytes", "serde", - "vitaminc-protected", - "vitaminc-random", + "vitaminc-protected 0.2.0-pre", + "vitaminc-random 0.2.0-pre", + "zeroize", +] + +[[package]] +name = "vitaminc-aead" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b4b439efcb59c4fb2b56aa59934d5907d6907172feb45b45df1eb9311f357e" +dependencies = [ + "bytes", + "serde", + "vitaminc-aead-derive", + "vitaminc-prf", + "vitaminc-protected 0.4.0", + "vitaminc-random 0.4.0", "zeroize", ] +[[package]] +name = "vitaminc-aead-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fa56c62afe5cc393173876b83ee361246e341e855f7d45a334e1edd30c0018e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "vitaminc-encrypt" version = "0.2.0-pre" @@ -4608,12 +5018,50 @@ checksum = "2bdbcfd5e9054ce2a3f0cfac67c3e3164d65f38f3a6d593bc4d5d943e53aaf44" dependencies = [ "aes-gcm", "aws-lc-rs", - "vitaminc-aead", - "vitaminc-protected", - "vitaminc-random", + "vitaminc-aead 0.2.0-pre", + "vitaminc-protected 0.2.0-pre", + "vitaminc-random 0.2.0-pre", + "zeroize", +] + +[[package]] +name = "vitaminc-encrypt" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb81b908b8747fd74ffc198591849ccab8e0be52049d41b3d5d7bb8aed5342b" +dependencies = [ + "aes-gcm", + "aws-lc-rs", + "vitaminc-aead 0.4.0", + "vitaminc-protected 0.4.0", + "vitaminc-random 0.4.0", + "zeroize", +] + +[[package]] +name = "vitaminc-hmac" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d9de7991de2c5e4526d9fc81ab2c153ba5c0af7b50bdc9f1bd19eddb10b8307" +dependencies = [ + "hmac 0.13.0", + "sha2 0.11.0", + "vitaminc-prf", + "vitaminc-protected 0.4.0", "zeroize", ] +[[package]] +name = "vitaminc-prf" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d8978944fc2fba04255b776b384ff71b9ed5abc4d5bce859a11cc5d6841cba9" +dependencies = [ + "mutants", + "thiserror 2.0.20", + "vitaminc-protected 0.4.0", +] + [[package]] name = "vitaminc-protected" version = "0.2.0-pre" @@ -4625,7 +5073,23 @@ dependencies = [ "serde", "serde_bytes", "subtle", - "vitaminc-protected-derive", + "vitaminc-protected-derive 0.2.0-pre", + "zeroize", +] + +[[package]] +name = "vitaminc-protected" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31efcddf3193efbe1801959922159ffe60a1e1388bc993927d3d7242f144c7" +dependencies = [ + "bitvec", + "digest 0.11.3", + "serde", + "serde_bytes", + "subtle", + "thiserror 2.0.20", + "vitaminc-protected-derive 0.4.0", "zeroize", ] @@ -4640,6 +5104,17 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "vitaminc-protected-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17fe15916386e0404b17c468e8c5a255ffc037e930512b5aa494802c8bba22d8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "vitaminc-random" version = "0.2.0-pre" @@ -4649,8 +5124,22 @@ dependencies = [ "getrandom 0.4.2", "rand 0.10.1", "thiserror 2.0.20", - "vitaminc-protected", - "vitaminc-random-derives", + "vitaminc-protected 0.2.0-pre", + "vitaminc-random-derives 0.2.0-pre", + "zeroize", +] + +[[package]] +name = "vitaminc-random" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f8f9bc3db765f757068db06bd60f5e6fbfa8d546ed6da82246c33b6321fe45f" +dependencies = [ + "getrandom 0.4.2", + "rand 0.10.1", + "thiserror 2.0.20", + "vitaminc-protected 0.4.0", + "vitaminc-random-derives 0.4.0", "zeroize", ] @@ -4665,6 +5154,17 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "vitaminc-random-derives" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0931f7e84808c1072d77b30bab7f4e254334e038fc28929fbf7d647796b856cd" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "vitaminc-traits" version = "0.2.0-pre" @@ -4676,8 +5176,24 @@ dependencies = [ "rmp-serde", "serde", "thiserror 2.0.20", - "vitaminc-protected", - "vitaminc-random", + "vitaminc-protected 0.2.0-pre", + "vitaminc-random 0.2.0-pre", + "zeroize", +] + +[[package]] +name = "vitaminc-traits" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c806b05905d3a51c5440c64eb087bfe4b2a02e06dd4792ba9c53e005d42aeb5" +dependencies = [ + "anyhow", + "bytes", + "rmp-serde", + "serde", + "thiserror 2.0.20", + "vitaminc-protected 0.4.0", + "vitaminc-random 0.4.0", "zeroize", ] @@ -4721,6 +5237,15 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + [[package]] name = "wasip2" version = "1.0.3+wasi-0.2.9" @@ -4745,6 +5270,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" +[[package]] +name = "wasite" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fe902b4a6b8028a753d5424909b764ccf79b7a209eac9bf97e59cda9f71a42" +dependencies = [ + "wasi 0.14.7+wasi-0.2.4", +] + [[package]] name = "wasm-bindgen" version = "0.2.121" @@ -4884,7 +5418,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" dependencies = [ "libredox", - "wasite", + "wasite 0.1.0", +] + +[[package]] +name = "whoami" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626c4bac6755d76ffc12cb01b2eac751db1996b9e0041de9aa02c8c211ddc82c" +dependencies = [ + "libc", + "libredox", + "objc2-system-configuration", + "wasite 1.0.2", + "web-sys", ] [[package]] @@ -5453,9 +6000,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29486723fdd2bdb0c234174dc242051a46ac0c82935ccaa39e9781dbf7bcc6d2" dependencies = [ "base64", - "cipherstash-config", + "cipherstash-config 0.42.2", + "const-hex", + "cts-common 0.42.2", + "fake", + "getrandom 0.2.16", + "opaque-debug", + "rand 0.8.6", + "serde", + "static_assertions", + "thiserror 1.0.69", + "utoipa", + "uuid", + "validator", + "zeroize", +] + +[[package]] +name = "zerokms-protocol" +version = "0.12.30" +dependencies = [ + "base64", + "cipherstash-config 0.42.3", "const-hex", - "cts-common", + "cts-common 0.42.3", "fake", "getrandom 0.2.16", "opaque-debug", diff --git a/packages/eql/Cargo.toml b/packages/eql/Cargo.toml index 0c8086b8f..ab1492558 100644 --- a/packages/eql/Cargo.toml +++ b/packages/eql/Cargo.toml @@ -26,6 +26,7 @@ members = [ "crates/eql-tests-macros", "crates/eql-bindings", "tests/sqlx", + "tests/encryption", ] default-members = ["tests/sqlx"] diff --git a/packages/eql/crates/eql-bindings/Cargo.toml b/packages/eql/crates/eql-bindings/Cargo.toml index 5c443d5cc..0196e914a 100644 --- a/packages/eql/crates/eql-bindings/Cargo.toml +++ b/packages/eql/crates/eql-bindings/Cargo.toml @@ -33,6 +33,16 @@ serde_json = "1" # caught by the `types:check` gate on the committed TS/JSON output. ts-rs = { version = "10", features = ["no-serde-warnings"] } schemars = "1" +# Unpublished until cipherstash-suite#2215 lands. Local development uses +# tasks/dev/with-stack-encrypt.sh; release/CI use the registry versions. +stack-encrypt = { version = "0.1.0", optional = true, default-features = false } +vitaminc-prf = { version = "0.4.0", optional = true } +base64 = { version = "0.22", optional = true } +hex = { version = "0.4", optional = true } + +[features] +default = [] +stack-encrypt = ["dep:stack-encrypt", "dep:vitaminc-prf", "dep:base64", "dep:hex"] [dev-dependencies] # Parity oracle: tests/catalog_parity.rs asserts the v3 domain inventory diff --git a/packages/eql/crates/eql-bindings/README.md b/packages/eql/crates/eql-bindings/README.md index 4812c331e..955ad8faa 100644 --- a/packages/eql/crates/eql-bindings/README.md +++ b/packages/eql/crates/eql-bindings/README.md @@ -98,3 +98,64 @@ evolution plan. [`ts-rs`]: https://github.com/Aleph-Alpha/ts-rs [`schemars`]: https://graham.cool/schemars/ + +## Rust text equality with Stack Encrypt + +The optional `stack-encrypt` feature enables `EncryptFrom` for `TextEq` +and `TextEqQuery`, and `DecryptInto` for `TextEq`. Other domains do not +yet expose these conversions. The derives are emitted by `eql-codegen`. + +```rust,ignore +use eql_bindings::{Identifier, v3::text::{TextEq, TextEqQuery}}; + +let column = Identifier::for_column("users", "email")?; +let keyset = cipher.default_keyset(); +let email = "alice@example.com".to_owned(); +let stored: TextEq = keyset.encrypt_as(&email, column.clone()).await?; +let query: TextEqQuery = keyset.encrypt_as(&email, column.clone()).await?; +let opened: String = cipher.decrypt_as(stored, column.into()).await?; +``` + +Both identifier components must be nonempty. Encryption stores the identifier +in `i`; its table and column supply the ciphertext AAD, term context, and ZeroKMS +descriptor. The storage field names `c` and `hm` add no further context. +Decryption validates `i` before retrieving keys. Passing an expected identifier, +as above, also checks the destination. `Default::default()` uses the stored +identifier alone, so moving a complete payload with its original `i` still opens. + +Ciphertext uses Vitamin C's native string encoding. Equality uses its exact +string PRF input, with no normalization or case folding: `café` and +`cafe\u{301}`, or `Alice` and `alice`, produce different terms. A query contains +only `v`, `i`, and `hm` and requests no data keys. Neither EQL type implements +the plaintext-side `Encrypt` or `Decrypt` traits. + +This is a **new producer profile**, independent of existing cipherstash-client +ciphertext and terms. `c` is `stack-encrypt:1:` followed by padded standard base64 of Stack +Encrypt's native `SealedValue` bytes; `hm` is the native 32-byte equality term +encoded as hex. Serde handles the final EQL JSON envelope only. SQL consumes +these payloads through the existing `public.eql_v3_text_eq` and +`eql_v3.query_text_eq` domains and their equality extractor index. Writers and +query producers for these values must use this same profile. + +### Developing against the unpublished crates + +Stack Encrypt and its runtime prerequisites must be published before the normal +registry-based build, CI, and `eql-bindings` publication can succeed. Regenerate +`Cargo.lock` without local overrides once they are published, before merging. +During development, use a checkout containing +[cipherstash-suite#2215](https://github.com/cipherstash/cipherstash-suite/pull/2215) +(tested with commit `cbb3e000009b5974e90ec7da98d015b4b19a7270`): + +```bash +# From packages/eql. Generates a temporary Cargo patch; no local paths are committed. +bash tasks/dev/with-stack-encrypt.sh /path/to/cipherstash-suite \ + test -p eql-encryption-tests +bash tasks/dev/with-stack-encrypt.sh /path/to/cipherstash-suite \ + test -p eql-bindings --features stack-encrypt + +# Against a disposable PostgreSQL database, using fresh encryption and a fake KMS: +psql "$EQL_TEST_DATABASE_URL" -v ON_ERROR_STOP=1 \ + -f crates/eql-bindings/sql/cipherstash-encrypt.sql +bash tasks/dev/with-stack-encrypt.sh /path/to/cipherstash-suite \ + test -p eql-encryption-tests --test text_eq -- --ignored +``` diff --git a/packages/eql/crates/eql-bindings/bindings/v3/Ciphertext.ts b/packages/eql/crates/eql-bindings/bindings/v3/Ciphertext.ts index 7beff648e..01c400762 100644 --- a/packages/eql/crates/eql-bindings/bindings/v3/Ciphertext.ts +++ b/packages/eql/crates/eql-bindings/bindings/v3/Ciphertext.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. /** - * mp_base85 source ciphertext — the `c` envelope key. + * Producer-encoded source ciphertext — the `c` envelope key. * * Required by every v3 domain CHECK; present on every payload. */ diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint.json index dcf39ca1d..f2d22fb58 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_eq.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_eq.json index 376e97e59..895726c7e 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_eq.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord.json index 4dd53489d..4087631d9 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord_ope.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord_ope.json index c9b7f2155..b48b3b984 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord_ope.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord_ore.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord_ore.json index ec0620b71..643631c7e 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord_ore.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_bigint_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_boolean.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_boolean.json index feee8688d..d8a5ad205 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_boolean.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_boolean.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date.json index 7265731c5..a0eedd9e6 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_eq.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_eq.json index 87c588b18..659840e85 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_eq.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord.json index e7a1898bd..39485b088 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord_ope.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord_ope.json index 67d2daa5b..8f4cf8216 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord_ope.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord_ore.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord_ore.json index 010869e58..63caa1ed2 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord_ore.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_date_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double.json index 2b2dc709f..bbad0b189 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_eq.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_eq.json index fea7dbc50..6338de607 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_eq.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord.json index d636c06d7..da2e629f3 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord_ope.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord_ope.json index 6a1ddba44..b27cdb37c 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord_ope.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord_ore.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord_ore.json index ebc8a6ded..e95ad4033 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord_ore.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_double_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer.json index 92d36fa97..15ba554eb 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_eq.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_eq.json index 7a07dc8dc..7d83e20ea 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_eq.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord.json index 096415e90..40684c7df 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord_ope.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord_ope.json index 7a9173d07..bec37060d 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord_ope.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord_ore.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord_ore.json index b270f38f9..3cdd5a4f9 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord_ore.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_integer_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_json.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_json.json index 5a206f9d9..67a7488ba 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_json.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_json.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric.json index 8d2d08a06..fb76317a5 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_eq.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_eq.json index 741c95904..de4c798bc 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_eq.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord.json index ae0074cf0..de1e85247 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord_ope.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord_ope.json index 2ffee02ff..09886d191 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord_ope.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord_ore.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord_ore.json index 9db393f7a..e3ad24415 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord_ore.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_numeric_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real.json index 52da42ac2..7e12f7197 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_eq.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_eq.json index ce0f03141..a0d474ca5 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_eq.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord.json index 69aeb260e..08ab922bb 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord_ope.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord_ope.json index 548832cd3..d6bc7fdec 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord_ope.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord_ore.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord_ore.json index c6b5ca349..bc8fdd5d8 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord_ore.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_real_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint.json index f04378a6f..7d3e346b4 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_eq.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_eq.json index a793597d4..342927953 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_eq.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord.json index 5e95b20ac..806ef68d3 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord_ope.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord_ope.json index 63f1b3dd2..627c73238 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord_ope.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord_ore.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord_ore.json index 8b84af97c..395c2a733 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord_ore.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_smallint_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text.json index 60a7fdafd..e50fcb359 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_eq.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_eq.json index b7d94f830..89be47f98 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_eq.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_match.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_match.json index e7f9f34cf..df5e41012 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_match.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_match.json @@ -11,7 +11,7 @@ "type": "array" }, "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord.json index 7ef5a1242..efd68d5b7 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord_ope.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord_ope.json index 0d6a0ab29..10151de25 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord_ope.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord_ore.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord_ore.json index 5f97ccc7d..ccd48ea8b 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord_ore.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_search.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_search.json index ee05c7bfc..bd942a291 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_search.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_search.json @@ -11,7 +11,7 @@ "type": "array" }, "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_search_ore.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_search_ore.json index 17121f697..3567eac0d 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_search_ore.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_text_search_ore.json @@ -11,7 +11,7 @@ "type": "array" }, "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp.json index 16e97bd9d..853050330 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_eq.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_eq.json index c76a60ade..97e064b29 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_eq.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord.json index e0a74f83a..4462017f6 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord_ope.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord_ope.json index cdf411d46..da2ae0deb 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord_ope.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord_ore.json b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord_ore.json index 0a286d1c1..e5c5eefcc 100644 --- a/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord_ore.json +++ b/packages/eql/crates/eql-bindings/schema/v3/eql_v3_timestamp_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/crates/eql-bindings/src/encryption.rs b/packages/eql/crates/eql-bindings/src/encryption.rs new file mode 100644 index 000000000..8f9b34997 --- /dev/null +++ b/packages/eql/crates/eql-bindings/src/encryption.rs @@ -0,0 +1,193 @@ +//! Opt-in Stack Encrypt support for [`TextEq`] and [`TextEqQuery`]. +//! +//! The target declares its operations; Stack Encrypt invokes Vitamin C's +//! plaintext implementations. These adapters only encode encrypted outputs. +//! Equality uses Vitamin C's exact string semantics: no normalization or case +//! folding. The stored identifier supplies context for ciphertext and terms. +//! +//! This is a new producer profile, independent of existing cipherstash-client +//! ciphertext and terms. The EQL envelope and SQL domains remain unchanged. +//! +//! [`TextEq`]: crate::v3::text::TextEq +//! [`TextEqQuery`]: crate::v3::text::TextEqQuery +//! +//! Query terms cannot recover plaintext: +//! ```compile_fail +//! use eql_bindings::v3::text::TextEqQuery; +//! fn require>() {} +//! require::(); +//! ``` +//! An encrypted EQL record is not a plaintext value: +//! ```compile_fail +//! use eql_bindings::v3::text::TextEq; +//! fn require() {} +//! require::(); +//! ``` +//! The text domain accepts text, and requires a concrete column identifier: +//! ```compile_fail +//! use eql_bindings::v3::text::TextEq; +//! fn require>() {} +//! require::(); +//! ``` +//! ```compile_fail +//! use eql_bindings::v3::text::TextEq; +//! fn require>() {} +//! require::(); +//! ``` + +use base64::{engine::general_purpose::STANDARD, Engine as _}; +use stack_encrypt::sem::EqualityTerm; +use stack_encrypt::target::transcode::{Reader, Transcode, Visitor}; +use stack_encrypt::target::{self, AeadContext, CallerContext}; +use stack_encrypt::{ + Aad, AadPiece, CipherText, Decrypt, DecryptField, DecryptInto, Decryptable, Decryption, + Encrypt, EncryptFrom, Encryption, Error, IntoAad, IntoPrfContext, MaybeEmpty, NonEmpty, + PrfContext, SealedValue, StackCipherText, +}; +use vitaminc_prf::PrfValue; + +use crate::{ + v3::terms::{Ciphertext, Hmac256}, + Identifier, +}; + +// An explicit producer/version marker prevents a legacy EQL ciphertext from +// being interpreted as a native Stack Encrypt leaf. The body is base64 of the +// cipher's own SealedValue encoding, never a serialization of the plaintext. +const CIPHERTEXT_PREFIX: &str = "stack-encrypt:1:"; + +fn codec(error: impl std::error::Error + Send + Sync + 'static) -> Error { + Error::Other(Box::new(error)) +} + +impl Identifier { + /// Validate the table and column used for writes and equality probes. + pub fn for_column( + table: impl Into, + column: impl Into, + ) -> Result, stack_encrypt::EmptyError> { + NonEmpty::new(Self { + t: table.into(), + c: column.into(), + }) + } +} + +impl MaybeEmpty for Identifier { + fn is_empty(&self) -> bool { + self.t.is_empty() || self.c.is_empty() + } +} +impl<'a> IntoAad<'a> for Identifier { + fn into_aad(self) -> Aad<'a> { + (self.t, self.c).into_aad() + } + fn into_aad_piece(self) -> AadPiece<'a> { + (self.t, self.c).into_aad_piece() + } +} +impl<'a> IntoPrfContext<'a> for Identifier { + fn into_prf_context(self) -> PrfContext<'a> { + (self.t, self.c).into_prf_context() + } +} + +/// Builds the final EQL ciphertext string from a native sealed leaf. +#[doc(hidden)] +pub struct CiphertextVisitor; +impl Visitor for CiphertextVisitor { + type Value = Ciphertext; + fn sealed(self, leaf: SealedValue) -> Result { + let mut encoded = String::from(CIPHERTEXT_PREFIX); + STANDARD.encode_string(leaf.to_bytes(), &mut encoded); + Ok(Ciphertext(encoded)) + } +} +impl Transcode for Ciphertext { + type Visitor = CiphertextVisitor; + fn visitor() -> Self::Visitor { + CiphertextVisitor + } +} +impl Reader for Ciphertext { + fn read(self, visitor: V) -> Result { + let body = self + .0 + .strip_prefix(CIPHERTEXT_PREFIX) + .ok_or_else(|| Error::Other("unsupported EQL ciphertext producer or version".into()))?; + let bytes = STANDARD.decode(body).map_err(codec)?; + visitor.sealed(SealedValue::from_bytes(&bytes).map_err(codec)?) + } +} + +impl EncryptFrom for Ciphertext { + type Context = AeadContext; + fn encryption<'s, K: 'static>(context: Self::Context) -> Encryption<'s, S, Self, K> + where + S: 's, + { + target::ciphertext(context).transcode() + } +} + +struct NativeCiphertextVisitor; +impl Visitor for NativeCiphertextVisitor { + type Value = StackCipherText; + fn sealed(self, leaf: SealedValue) -> Result { + Ok(CipherText::Single(leaf)) + } +} +impl + 'static> DecryptInto

for Ciphertext { + type Context = AeadContext; + fn decryption(self, context: Self::Context) -> Decryption { + match self.read(NativeCiphertextVisitor) { + Ok(native) => target::open(native, context), + Err(error) => Decryption::failed(error), + } + } +} +impl Decryptable for Ciphertext { + const DECRYPTABLE: bool = true; +} +impl DecryptField for Ciphertext +where + Self: DecryptInto

, + Ctx: Into<>::Context>, +{ + fn decryption_field(self, context: Ctx) -> Option> { + Some(self.decryption(context.into())) + } +} + +/// Builds EQL's hex equality term from the native HMAC output. +#[doc(hidden)] +pub struct EqualityVisitor; +impl Visitor for EqualityVisitor { + type Value = Hmac256; + fn equality(self, term: EqualityTerm) -> Result { + Ok(Hmac256(hex::encode(term.as_bytes()))) + } +} +impl Transcode for Hmac256 { + type Visitor = EqualityVisitor; + fn visitor() -> Self::Visitor { + EqualityVisitor + } +} +impl EncryptFrom for Hmac256 { + type Context = CallerContext; + fn encryption<'s, K: 'static>(context: Self::Context) -> Encryption<'s, S, Self, K> + where + S: 's, + { + target::equality(context).transcode() + } +} +impl Decryptable for Hmac256 { + const DECRYPTABLE: bool = false; +} +impl DecryptField for Hmac256 { + fn decryption_field(self, _: Ctx) -> Option> { + None + } +} diff --git a/packages/eql/crates/eql-bindings/src/lib.rs b/packages/eql/crates/eql-bindings/src/lib.rs index 7846e5807..f7b7a6e91 100644 --- a/packages/eql/crates/eql-bindings/src/lib.rs +++ b/packages/eql/crates/eql-bindings/src/lib.rs @@ -34,6 +34,9 @@ pub mod from_v2; pub mod sql; pub mod v3; +#[cfg(feature = "stack-encrypt")] +pub mod encryption; + /// EQL wire-format version. Hard-coded to `3` for every payload in the /// [`v3`] tier, whose generated domain CHECKs assert `VALUE->>'v' = '3'`. /// (The legacy `eql_v2` wire stays `v: 2` — see diff --git a/packages/eql/crates/eql-bindings/src/v3/terms.rs b/packages/eql/crates/eql-bindings/src/v3/terms.rs index e2300f302..1e9d97de7 100644 --- a/packages/eql/crates/eql-bindings/src/v3/terms.rs +++ b/packages/eql/crates/eql-bindings/src/v3/terms.rs @@ -15,7 +15,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use ts_rs::TS; -/// mp_base85 source ciphertext — the `c` envelope key. +/// Producer-encoded source ciphertext — the `c` envelope key. /// /// Required by every v3 domain CHECK; present on every payload. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, TS, JsonSchema)] diff --git a/packages/eql/crates/eql-bindings/src/v3/text.rs b/packages/eql/crates/eql-bindings/src/v3/text.rs index de7ce7b2c..66f682952 100644 --- a/packages/eql/crates/eql-bindings/src/v3/text.rs +++ b/packages/eql/crates/eql-bindings/src/v3/text.rs @@ -40,11 +40,16 @@ impl DomainType for Text { /// `public.eql_v3_text_eq` — equality domain. /// /// Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`. +#[cfg_attr(feature = "stack-encrypt", derive(stack_encrypt::EncryptFrom))] +#[cfg_attr(feature = "stack-encrypt", derive(stack_encrypt::DecryptInto))] +#[cfg_attr(feature = "stack-encrypt", stash(plaintext = String))] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] pub struct TextEq { + #[cfg_attr(feature = "stack-encrypt", stash(default))] pub v: SchemaVersion, + #[cfg_attr(feature = "stack-encrypt", stash(context_field))] pub i: Identifier, pub c: Ciphertext, pub hm: Hmac256, @@ -271,11 +276,15 @@ impl DomainType for TextSearch { /// `eql_v3.query_text_eq` — equality domain query operand. /// /// Operators: `=` `<>`. Required keys: `v` `i` `hm`. +#[cfg_attr(feature = "stack-encrypt", derive(stack_encrypt::EncryptFrom))] +#[cfg_attr(feature = "stack-encrypt", stash(plaintext = String))] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] pub struct TextEqQuery { + #[cfg_attr(feature = "stack-encrypt", stash(default))] pub v: SchemaVersion, + #[cfg_attr(feature = "stack-encrypt", stash(context_field))] pub i: Identifier, pub hm: Hmac256, } diff --git a/packages/eql/crates/eql-codegen/src/bindings.rs b/packages/eql/crates/eql-codegen/src/bindings.rs index 271fd71ee..7cb013f4f 100644 --- a/packages/eql/crates/eql-codegen/src/bindings.rs +++ b/packages/eql/crates/eql-codegen/src/bindings.rs @@ -136,6 +136,7 @@ fn render_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { let ident = format_ident!("{}", domain.struct_ident(family.name)); let sql_domain = crate::context::domain_name(&full); let [doc_summary, doc_blank, doc_detail] = struct_doc_lines(&full, domain); + let (encryption, version, context) = encryption_attrs(family, domain, false); // The envelope triple is hardcoded (not looped over `ENVELOPE_KEYS`) because // each key maps to a distinct Rust type: `v: SchemaVersion`, `i: Identifier`, @@ -143,8 +144,8 @@ fn render_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { // `eql_domains::ENVELOPE_KEYS` — `envelope_fields_match_catalog_keys` (below) // fails if that ever diverges. let mut fields = TokenStream::new(); - fields.extend(quote! { pub v: SchemaVersion, }); - fields.extend(quote! { pub i: Identifier, }); + fields.extend(quote! { #version pub v: SchemaVersion, }); + fields.extend(quote! { #context pub i: Identifier, }); fields.extend(quote! { pub c: Ciphertext, }); for term in Term::payload_terms(domain.terms) { let fid = format_ident!("{}", term.json_key()); @@ -163,6 +164,7 @@ fn render_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { #[doc = #doc_summary] #[doc = #doc_blank] #[doc = #doc_detail] + #encryption #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -206,6 +208,7 @@ fn render_query_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { // Query operands live in the public-API schema, NOT `public`: they are // never valid column types. let sql_domain = format!("eql_v3.{query_name}"); + let (encryption, version, context) = encryption_attrs(family, domain, true); // Query doc: same capability label + operator union as storage, but the // required-key list drops `c` (query operands omit the ciphertext). @@ -230,8 +233,8 @@ fn render_query_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { // Envelope minus `c`: `v`/`i` only. Kept in lockstep with the storage // struct's envelope triple (see `envelope_fields_match_catalog_keys`). let mut fields = TokenStream::new(); - fields.extend(quote! { pub v: SchemaVersion, }); - fields.extend(quote! { pub i: Identifier, }); + fields.extend(quote! { #version pub v: SchemaVersion, }); + fields.extend(quote! { #context pub i: Identifier, }); for term in Term::payload_terms(domain.terms) { let fid = format_ident!("{}", term.json_key()); let tid = format_ident!("{}", term.binding_newtype()); @@ -243,6 +246,7 @@ fn render_query_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { #[doc = #summary] #[doc = ""] #[doc = #detail] + #encryption #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -273,6 +277,31 @@ fn render_query_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { } } +/// Text equality is the first supported encryption domain. Keep attributes in +/// the generator so regeneration preserves the opt-in derives without enabling +/// other domains before their operations and plaintext contracts are supported. +fn encryption_attrs( + family: &DomainFamily, + domain: &Domain, + query: bool, +) -> (TokenStream, TokenStream, TokenStream) { + if family.name != "text" || domain.name != "eq" { + return Default::default(); + } + let decrypt = (!query).then( + || quote!(#[cfg_attr(feature = "stack-encrypt", derive(stack_encrypt::DecryptInto))]), + ); + ( + quote! { + #[cfg_attr(feature = "stack-encrypt", derive(stack_encrypt::EncryptFrom))] + #decrypt + #[cfg_attr(feature = "stack-encrypt", stash(plaintext = String))] + }, + quote!(#[cfg_attr(feature = "stack-encrypt", stash(default))]), + quote!(#[cfg_attr(feature = "stack-encrypt", stash(context_field))]), + ) +} + /// Render a whole family module (`integer.rs`, `text.rs`, …): the import header /// (exactly the term newtypes the family uses) followed by every domain's /// storage struct + impl, then a query twin for each term-bearing domain. diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint.json index dcf39ca1d..f2d22fb58 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_eq.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_eq.json index 376e97e59..895726c7e 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_eq.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord.json index 4dd53489d..4087631d9 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord_ope.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord_ope.json index c9b7f2155..b48b3b984 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord_ope.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord_ore.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord_ore.json index ec0620b71..643631c7e 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord_ore.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_bigint_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_boolean.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_boolean.json index feee8688d..d8a5ad205 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_boolean.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_boolean.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date.json index 7265731c5..a0eedd9e6 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_eq.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_eq.json index 87c588b18..659840e85 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_eq.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord.json index e7a1898bd..39485b088 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord_ope.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord_ope.json index 67d2daa5b..8f4cf8216 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord_ope.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord_ore.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord_ore.json index 010869e58..63caa1ed2 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord_ore.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_date_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double.json index 2b2dc709f..bbad0b189 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_eq.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_eq.json index fea7dbc50..6338de607 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_eq.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord.json index d636c06d7..da2e629f3 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord_ope.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord_ope.json index 6a1ddba44..b27cdb37c 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord_ope.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord_ore.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord_ore.json index ebc8a6ded..e95ad4033 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord_ore.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_double_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer.json index 92d36fa97..15ba554eb 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_eq.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_eq.json index 7a07dc8dc..7d83e20ea 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_eq.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord.json index 096415e90..40684c7df 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord_ope.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord_ope.json index 7a9173d07..bec37060d 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord_ope.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord_ore.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord_ore.json index b270f38f9..3cdd5a4f9 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord_ore.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_integer_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_json.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_json.json index 5a206f9d9..67a7488ba 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_json.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_json.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric.json index 8d2d08a06..fb76317a5 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_eq.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_eq.json index 741c95904..de4c798bc 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_eq.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord.json index ae0074cf0..de1e85247 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord_ope.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord_ope.json index 2ffee02ff..09886d191 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord_ope.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord_ore.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord_ore.json index 9db393f7a..e3ad24415 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord_ore.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_numeric_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real.json index 52da42ac2..7e12f7197 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_eq.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_eq.json index ce0f03141..a0d474ca5 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_eq.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord.json index 69aeb260e..08ab922bb 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord_ope.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord_ope.json index 548832cd3..d6bc7fdec 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord_ope.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord_ore.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord_ore.json index c6b5ca349..bc8fdd5d8 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord_ore.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_real_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint.json index f04378a6f..7d3e346b4 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_eq.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_eq.json index a793597d4..342927953 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_eq.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord.json index 5e95b20ac..806ef68d3 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord_ope.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord_ope.json index 63f1b3dd2..627c73238 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord_ope.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord_ore.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord_ore.json index 8b84af97c..395c2a733 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord_ore.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_smallint_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text.json index 60a7fdafd..e50fcb359 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_eq.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_eq.json index b7d94f830..89be47f98 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_eq.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_match.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_match.json index e7f9f34cf..df5e41012 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_match.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_match.json @@ -11,7 +11,7 @@ "type": "array" }, "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord.json index 7ef5a1242..efd68d5b7 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord_ope.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord_ope.json index 0d6a0ab29..10151de25 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord_ope.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord_ore.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord_ore.json index 5f97ccc7d..ccd48ea8b 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord_ore.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_search.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_search.json index ee05c7bfc..bd942a291 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_search.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_search.json @@ -11,7 +11,7 @@ "type": "array" }, "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_search_ore.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_search_ore.json index 17121f697..3567eac0d 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_search_ore.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_text_search_ore.json @@ -11,7 +11,7 @@ "type": "array" }, "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp.json index 16e97bd9d..853050330 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_eq.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_eq.json index c76a60ade..97e064b29 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_eq.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_eq.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Hmac256": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord.json index e0a74f83a..4462017f6 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord_ope.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord_ope.json index cdf411d46..da2ae0deb 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord_ope.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord_ope.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord_ore.json b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord_ore.json index 0a286d1c1..e5c5eefcc 100644 --- a/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord_ore.json +++ b/packages/eql/packages/eql/src/generated/schema/v3/eql_v3_timestamp_ord_ore.json @@ -1,7 +1,7 @@ { "$defs": { "Ciphertext": { - "description": "mp_base85 source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", + "description": "Producer-encoded source ciphertext — the `c` envelope key.\n\nRequired by every v3 domain CHECK; present on every payload.", "type": "string" }, "Identifier": { diff --git a/packages/eql/packages/eql/src/generated/v3/Ciphertext.ts b/packages/eql/packages/eql/src/generated/v3/Ciphertext.ts index 7beff648e..01c400762 100644 --- a/packages/eql/packages/eql/src/generated/v3/Ciphertext.ts +++ b/packages/eql/packages/eql/src/generated/v3/Ciphertext.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. /** - * mp_base85 source ciphertext — the `c` envelope key. + * Producer-encoded source ciphertext — the `c` envelope key. * * Required by every v3 domain CHECK; present on every payload. */ diff --git a/packages/eql/tasks/dev/with-stack-encrypt.sh b/packages/eql/tasks/dev/with-stack-encrypt.sh new file mode 100644 index 000000000..b9aac6f78 --- /dev/null +++ b/packages/eql/tasks/dev/with-stack-encrypt.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Use the unpublished sibling suite without committing a machine-specific path. +# Usage: tasks/dev/with-stack-encrypt.sh /path/to/cipherstash-suite test -p eql-bindings --features stack-encrypt +set -euo pipefail +suite_root=$(cd "${1:?supply the cipherstash-suite checkout}" && pwd) +shift +patch_file=$(mktemp) +trap 'rm -f "$patch_file"' EXIT +python3 - "$suite_root" > "$patch_file" <<'PY' +import json, pathlib, sys +suite = pathlib.Path(sys.argv[1]) +print('[patch.crates-io]') +for name in ('stack-encrypt', 'stack-kms'): + path = suite / 'packages' / name + if not (path / 'Cargo.toml').is_file(): + raise SystemExit(f'missing {path}/Cargo.toml') + print(f'{name} = {{ path = {json.dumps(str(path))} }}') +PY +command=${1:?supply a Cargo command such as test or check} +shift +# External subcommands (notably clippy) need the override after their name. +cargo "$command" --config "$patch_file" "$@" diff --git a/packages/eql/tests/encryption/Cargo.toml b/packages/eql/tests/encryption/Cargo.toml new file mode 100644 index 000000000..3ef0d2f5a --- /dev/null +++ b/packages/eql/tests/encryption/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "eql-encryption-tests" +version = "0.1.0" +edition = "2021" +publish = false + +# Keep crypto/Postgres test dependencies out of eql-bindings' default test build. +[dev-dependencies] +eql-bindings = { path = "../../crates/eql-bindings", features = ["stack-encrypt"] } +stack-encrypt = { version = "0.1.0", default-features = false } +stack-kms = { version = "0.1.0", default-features = false, features = ["test-support"] } +serde = "1" +serde_json = "1" +uuid = "1" +tokio = { version = "1", features = ["rt", "macros"] } +postgres = "0.19" diff --git a/packages/eql/tests/encryption/tests/common/mod.rs b/packages/eql/tests/encryption/tests/common/mod.rs new file mode 100644 index 000000000..e64883b5c --- /dev/null +++ b/packages/eql/tests/encryption/tests/common/mod.rs @@ -0,0 +1,68 @@ +use std::borrow::Cow; +use std::sync::{Arc, Mutex}; + +use stack_encrypt::StackCipher; +use stack_kms::{ + DataKey, DataKeySource, DataKeyWithTag, FakeDataKeySource, GenerateKeyPayload, IdentifiedBy, + IndexKey, IndexKeySource, RetrieveKeyPayload, UnverifiedContext, +}; +use uuid::Uuid; + +#[derive(Default)] +pub struct Calls { + pub generate: Vec>, + pub retrieve: Vec>, +} + +pub struct Observed { + inner: FakeDataKeySource, + calls: Arc>, +} +impl DataKeySource for Observed { + async fn generate_keys( + &self, + payloads: Vec>, + keyset: Option, + context: Option>, + ) -> Result, stack_kms::Error> { + self.calls + .lock() + .unwrap() + .generate + .push(payloads.iter().map(|p| p.descriptor.to_owned()).collect()); + self.inner.generate_keys(payloads, keyset, context).await + } + async fn retrieve_keys( + &self, + payloads: Vec>, + keyset: Option, + context: Option<&UnverifiedContext>, + ) -> Result, stack_kms::Error> { + self.calls + .lock() + .unwrap() + .retrieve + .push(payloads.iter().map(|p| p.descriptor.to_owned()).collect()); + self.inner.retrieve_keys(payloads, keyset, context).await + } +} +impl IndexKeySource for Observed { + async fn load_index_key( + &self, + keyset: Option, + ) -> Result<(Uuid, IndexKey), stack_kms::Error> { + self.inner.load_index_key(keyset).await + } +} +pub async fn cipher() -> (StackCipher, Arc>) { + let calls = Arc::new(Mutex::new(Calls::default())); + let cipher = StackCipher::builder() + .kms(Observed { + inner: FakeDataKeySource::new(), + calls: calls.clone(), + }) + .init() + .await + .unwrap(); + (cipher, calls) +} diff --git a/packages/eql/tests/encryption/tests/text_eq.rs b/packages/eql/tests/encryption/tests/text_eq.rs new file mode 100644 index 000000000..006ff6597 --- /dev/null +++ b/packages/eql/tests/encryption/tests/text_eq.rs @@ -0,0 +1,319 @@ +mod common; + +use eql_bindings::v3::terms::{Ciphertext, Hmac256}; +use eql_bindings::v3::text::{TextEq, TextEqQuery}; +use eql_bindings::{Identifier, SchemaVersion}; +use stack_encrypt::target::transcode::{Reader, Transcode, Visitor}; +use stack_encrypt::{ + Cipher, CipherText, Encrypt, Error, IntoAad, NonEmpty, SealedValue, StackCipherText, +}; + +fn column() -> NonEmpty { + Identifier::for_column("users", "email").unwrap() +} +struct NativeLeaf; +impl Visitor for NativeLeaf { + type Value = StackCipherText; + fn sealed(self, leaf: SealedValue) -> Result { + Ok(CipherText::Single(leaf)) + } +} + +#[tokio::test] +async fn text_round_trips_through_json_and_both_canonical_directions() { + let (cipher, _) = common::cipher().await; + let keyset = cipher.default_keyset(); + for text in [ + "alice@example.com", + "", + "雪☃", + "caf\u{e9}", + "cafe\u{301}", + "a\0b", + ] { + let value = text.to_owned(); + let stored: TextEq = keyset.encrypt_as(&value, column()).await.unwrap(); + let json = serde_json::to_value(&stored).unwrap(); + assert_eq!(json["v"], 3); + assert_eq!(json["i"], serde_json::json!({"t":"users", "c":"email"})); + assert_eq!(json.as_object().unwrap().len(), 4); + let stored: TextEq = serde_json::from_value(json).unwrap(); + let native = stored.c.clone().read(NativeLeaf).unwrap(); + let opened: String = cipher.decrypt(native, column()).await.unwrap(); + assert_eq!( + opened, value, + "EQL adds no plaintext encoding or envelope-field AAD" + ); + let opened: String = cipher.decrypt_as(stored, Default::default()).await.unwrap(); + assert_eq!(opened, value); + + let native = keyset.encrypt(value.clone(), column()).await.unwrap(); + let stored = TextEq { + v: SchemaVersion::CURRENT, + i: column().into_inner(), + c: native.read(Ciphertext::visitor()).unwrap(), + hm: keyset + .equality_term(value.clone(), column()) + .await + .unwrap() + .read(Hmac256::visitor()) + .unwrap(), + }; + let opened: String = cipher.decrypt_as(stored, column().into()).await.unwrap(); + assert_eq!( + opened, value, + "native ciphertext opens through the EQL target" + ); + } +} + +#[tokio::test] +async fn independent_queries_match_native_terms_without_requesting_data_keys() { + let (writer, _) = common::cipher().await; + let (reader, calls) = common::cipher().await; + let value = "café".to_owned(); + let stored: TextEq = writer + .default_keyset() + .encrypt_as(&value, column()) + .await + .unwrap(); + let query: TextEqQuery = reader + .default_keyset() + .encrypt_as(&value, column()) + .await + .unwrap(); + assert_eq!(query.hm, stored.hm); + let canonical = reader + .default_keyset() + .equality_term(value.clone(), column()) + .await + .unwrap() + .read(Hmac256::visitor()) + .unwrap(); + assert_eq!(query.hm, canonical); + assert_eq!(query.hm.0.len(), 64); + assert!(query.hm.0.bytes().all(|b| b.is_ascii_hexdigit())); + let json = serde_json::to_value(&query).unwrap(); + assert!(json.get("c").is_none()); + assert_eq!(serde_json::from_value::(json).unwrap(), query); + for different in ["CAFÉ", "cafe\u{301}", "café "] { + let probe: TextEqQuery = reader + .default_keyset() + .encrypt_as(&different.to_owned(), column()) + .await + .unwrap(); + assert_ne!(query.hm, probe.hm, "no added normalization or case folding"); + } + let calls = calls.lock().unwrap(); + assert!(calls.generate.is_empty()); + assert!(calls.retrieve.is_empty()); +} + +#[tokio::test] +async fn identifier_components_are_nonempty_and_keep_their_boundaries() { + assert!(Identifier::for_column("", "email").is_err()); + assert!(Identifier::for_column("users", "").is_err()); + let (cipher, calls) = common::cipher().await; + let keyset = cipher.default_keyset(); + let value = "text".to_owned(); + let mut outputs = Vec::new(); + for (t, c) in [ + ("ab", "c"), + ("a", "bc"), + ("users", "email"), + ("users", "name"), + ("other", "email"), + ] { + let stored: TextEq = keyset + .encrypt_as(&value, Identifier::for_column(t, c).unwrap()) + .await + .unwrap(); + for previous in &outputs { + assert_ne!(previous, &stored.hm); + } + outputs.push(stored.hm); + } + let calls = calls.lock().unwrap(); + let descriptors: std::collections::HashSet<_> = calls.generate.iter().flatten().collect(); + assert_eq!( + descriptors.len(), + 5, + "ZeroKMS descriptors also preserve identifier boundaries" + ); +} + +#[tokio::test] +async fn context_and_encoding_failures_are_rejected_before_retrieving_keys() { + let (cipher, calls) = common::cipher().await; + let stored: TextEq = cipher + .default_keyset() + .encrypt_as(&"secret".to_owned(), column()) + .await + .unwrap(); + let wrong = Identifier::for_column("users", "name").unwrap(); + assert!(cipher + .decrypt_as::(stored.clone(), wrong.clone().into()) + .await + .is_err()); + for empty_table in [true, false] { + let mut invalid = stored.clone(); + if empty_table { + invalid.i.t.clear(); + } else { + invalid.i.c.clear(); + } + assert!(cipher + .decrypt_as::(invalid, Default::default()) + .await + .is_err()); + } + for encoded in [ + "legacy-ciphertext", + "stack-encrypt:2:00000", + "stack-encrypt:1:", + "stack-encrypt:1:~", + "stack-encrypt:1:00000", + "stack-encrypt:1:AAAA", + "stack-encrypt:1:雪", + ] { + let mut invalid = stored.clone(); + invalid.c = Ciphertext(encoded.into()); + assert!(cipher + .decrypt_as::(invalid, Default::default()) + .await + .is_err()); + } + assert!(calls.lock().unwrap().retrieve.is_empty()); + let mut moved = stored; + moved.i = wrong.into_inner(); + assert!( + cipher + .decrypt_as::(moved, Default::default()) + .await + .is_err(), + "a different nonempty stored identifier fails authentication" + ); + assert_eq!(calls.lock().unwrap().retrieve.len(), 1); +} + +#[tokio::test] +async fn a_column_batches_generation_and_retrieval_under_the_same_descriptors() { + let (cipher, calls) = common::cipher().await; + let values = vec!["one".to_owned(), "two".to_owned(), "three".to_owned()]; + let records: Vec = cipher + .default_keyset() + .encrypt_as(&values, column()) + .await + .unwrap(); + let json = serde_json::to_string(&records).unwrap(); + let records: Vec = serde_json::from_str(&json).unwrap(); + let opened: Vec = cipher.decrypt_as(records, column().into()).await.unwrap(); + assert_eq!(opened, values); + let calls = calls.lock().unwrap(); + assert_eq!(calls.generate.len(), 1); + assert_eq!(calls.retrieve.len(), 1); + assert_eq!(calls.generate[0].len(), 3); + assert_eq!(calls.generate, calls.retrieve); +} + +#[derive(Clone)] +struct WithoutSerde(String); +impl Encrypt for WithoutSerde { + fn encrypt_with_aad<'a, C: Cipher, A: IntoAad<'a>>( + self, + cipher: C, + aad: A, + ) -> Result { + self.0.encrypt_with_aad(cipher, aad) + } +} +#[tokio::test] +async fn leaf_transcoding_requires_no_serde_and_refuses_container_shapes() { + let (cipher, _) = common::cipher().await; + let keyset = cipher.default_keyset(); + let leaf: Ciphertext = keyset + .encrypt_as(&WithoutSerde("native".into()), column().into()) + .await + .unwrap(); + let text: String = cipher.decrypt_as(leaf, column().into()).await.unwrap(); + assert_eq!(text, "native"); + assert!(keyset + .encrypt_as::<_, Ciphertext>(&vec!["one".to_owned()], column().into()) + .await + .is_err()); +} + +/// Needs an EQL v3 installation; uses fresh real ciphertext with a fake key source. +#[test] +#[ignore = "requires EQL_TEST_DATABASE_URL pointing to an EQL v3 database"] +fn postgres_stores_queries_indexes_and_returns_decryptable_text() { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + let (cipher, _) = runtime.block_on(common::cipher()); + let keyset = cipher.default_keyset(); + let mut db = postgres::Client::connect( + &std::env::var("EQL_TEST_DATABASE_URL").expect("EQL_TEST_DATABASE_URL"), + postgres::NoTls, + ) + .unwrap(); + db.batch_execute( + "CREATE TEMP TABLE text_eq_roundtrip (id int PRIMARY KEY, email public.eql_v3_text_eq); + CREATE INDEX text_eq_email_idx ON text_eq_roundtrip (eql_v3.eq_term(email));", + ) + .unwrap(); + for (id, value) in [ + (1, "alice@example.com"), + (2, "bob@example.com"), + (3, "alice@example.com"), + (4, "ALICE@example.com"), + ] { + let record: TextEq = runtime + .block_on(async { keyset.encrypt_as(&value.to_owned(), column()).await }) + .unwrap(); + let json = serde_json::to_string(&record).unwrap(); + db.execute( + "INSERT INTO text_eq_roundtrip VALUES ($1, $2::text::jsonb::public.eql_v3_text_eq)", + &[&id, &json], + ) + .unwrap(); + } + let (query_cipher, _) = runtime.block_on(common::cipher()); + let query: TextEqQuery = runtime + .block_on(async { + query_cipher + .default_keyset() + .encrypt_as(&"alice@example.com".to_owned(), column()) + .await + }) + .unwrap(); + let json = serde_json::to_string(&query).unwrap(); + let rows = db.query("SELECT id, email::text FROM text_eq_roundtrip WHERE email = $1::text::jsonb::eql_v3.query_text_eq ORDER BY id", &[&json]).unwrap(); + assert_eq!( + rows.iter().map(|r| r.get::<_, i32>(0)).collect::>(), + [1, 3] + ); + for row in rows { + let record: TextEq = serde_json::from_str(row.get::<_, &str>(1)).unwrap(); + let value: String = runtime + .block_on(async { cipher.decrypt_as(record, column().into()).await }) + .unwrap(); + assert_eq!(value, "alice@example.com"); + } + let different = db.query("SELECT id FROM text_eq_roundtrip WHERE email <> $1::text::jsonb::eql_v3.query_text_eq ORDER BY id", &[&json]).unwrap(); + assert_eq!( + different + .iter() + .map(|r| r.get::<_, i32>(0)) + .collect::>(), + [2, 4] + ); + db.batch_execute("SET enable_seqscan = off").unwrap(); + let plan = db.query("EXPLAIN SELECT * FROM text_eq_roundtrip WHERE email = $1::text::jsonb::eql_v3.query_text_eq", &[&json]).unwrap(); + assert!( + plan.iter() + .any(|r| r.get::<_, &str>(0).contains("text_eq_email_idx")), + "equality can use the extractor index" + ); +} From 3669ca3b1849637d3b6afd0b9b358596ac0843cc Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Sat, 12 Sep 2026 21:24:48 -0400 Subject: [PATCH 2/3] ci(eql): stop encryption checks on failure The workflow's custom bash shell omits errexit, so a successful final command could hide an earlier failure. Enable fail-fast behavior in both new checks and document the internal test crate in the repository map and security scope. Refs #971 --- .github/workflows/test-eql.yml | 2 ++ AGENTS.md | 2 +- SECURITY.md | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-eql.yml b/.github/workflows/test-eql.yml index be171ce16..6153bdc29 100644 --- a/.github/workflows/test-eql.yml +++ b/.github/workflows/test-eql.yml @@ -568,6 +568,7 @@ jobs: PGPASSWORD: postgres EQL_TEST_DATABASE_URL: "host=localhost port=7433 user=postgres password=postgres dbname=postgres" run: | + set -euo pipefail cargo test --locked -p eql-bindings --features stack-encrypt cargo test --locked -p eql-encryption-tests cargo clippy --locked -p eql-bindings -p eql-encryption-tests --all-features --all-targets -- -D warnings @@ -576,6 +577,7 @@ jobs: - name: Compile Rust text equality for WASI without HTTP run: | + set -euo pipefail rustup target add wasm32-wasip1 cargo check --locked -p eql-bindings --features stack-encrypt --target wasm32-wasip1 diff --git a/AGENTS.md b/AGENTS.md index 239450d94..06727dd85 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,7 +83,7 @@ If these variables are missing, tests that require live encryption will fail or - `packages/utils`: Shared config (`utils/config`) and logger (`utils/logger`) - `packages/bench`: Performance / index-engagement benchmarks (private, not published) - `packages/protect-ffi`: Native FFI bindings to the CipherStash Client SDK (`@cipherstash/protect-ffi`) — the Rust core that `packages/stack` encrypts and decrypts through, absorbed from `cipherstash/protectjs-ffi`. Contains a **nested Cargo workspace** (`crates/`) and six per-platform binary packages under `platforms/*`, each published as `@cipherstash/protect-ffi-` and linked here via `workspace:*`. Also holds the repo's live FFI integration suite at `integration-tests/` — a private workspace member (`@cipherstash/ffi-integration-tests`) enrolled by its own literal entry in `pnpm-workspace.yaml`, needing Docker and credentials, and deliberately carrying **no `test` script** so `pnpm test` cannot reach it. See the "Working on protect-ffi" notes below before touching it — its default `test` and `build` are deliberately Rust-free. -- `packages/eql`: The Encrypt Query Language subtree — the SQL bundle that stores and queries encrypted payloads — absorbed from `cipherstash/encrypt-query-language`. **The directory is the subtree root, not the package.** It was imported at a *verbatim prefix* so its repo-root-relative paths (mise tasks, `Doxyfile`, `sync-generated.mjs`) keep resolving, which puts the npm package `@cipherstash/eql` two levels down at `packages/eql/packages/eql` — the same shape as `packages/protect-ffi/platforms/*`, and enrolled the same way, by an explicit `packages/eql/packages/*` glob in `pnpm-workspace.yaml`. The subtree root deliberately carries no `package.json`. Also contains a **nested Cargo workspace** at `packages/eql/crates/` (`eql-bindings`, published in lockstep with the npm package, plus `eql-domains` / `eql-codegen` / `eql-tests-macros`, which are not), a SQLx test crate at `packages/eql/tests/sqlx`, an ~900-line `mise.toml` task surface, its own `AGENTS.md`, and `docs/`. See the "Working on EQL" notes below before touching it. +- `packages/eql`: The Encrypt Query Language subtree — the SQL bundle that stores and queries encrypted payloads — absorbed from `cipherstash/encrypt-query-language`. **The directory is the subtree root, not the package.** It was imported at a *verbatim prefix* so its repo-root-relative paths (mise tasks, `Doxyfile`, `sync-generated.mjs`) keep resolving, which puts the npm package `@cipherstash/eql` two levels down at `packages/eql/packages/eql` — the same shape as `packages/protect-ffi/platforms/*`, and enrolled the same way, by an explicit `packages/eql/packages/*` glob in `pnpm-workspace.yaml`. The subtree root deliberately carries no `package.json`. Also contains a **nested Cargo workspace** at `packages/eql/crates/` (`eql-bindings`, published in lockstep with the npm package, plus `eql-domains` / `eql-codegen` / `eql-tests-macros`, which are not), a SQLx test crate at `packages/eql/tests/sqlx`, an unpublished encryption test crate at `packages/eql/tests/encryption` (real encryption with a fake key source, plus optional PostgreSQL coverage), an ~900-line `mise.toml` task surface, its own `AGENTS.md`, and `docs/`. See the "Working on EQL" notes below before touching it. **Repository ownership:** EQL now lives in `cipherstash/stack`. File and update EQL issues in this repository, never in the historical `cipherstash/encrypt-query-language` repository. Old upstream issue and PR diff --git a/SECURITY.md b/SECURITY.md index eab646082..34295212e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -26,6 +26,11 @@ This repository also carries the source of the **`eql-bindings`** Rust crate lockstep with `@cipherstash/eql`. It is in scope for security reports on the same terms as the npm packages above. +The `eql-encryption-tests` Rust crate (`packages/eql/tests/encryption`) is +an unpublished test harness for these bindings. It uses real encryption with +a fake key source and an optional disposable PostgreSQL database; it adds no +published package or production service. + > **Note on publishing.** `@cipherstash/eql` and the `eql-bindings` crate are > developed here but are *published* from `cipherstash/encrypt-query-language` > until the Phase 5 cutover in From 545c70094e75c5481380b894bb01a29ae503982f Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Sat, 12 Sep 2026 21:44:42 -0400 Subject: [PATCH 3/3] docs(eql): show text encryption in Rustdoc Make successful encryption discoverable alongside the type contracts. Execute the displayed example in the encryption test crate so its runtime and fake key source stay out of the bindings' default tests. Generate plaintext/context descriptions and EQL reference links with the Rust types so regeneration preserves them. Refs #971 --- AGENTS.md | 2 +- SECURITY.md | 5 +- packages/eql/crates/eql-bindings/Cargo.toml | 3 + packages/eql/crates/eql-bindings/README.md | 12 +++ .../eql/crates/eql-bindings/src/encryption.rs | 32 ++++++ .../eql-bindings/src/encryption/example.rs | 32 ++++++ packages/eql/crates/eql-bindings/src/lib.rs | 7 ++ .../eql/crates/eql-bindings/src/v3/bigint.rs | 45 ++++++++ .../eql/crates/eql-bindings/src/v3/boolean.rs | 5 + .../eql/crates/eql-bindings/src/v3/date.rs | 45 ++++++++ .../eql/crates/eql-bindings/src/v3/double.rs | 45 ++++++++ .../eql/crates/eql-bindings/src/v3/integer.rs | 45 ++++++++ .../eql/crates/eql-bindings/src/v3/json.rs | 20 ++++ .../eql-bindings/src/v3/json_storage.rs | 5 + .../eql/crates/eql-bindings/src/v3/numeric.rs | 45 ++++++++ .../eql/crates/eql-bindings/src/v3/real.rs | 45 ++++++++ .../crates/eql-bindings/src/v3/smallint.rs | 45 ++++++++ .../eql/crates/eql-bindings/src/v3/text.rs | 101 ++++++++++++++++++ .../crates/eql-bindings/src/v3/timestamp.rs | 45 ++++++++ .../eql/crates/eql-codegen/src/bindings.rs | 38 +++++++ packages/eql/tests/encryption/Cargo.toml | 7 ++ 21 files changed, 626 insertions(+), 3 deletions(-) create mode 100644 packages/eql/crates/eql-bindings/src/encryption/example.rs diff --git a/AGENTS.md b/AGENTS.md index 06727dd85..dfc7759d6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,7 +83,7 @@ If these variables are missing, tests that require live encryption will fail or - `packages/utils`: Shared config (`utils/config`) and logger (`utils/logger`) - `packages/bench`: Performance / index-engagement benchmarks (private, not published) - `packages/protect-ffi`: Native FFI bindings to the CipherStash Client SDK (`@cipherstash/protect-ffi`) — the Rust core that `packages/stack` encrypts and decrypts through, absorbed from `cipherstash/protectjs-ffi`. Contains a **nested Cargo workspace** (`crates/`) and six per-platform binary packages under `platforms/*`, each published as `@cipherstash/protect-ffi-` and linked here via `workspace:*`. Also holds the repo's live FFI integration suite at `integration-tests/` — a private workspace member (`@cipherstash/ffi-integration-tests`) enrolled by its own literal entry in `pnpm-workspace.yaml`, needing Docker and credentials, and deliberately carrying **no `test` script** so `pnpm test` cannot reach it. See the "Working on protect-ffi" notes below before touching it — its default `test` and `build` are deliberately Rust-free. -- `packages/eql`: The Encrypt Query Language subtree — the SQL bundle that stores and queries encrypted payloads — absorbed from `cipherstash/encrypt-query-language`. **The directory is the subtree root, not the package.** It was imported at a *verbatim prefix* so its repo-root-relative paths (mise tasks, `Doxyfile`, `sync-generated.mjs`) keep resolving, which puts the npm package `@cipherstash/eql` two levels down at `packages/eql/packages/eql` — the same shape as `packages/protect-ffi/platforms/*`, and enrolled the same way, by an explicit `packages/eql/packages/*` glob in `pnpm-workspace.yaml`. The subtree root deliberately carries no `package.json`. Also contains a **nested Cargo workspace** at `packages/eql/crates/` (`eql-bindings`, published in lockstep with the npm package, plus `eql-domains` / `eql-codegen` / `eql-tests-macros`, which are not), a SQLx test crate at `packages/eql/tests/sqlx`, an unpublished encryption test crate at `packages/eql/tests/encryption` (real encryption with a fake key source, plus optional PostgreSQL coverage), an ~900-line `mise.toml` task surface, its own `AGENTS.md`, and `docs/`. See the "Working on EQL" notes below before touching it. +- `packages/eql`: The Encrypt Query Language subtree — the SQL bundle that stores and queries encrypted payloads — absorbed from `cipherstash/encrypt-query-language`. **The directory is the subtree root, not the package.** It was imported at a *verbatim prefix* so its repo-root-relative paths (mise tasks, `Doxyfile`, `sync-generated.mjs`) keep resolving, which puts the npm package `@cipherstash/eql` two levels down at `packages/eql/packages/eql` — the same shape as `packages/protect-ffi/platforms/*`, and enrolled the same way, by an explicit `packages/eql/packages/*` glob in `pnpm-workspace.yaml`. The subtree root deliberately carries no `package.json`. Also contains a **nested Cargo workspace** at `packages/eql/crates/` (`eql-bindings`, published in lockstep with the npm package, plus `eql-domains` / `eql-codegen` / `eql-tests-macros`, which are not), a SQLx test crate at `packages/eql/tests/sqlx`, an unpublished encryption test crate at `packages/eql/tests/encryption` (real encryption with a fake key source, the executable Rustdoc example at `packages/eql/crates/eql-bindings/src/encryption/example.rs`, plus optional PostgreSQL coverage), an ~900-line `mise.toml` task surface, its own `AGENTS.md`, and `docs/`. See the "Working on EQL" notes below before touching it. **Repository ownership:** EQL now lives in `cipherstash/stack`. File and update EQL issues in this repository, never in the historical `cipherstash/encrypt-query-language` repository. Old upstream issue and PR diff --git a/SECURITY.md b/SECURITY.md index 34295212e..6249f4ba2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -28,8 +28,9 @@ same terms as the npm packages above. The `eql-encryption-tests` Rust crate (`packages/eql/tests/encryption`) is an unpublished test harness for these bindings. It uses real encryption with -a fake key source and an optional disposable PostgreSQL database; it adds no -published package or production service. +a fake key source, executes the Rustdoc text encryption example, and provides +optional disposable PostgreSQL coverage. It adds no published package or +production service. > **Note on publishing.** `@cipherstash/eql` and the `eql-bindings` crate are > developed here but are *published* from `cipherstash/encrypt-query-language` diff --git a/packages/eql/crates/eql-bindings/Cargo.toml b/packages/eql/crates/eql-bindings/Cargo.toml index 0196e914a..1724f7e41 100644 --- a/packages/eql/crates/eql-bindings/Cargo.toml +++ b/packages/eql/crates/eql-bindings/Cargo.toml @@ -44,6 +44,9 @@ hex = { version = "0.4", optional = true } default = [] stack-encrypt = ["dep:stack-encrypt", "dep:vitaminc-prf", "dep:base64", "dep:hex"] +[package.metadata.docs.rs] +features = ["stack-encrypt"] + [dev-dependencies] # Parity oracle: tests/catalog_parity.rs asserts the v3 domain inventory # exactly covers eql_domains::CATALOG, so the types here cannot drift from diff --git a/packages/eql/crates/eql-bindings/README.md b/packages/eql/crates/eql-bindings/README.md index 955ad8faa..58e84301a 100644 --- a/packages/eql/crates/eql-bindings/README.md +++ b/packages/eql/crates/eql-bindings/README.md @@ -105,6 +105,18 @@ The optional `stack-encrypt` feature enables `EncryptFrom` for `TextEq` and `TextEqQuery`, and `DecryptInto` for `TextEq`. Other domains do not yet expose these conversions. The derives are emitted by `eql-codegen`. +The generated API docs include a complete, executable example in the +`eql_bindings::encryption` module, from cipher setup through decryption. Its +[source](src/encryption/example.rs) is also run by the encryption test crate: + +```bash +# From packages/eql, using the unpublished suite checkout: +bash tasks/dev/with-stack-encrypt.sh /path/to/cipherstash-suite \ + test -p eql-encryption-tests --test text_eq_example +``` + +With an initialized cipher, the essential calls are: + ```rust,ignore use eql_bindings::{Identifier, v3::text::{TextEq, TextEqQuery}}; diff --git a/packages/eql/crates/eql-bindings/src/encryption.rs b/packages/eql/crates/eql-bindings/src/encryption.rs index 8f9b34997..553796c4e 100644 --- a/packages/eql/crates/eql-bindings/src/encryption.rs +++ b/packages/eql/crates/eql-bindings/src/encryption.rs @@ -11,6 +11,38 @@ //! [`TextEq`]: crate::v3::text::TextEq //! [`TextEqQuery`]: crate::v3::text::TextEqQuery //! +//! # Example +//! +//! Enable the `stack-encrypt` feature on `eql-bindings`. Both targets accept +//! a [`String`] as plaintext and a [`NonEmpty`] as context. +//! [`Identifier::for_column`] validates the table and column; the returned +//! identifier is passed directly to `encrypt_as` and stored in the output's `i`. +//! The output type selects the encryption operations: [`TextEq`] includes +//! ciphertext and an equality term, while [`TextEqQuery`] contains only the term. +//! +//! This complete example runs locally without credentials. It uses real +//! encryption with `stack_kms::FakeDataKeySource`, whose keys exist only for +//! this process. Add `stack-encrypt`, `stack-kms` (with `default-features = false` +//! and `features = ["test-support"]`), and `tokio` (with `features = ["rt", "macros"]`) +//! to your example's dependencies. Stack Encrypt is currently unpublished; +//! use the local development helper described in the crate README. +//! +//! The code below is executed by `cargo test -p eql-encryption-tests --test +//! text_eq_example`. It is excluded from this crate's doctests so its optional +//! test key source and runtime remain in the separate encryption test crate. +#![doc = concat!("```rust,ignore\n", include_str!("encryption/example.rs"), "\n```")] +//! +//! For ZeroKMS, enable Stack Encrypt's `http` feature and replace the fake-key +//! builder with `StackCipher::new().await?`, using your configured CipherStash +//! credentials. The encryption and decryption calls are identical. +//! +//! Use the same table and column identifier for writes and queries. Passing +//! `column.into()` on decryption also checks that the stored identifier matches +//! the expected destination before retrieving keys. `Default::default()` instead +//! uses the stored identifier alone. +//! +//! # Unsupported conversions +//! //! Query terms cannot recover plaintext: //! ```compile_fail //! use eql_bindings::v3::text::TextEqQuery; diff --git a/packages/eql/crates/eql-bindings/src/encryption/example.rs b/packages/eql/crates/eql-bindings/src/encryption/example.rs new file mode 100644 index 000000000..58a482b94 --- /dev/null +++ b/packages/eql/crates/eql-bindings/src/encryption/example.rs @@ -0,0 +1,32 @@ +use eql_bindings::{ + v3::text::{TextEq, TextEqQuery}, + Identifier, +}; +use stack_encrypt::StackCipher; +use stack_kms::FakeDataKeySource; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<(), Box> { + // Local example only: these keys are held in memory, not in ZeroKMS. + let cipher = StackCipher::builder() + .kms(FakeDataKeySource::new()) + .init() + .await?; + let keyset = cipher.default_keyset(); + let column = Identifier::for_column("users", "email")?; + let email = String::from("alice@example.com"); + + // The output type selects ciphertext plus the equality term. + let stored: TextEq = keyset.encrypt_as(&email, column.clone()).await?; + assert_eq!(stored.i.t, "users"); + assert_eq!(stored.i.c, "email"); + + // Use the same identifier to produce an equality query for this column. + let query: TextEqQuery = keyset.encrypt_as(&email, column.clone()).await?; + assert_eq!(stored.hm, query.hm); + + // Decrypt to String, also checking the expected table and column. + let opened: String = cipher.decrypt_as(stored, column.into()).await?; + assert_eq!(opened, email); + Ok(()) +} diff --git a/packages/eql/crates/eql-bindings/src/lib.rs b/packages/eql/crates/eql-bindings/src/lib.rs index f7b7a6e91..b88f65b61 100644 --- a/packages/eql/crates/eql-bindings/src/lib.rs +++ b/packages/eql/crates/eql-bindings/src/lib.rs @@ -25,6 +25,13 @@ //! //! Wire rule: **field names ARE wire names** — no `#[serde(rename)]` //! anywhere. The struct definition reads exactly like the JSON payload. +//! +//! Enable the `stack-encrypt` feature to encrypt Rust strings into `TextEq` +//! storage values and `TextEqQuery` search values. +#![cfg_attr( + feature = "stack-encrypt", + doc = "See the [complete encryption example](encryption#example), including cipher setup, table/column identifiers, and decryption." +)] use schemars::JsonSchema; use serde::{Deserialize, Serialize}; diff --git a/packages/eql/crates/eql-bindings/src/v3/bigint.rs b/packages/eql/crates/eql-bindings/src/v3/bigint.rs index 62ae38ffe..cf9b64b1b 100644 --- a/packages/eql/crates/eql-bindings/src/v3/bigint.rs +++ b/packages/eql/crates/eql-bindings/src/v3/bigint.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_bigint` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL bigint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -40,6 +45,11 @@ impl DomainType for Bigint { /// `public.eql_v3_bigint_eq` — equality domain. /// /// Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL bigint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -72,6 +82,11 @@ impl DomainType for BigintEq { /// `public.eql_v3_bigint_ord_ore` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL bigint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -104,6 +119,11 @@ impl DomainType for BigintOrdOre { /// `public.eql_v3_bigint_ord` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL bigint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -136,6 +156,11 @@ impl DomainType for BigintOrd { /// `public.eql_v3_bigint_ord_ope` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL bigint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -168,6 +193,11 @@ impl DomainType for BigintOrdOpe { /// `eql_v3.query_bigint_eq` — equality domain query operand. /// /// Operators: `=` `<>`. Required keys: `v` `i` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL bigint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -199,6 +229,11 @@ impl DomainType for BigintEqQuery { /// `eql_v3.query_bigint_ord_ore` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL bigint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -230,6 +265,11 @@ impl DomainType for BigintOrdOreQuery { /// `eql_v3.query_bigint_ord` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL bigint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -261,6 +301,11 @@ impl DomainType for BigintOrdQuery { /// `eql_v3.query_bigint_ord_ope` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL bigint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/boolean.rs b/packages/eql/crates/eql-bindings/src/v3/boolean.rs index 2a64a6046..98eb74eb9 100644 --- a/packages/eql/crates/eql-bindings/src/v3/boolean.rs +++ b/packages/eql/crates/eql-bindings/src/v3/boolean.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_boolean` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL boolean reference](https://cipherstash.com/docs/reference/eql/booleans) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/date.rs b/packages/eql/crates/eql-bindings/src/v3/date.rs index c85bedc34..1c112e073 100644 --- a/packages/eql/crates/eql-bindings/src/v3/date.rs +++ b/packages/eql/crates/eql-bindings/src/v3/date.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_date` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL date reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -40,6 +45,11 @@ impl DomainType for Date { /// `public.eql_v3_date_eq` — equality domain. /// /// Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL date reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -72,6 +82,11 @@ impl DomainType for DateEq { /// `public.eql_v3_date_ord_ore` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL date reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -104,6 +119,11 @@ impl DomainType for DateOrdOre { /// `public.eql_v3_date_ord` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL date reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -136,6 +156,11 @@ impl DomainType for DateOrd { /// `public.eql_v3_date_ord_ope` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL date reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -168,6 +193,11 @@ impl DomainType for DateOrdOpe { /// `eql_v3.query_date_eq` — equality domain query operand. /// /// Operators: `=` `<>`. Required keys: `v` `i` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL date reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -199,6 +229,11 @@ impl DomainType for DateEqQuery { /// `eql_v3.query_date_ord_ore` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL date reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -230,6 +265,11 @@ impl DomainType for DateOrdOreQuery { /// `eql_v3.query_date_ord` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL date reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -261,6 +301,11 @@ impl DomainType for DateOrdQuery { /// `eql_v3.query_date_ord_ope` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL date reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/double.rs b/packages/eql/crates/eql-bindings/src/v3/double.rs index 7564d5614..d45cf48a4 100644 --- a/packages/eql/crates/eql-bindings/src/v3/double.rs +++ b/packages/eql/crates/eql-bindings/src/v3/double.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_double` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL double reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -40,6 +45,11 @@ impl DomainType for Double { /// `public.eql_v3_double_eq` — equality domain. /// /// Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL double reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -72,6 +82,11 @@ impl DomainType for DoubleEq { /// `public.eql_v3_double_ord_ore` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL double reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -104,6 +119,11 @@ impl DomainType for DoubleOrdOre { /// `public.eql_v3_double_ord` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL double reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -136,6 +156,11 @@ impl DomainType for DoubleOrd { /// `public.eql_v3_double_ord_ope` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL double reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -168,6 +193,11 @@ impl DomainType for DoubleOrdOpe { /// `eql_v3.query_double_eq` — equality domain query operand. /// /// Operators: `=` `<>`. Required keys: `v` `i` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL double reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -199,6 +229,11 @@ impl DomainType for DoubleEqQuery { /// `eql_v3.query_double_ord_ore` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL double reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -230,6 +265,11 @@ impl DomainType for DoubleOrdOreQuery { /// `eql_v3.query_double_ord` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL double reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -261,6 +301,11 @@ impl DomainType for DoubleOrdQuery { /// `eql_v3.query_double_ord_ope` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL double reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/integer.rs b/packages/eql/crates/eql-bindings/src/v3/integer.rs index 12db55037..19ad4516e 100644 --- a/packages/eql/crates/eql-bindings/src/v3/integer.rs +++ b/packages/eql/crates/eql-bindings/src/v3/integer.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_integer` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL integer reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -40,6 +45,11 @@ impl DomainType for Integer { /// `public.eql_v3_integer_eq` — equality domain. /// /// Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL integer reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -72,6 +82,11 @@ impl DomainType for IntegerEq { /// `public.eql_v3_integer_ord_ore` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL integer reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -104,6 +119,11 @@ impl DomainType for IntegerOrdOre { /// `public.eql_v3_integer_ord` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL integer reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -136,6 +156,11 @@ impl DomainType for IntegerOrd { /// `public.eql_v3_integer_ord_ope` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL integer reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -168,6 +193,11 @@ impl DomainType for IntegerOrdOpe { /// `eql_v3.query_integer_eq` — equality domain query operand. /// /// Operators: `=` `<>`. Required keys: `v` `i` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL integer reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -199,6 +229,11 @@ impl DomainType for IntegerEqQuery { /// `eql_v3.query_integer_ord_ore` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL integer reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -230,6 +265,11 @@ impl DomainType for IntegerOrdOreQuery { /// `eql_v3.query_integer_ord` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL integer reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -261,6 +301,11 @@ impl DomainType for IntegerOrdQuery { /// `eql_v3.query_integer_ord_ope` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL integer reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/json.rs b/packages/eql/crates/eql-bindings/src/v3/json.rs index c53f2348d..3ae7642ff 100644 --- a/packages/eql/crates/eql-bindings/src/v3/json.rs +++ b/packages/eql/crates/eql-bindings/src/v3/json.rs @@ -86,6 +86,11 @@ impl JsonSchema for SteVecForm { /// stored once for the whole document (every entry encrypts under the /// document's single data key, with per-entry nonces derived from the /// entries' selectors). +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = "See the [EQL JSON reference](https://cipherstash.com/docs/reference/eql/json) for document storage and containment queries." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -107,6 +112,11 @@ pub struct SteVecDocument { /// presence, so no per-entry equality term exists (`hm` is retired). The /// optional `i`/`v`/`h` fields model metadata grafted onto an entry returned by /// `->`; naming them explicitly keeps unknown keys rejectable. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = "See the [EQL JSON reference](https://cipherstash.com/docs/reference/eql/json) for encrypted field access." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -136,6 +146,11 @@ pub struct SteVecEntry { } /// `eql_v3.query_json` — a containment needle (`{sv:[query-entry]}`). Strict. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = "See the [EQL JSON reference](https://cipherstash.com/docs/reference/eql/json) for containment queries." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -148,6 +163,11 @@ pub struct SteVecQuery { /// entries are selector-only (matched on presence), and (per the SQL CHECK) /// no element carries a ciphertext. Unknown fields are rejected so the Rust /// parser and published JSON Schema enforce the same boundary as PostgreSQL. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = "See the [EQL JSON reference](https://cipherstash.com/docs/reference/eql/json) for containment query entries." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/json_storage.rs b/packages/eql/crates/eql-bindings/src/v3/json_storage.rs index 4a969edb5..a9934edd9 100644 --- a/packages/eql/crates/eql-bindings/src/v3/json_storage.rs +++ b/packages/eql/crates/eql-bindings/src/v3/json_storage.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_json` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL json reference](https://cipherstash.com/docs/reference/eql/json) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/numeric.rs b/packages/eql/crates/eql-bindings/src/v3/numeric.rs index b55989fd7..835b322cf 100644 --- a/packages/eql/crates/eql-bindings/src/v3/numeric.rs +++ b/packages/eql/crates/eql-bindings/src/v3/numeric.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_numeric` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL numeric reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -40,6 +45,11 @@ impl DomainType for Numeric { /// `public.eql_v3_numeric_eq` — equality domain. /// /// Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL numeric reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -72,6 +82,11 @@ impl DomainType for NumericEq { /// `public.eql_v3_numeric_ord_ore` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL numeric reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -104,6 +119,11 @@ impl DomainType for NumericOrdOre { /// `public.eql_v3_numeric_ord` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL numeric reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -136,6 +156,11 @@ impl DomainType for NumericOrd { /// `public.eql_v3_numeric_ord_ope` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL numeric reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -168,6 +193,11 @@ impl DomainType for NumericOrdOpe { /// `eql_v3.query_numeric_eq` — equality domain query operand. /// /// Operators: `=` `<>`. Required keys: `v` `i` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL numeric reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -199,6 +229,11 @@ impl DomainType for NumericEqQuery { /// `eql_v3.query_numeric_ord_ore` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL numeric reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -230,6 +265,11 @@ impl DomainType for NumericOrdOreQuery { /// `eql_v3.query_numeric_ord` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL numeric reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -261,6 +301,11 @@ impl DomainType for NumericOrdQuery { /// `eql_v3.query_numeric_ord_ope` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL numeric reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/real.rs b/packages/eql/crates/eql-bindings/src/v3/real.rs index 186d98699..a118fdff9 100644 --- a/packages/eql/crates/eql-bindings/src/v3/real.rs +++ b/packages/eql/crates/eql-bindings/src/v3/real.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_real` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL real reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -40,6 +45,11 @@ impl DomainType for Real { /// `public.eql_v3_real_eq` — equality domain. /// /// Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL real reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -72,6 +82,11 @@ impl DomainType for RealEq { /// `public.eql_v3_real_ord_ore` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL real reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -104,6 +119,11 @@ impl DomainType for RealOrdOre { /// `public.eql_v3_real_ord` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL real reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -136,6 +156,11 @@ impl DomainType for RealOrd { /// `public.eql_v3_real_ord_ope` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL real reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -168,6 +193,11 @@ impl DomainType for RealOrdOpe { /// `eql_v3.query_real_eq` — equality domain query operand. /// /// Operators: `=` `<>`. Required keys: `v` `i` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL real reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -199,6 +229,11 @@ impl DomainType for RealEqQuery { /// `eql_v3.query_real_ord_ore` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL real reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -230,6 +265,11 @@ impl DomainType for RealOrdOreQuery { /// `eql_v3.query_real_ord` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL real reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -261,6 +301,11 @@ impl DomainType for RealOrdQuery { /// `eql_v3.query_real_ord_ope` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL real reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/smallint.rs b/packages/eql/crates/eql-bindings/src/v3/smallint.rs index e6ca7dec8..fe69cfaec 100644 --- a/packages/eql/crates/eql-bindings/src/v3/smallint.rs +++ b/packages/eql/crates/eql-bindings/src/v3/smallint.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_smallint` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL smallint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -40,6 +45,11 @@ impl DomainType for Smallint { /// `public.eql_v3_smallint_eq` — equality domain. /// /// Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL smallint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -72,6 +82,11 @@ impl DomainType for SmallintEq { /// `public.eql_v3_smallint_ord_ore` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL smallint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -104,6 +119,11 @@ impl DomainType for SmallintOrdOre { /// `public.eql_v3_smallint_ord` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL smallint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -136,6 +156,11 @@ impl DomainType for SmallintOrd { /// `public.eql_v3_smallint_ord_ope` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL smallint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -168,6 +193,11 @@ impl DomainType for SmallintOrdOpe { /// `eql_v3.query_smallint_eq` — equality domain query operand. /// /// Operators: `=` `<>`. Required keys: `v` `i` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL smallint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -199,6 +229,11 @@ impl DomainType for SmallintEqQuery { /// `eql_v3.query_smallint_ord_ore` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL smallint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -230,6 +265,11 @@ impl DomainType for SmallintOrdOreQuery { /// `eql_v3.query_smallint_ord` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL smallint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -261,6 +301,11 @@ impl DomainType for SmallintOrdQuery { /// `eql_v3.query_smallint_ord_ope` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL smallint reference](https://cipherstash.com/docs/reference/eql/numbers) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/text.rs b/packages/eql/crates/eql-bindings/src/v3/text.rs index 66f682952..504fc4646 100644 --- a/packages/eql/crates/eql-bindings/src/v3/text.rs +++ b/packages/eql/crates/eql-bindings/src/v3/text.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_text` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -40,6 +45,24 @@ impl DomainType for Text { /// `public.eql_v3_text_eq` — equality domain. /// /// Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " Plaintext input and decrypted output: [`String`]. With the `stack-encrypt` feature, implements `EncryptFrom` and `DecryptInto`." +)] +#[cfg_attr( + doc, + doc = " Encryption context: `NonEmpty`, constructed with `Identifier::for_column(table, column)` and stored in `i`." +)] +#[cfg_attr( + all(doc, feature = "stack-encrypt"), + doc = " See the [complete encryption example](crate::encryption#example)." +)] #[cfg_attr(feature = "stack-encrypt", derive(stack_encrypt::EncryptFrom))] #[cfg_attr(feature = "stack-encrypt", derive(stack_encrypt::DecryptInto))] #[cfg_attr(feature = "stack-encrypt", stash(plaintext = String))] @@ -77,6 +100,11 @@ impl DomainType for TextEq { /// `public.eql_v3_text_match` — match domain. /// /// Operators: `@@`. Required keys: `v` `i` `c` `bf`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -109,6 +137,11 @@ impl DomainType for TextMatch { /// `public.eql_v3_text_ord_ore` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `hm` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -142,6 +175,11 @@ impl DomainType for TextOrdOre { /// `public.eql_v3_text_ord` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `hm` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -175,6 +213,11 @@ impl DomainType for TextOrd { /// `public.eql_v3_text_ord_ope` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `hm` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -208,6 +251,11 @@ impl DomainType for TextOrdOpe { /// `public.eql_v3_text_search_ore` — search domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=` `@@`. Required keys: `v` `i` `c` `hm` `ob` `bf`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -242,6 +290,11 @@ impl DomainType for TextSearchOre { /// `public.eql_v3_text_search` — search domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=` `@@`. Required keys: `v` `i` `c` `hm` `op` `bf`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -276,6 +329,24 @@ impl DomainType for TextSearch { /// `eql_v3.query_text_eq` — equality domain query operand. /// /// Operators: `=` `<>`. Required keys: `v` `i` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " Plaintext input: [`String`]. With the `stack-encrypt` feature, implements `EncryptFrom` to build equality query terms. Query operands do not decrypt." +)] +#[cfg_attr( + doc, + doc = " Encryption context: `NonEmpty`, constructed with `Identifier::for_column(table, column)` and stored in `i`." +)] +#[cfg_attr( + all(doc, feature = "stack-encrypt"), + doc = " See the [complete encryption example](crate::encryption#example)." +)] #[cfg_attr(feature = "stack-encrypt", derive(stack_encrypt::EncryptFrom))] #[cfg_attr(feature = "stack-encrypt", stash(plaintext = String))] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] @@ -311,6 +382,11 @@ impl DomainType for TextEqQuery { /// `eql_v3.query_text_match` — match domain query operand. /// /// Operators: `@@`. Required keys: `v` `i` `bf`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -342,6 +418,11 @@ impl DomainType for TextMatchQuery { /// `eql_v3.query_text_ord_ore` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `hm` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -374,6 +455,11 @@ impl DomainType for TextOrdOreQuery { /// `eql_v3.query_text_ord` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `hm` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -406,6 +492,11 @@ impl DomainType for TextOrdQuery { /// `eql_v3.query_text_ord_ope` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `hm` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -438,6 +529,11 @@ impl DomainType for TextOrdOpeQuery { /// `eql_v3.query_text_search_ore` — search domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=` `@@`. Required keys: `v` `i` `hm` `ob` `bf`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -471,6 +567,11 @@ impl DomainType for TextSearchOreQuery { /// `eql_v3.query_text_search` — search domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=` `@@`. Required keys: `v` `i` `hm` `op` `bf`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL text reference](https://cipherstash.com/docs/reference/eql/text) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-bindings/src/v3/timestamp.rs b/packages/eql/crates/eql-bindings/src/v3/timestamp.rs index 33284a5c3..de022873d 100644 --- a/packages/eql/crates/eql-bindings/src/v3/timestamp.rs +++ b/packages/eql/crates/eql-bindings/src/v3/timestamp.rs @@ -9,6 +9,11 @@ use ts_rs::TS; /// `public.eql_v3_timestamp` — storage-only domain. /// /// Operators: none. Required keys: `v` `i` `c`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL timestamp reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -40,6 +45,11 @@ impl DomainType for Timestamp { /// `public.eql_v3_timestamp_eq` — equality domain. /// /// Operators: `=` `<>`. Required keys: `v` `i` `c` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL timestamp reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -72,6 +82,11 @@ impl DomainType for TimestampEq { /// `public.eql_v3_timestamp_ord_ore` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL timestamp reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -104,6 +119,11 @@ impl DomainType for TimestampOrdOre { /// `public.eql_v3_timestamp_ord` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL timestamp reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -136,6 +156,11 @@ impl DomainType for TimestampOrd { /// `public.eql_v3_timestamp_ord_ope` — ordering domain. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `c` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL timestamp reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -168,6 +193,11 @@ impl DomainType for TimestampOrdOpe { /// `eql_v3.query_timestamp_eq` — equality domain query operand. /// /// Operators: `=` `<>`. Required keys: `v` `i` `hm`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL timestamp reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -199,6 +229,11 @@ impl DomainType for TimestampEqQuery { /// `eql_v3.query_timestamp_ord_ore` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `ob`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL timestamp reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -230,6 +265,11 @@ impl DomainType for TimestampOrdOreQuery { /// `eql_v3.query_timestamp_ord` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL timestamp reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] @@ -261,6 +301,11 @@ impl DomainType for TimestampOrdQuery { /// `eql_v3.query_timestamp_ord_ope` — ordering domain query operand. /// /// Operators: `=` `<>` `<` `<=` `>` `>=`. Required keys: `v` `i` `op`. +#[cfg_attr(doc, doc = "")] +#[cfg_attr( + doc, + doc = " See the [EQL timestamp reference](https://cipherstash.com/docs/reference/eql/dates-and-times) for SQL domain variants, operators, and query examples." +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] #[serde(deny_unknown_fields)] diff --git a/packages/eql/crates/eql-codegen/src/bindings.rs b/packages/eql/crates/eql-codegen/src/bindings.rs index 7cb013f4f..84bdedea5 100644 --- a/packages/eql/crates/eql-codegen/src/bindings.rs +++ b/packages/eql/crates/eql-codegen/src/bindings.rs @@ -126,6 +126,29 @@ fn struct_doc_lines(full: &str, domain: &Domain) -> [String; 3] { [summary, String::new(), detail] } +/// Rust API navigation, separate from the shared wire-format descriptions +/// exported to TypeScript and JSON Schema. The site groups domain variants by +/// plaintext family, including their query operands. +fn reference_docs(family: &DomainFamily) -> TokenStream { + let page = match family.name { + "integer" | "smallint" | "bigint" | "numeric" | "real" | "double" => "numbers", + "date" | "timestamp" => "dates-and-times", + "text" => "text", + "boolean" => "booleans", + "json" => "json", + other => panic!("missing CipherStash documentation page for {other:?}"), + }; + let reference = format!( + " See the [EQL {} reference](https://cipherstash.com/docs/reference/eql/{page}) \ + for SQL domain variants, operators, and query examples.", + family.name + ); + quote! { + #[cfg_attr(doc, doc = "")] + #[cfg_attr(doc, doc = #reference)] + } +} + /// One payload struct + its three-method `DomainType` impl. A catalog-derived /// struct doc (summary + operators + required keys — see [`struct_doc_lines`]), /// no field docs. Term fields come from `Term::payload_terms`, matching on the @@ -136,6 +159,7 @@ fn render_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { let ident = format_ident!("{}", domain.struct_ident(family.name)); let sql_domain = crate::context::domain_name(&full); let [doc_summary, doc_blank, doc_detail] = struct_doc_lines(&full, domain); + let reference = reference_docs(family); let (encryption, version, context) = encryption_attrs(family, domain, false); // The envelope triple is hardcoded (not looped over `ENVELOPE_KEYS`) because @@ -164,6 +188,7 @@ fn render_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { #[doc = #doc_summary] #[doc = #doc_blank] #[doc = #doc_detail] + #reference #encryption #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] @@ -208,6 +233,7 @@ fn render_query_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { // Query operands live in the public-API schema, NOT `public`: they are // never valid column types. let sql_domain = format!("eql_v3.{query_name}"); + let reference = reference_docs(family); let (encryption, version, context) = encryption_attrs(family, domain, true); // Query doc: same capability label + operator union as storage, but the @@ -246,6 +272,7 @@ fn render_query_struct(family: &DomainFamily, domain: &Domain) -> TokenStream { #[doc = #summary] #[doc = ""] #[doc = #detail] + #reference #encryption #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TS, JsonSchema)] #[ts(export, export_to = "v3/")] @@ -291,8 +318,19 @@ fn encryption_attrs( let decrypt = (!query).then( || quote!(#[cfg_attr(feature = "stack-encrypt", derive(stack_encrypt::DecryptInto))]), ); + let plaintext = if query { + " Plaintext input: [`String`]. With the `stack-encrypt` feature, implements \ + `EncryptFrom` to build equality query terms. Query operands do not decrypt." + } else { + " Plaintext input and decrypted output: [`String`]. With the `stack-encrypt` \ + feature, implements `EncryptFrom` and `DecryptInto`." + }; ( quote! { + #[cfg_attr(doc, doc = "")] + #[cfg_attr(doc, doc = #plaintext)] + #[cfg_attr(doc, doc = " Encryption context: `NonEmpty`, constructed with `Identifier::for_column(table, column)` and stored in `i`.")] + #[cfg_attr(all(doc, feature = "stack-encrypt"), doc = " See the [complete encryption example](crate::encryption#example).")] #[cfg_attr(feature = "stack-encrypt", derive(stack_encrypt::EncryptFrom))] #decrypt #[cfg_attr(feature = "stack-encrypt", stash(plaintext = String))] diff --git a/packages/eql/tests/encryption/Cargo.toml b/packages/eql/tests/encryption/Cargo.toml index 3ef0d2f5a..aec904647 100644 --- a/packages/eql/tests/encryption/Cargo.toml +++ b/packages/eql/tests/encryption/Cargo.toml @@ -4,6 +4,13 @@ version = "0.1.0" edition = "2021" publish = false +# Execute the exact source included in the public Rustdoc example. Keep its +# test key source and async runtime out of eql-bindings' default test build. +[[test]] +name = "text_eq_example" +path = "../../crates/eql-bindings/src/encryption/example.rs" +harness = false + # Keep crypto/Postgres test dependencies out of eql-bindings' default test build. [dev-dependencies] eql-bindings = { path = "../../crates/eql-bindings", features = ["stack-encrypt"] }