diff --git a/.gitignore b/.gitignore
index a5b17a57..05413b30 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,6 @@
/cipherstash-proxy.local.toml
mise.local.toml
tests/pg/data**
-tests/sql/cipherstash-encrypt.sql
-tests/sql/cipherstash-encrypt-uninstall.sql
.vscode
rust-toolchain.toml
@@ -13,8 +11,9 @@ rust-toolchain.toml
# release artifacts
/cipherstash-proxy
-/cipherstash-eql.sql
/packages/cipherstash-proxy/eql-version-at-build-time.txt
+/cipherstash-encrypt.sql
+/cipherstash-encrypt-uninstall.sql
# credentials for local dev
.env.proxy.docker
diff --git a/Cargo.lock b/Cargo.lock
index b2e0ee88..3c322604 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -181,6 +181,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
dependencies = [
+ "serde",
"zeroize",
]
@@ -287,6 +288,61 @@ dependencies = [
"fs_extra",
]
+[[package]]
+name = "axum"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
[[package]]
name = "backtrace"
version = "0.3.74"
@@ -317,6 +373,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+[[package]]
+name = "base32"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076"
+
[[package]]
name = "base64"
version = "0.22.1"
@@ -588,9 +650,9 @@ dependencies = [
[[package]]
name = "cipherstash-client"
-version = "0.18.0"
+version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f099b1db6cf37b0ca36e9c8e0c2dade20f2035804e225f52475d44e750dd5dd5"
+checksum = "8fe21509165da6daf50b84d4dc9bc46b558e5afb34db75dbd2371b963faabe4d"
dependencies = [
"aes-gcm-siv",
"anyhow",
@@ -605,6 +667,7 @@ dependencies = [
"cipherstash-config",
"cipherstash-core",
"cllw-ore",
+ "cts-common",
"derive_more",
"dirs",
"futures",
@@ -724,9 +787,12 @@ name = "cipherstash-proxy-integration"
version = "0.1.0"
dependencies = [
"chrono",
+ "cipherstash-client",
+ "cipherstash-config",
"cipherstash-proxy",
"clap",
"fake 4.2.0",
+ "hex",
"rand 0.9.0",
"recipher",
"rustls",
@@ -739,6 +805,7 @@ dependencies = [
"tokio-rustls",
"tracing",
"tracing-subscriber",
+ "uuid",
"webpki-roots",
]
@@ -953,6 +1020,27 @@ dependencies = [
"cipher 0.4.4",
]
+[[package]]
+name = "cts-common"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "938da7d14d05c2769bf7ae33c5a395eb6a34ffdd25ec286e97702ae563314f9b"
+dependencies = [
+ "arrayvec",
+ "axum",
+ "base32",
+ "diesel",
+ "fake 3.1.0",
+ "http",
+ "miette",
+ "rand 0.8.5",
+ "regex",
+ "serde",
+ "thiserror 1.0.69",
+ "url",
+ "vitaminc",
+]
+
[[package]]
name = "darling"
version = "0.20.10"
@@ -973,6 +1061,7 @@ dependencies = [
"ident_case",
"proc-macro2",
"quote",
+ "strsim",
"syn 2.0.100",
]
@@ -1075,6 +1164,39 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc55fe0d1f6c107595572ec8b107c0999bb1a2e0b75e37429a4fb0d6474a0e7d"
+[[package]]
+name = "diesel"
+version = "2.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff3e1edb1f37b4953dd5176916347289ed43d7119cc2e6c7c3f7849ff44ea506"
+dependencies = [
+ "chrono",
+ "diesel_derives",
+ "uuid",
+]
+
+[[package]]
+name = "diesel_derives"
+version = "2.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68d4216021b3ea446fd2047f5c8f8fe6e98af34508a254a01e4d6bc1e844f84d"
+dependencies = [
+ "diesel_table_macro_syntax",
+ "dsl_auto_type",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "diesel_table_macro_syntax"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25"
+dependencies = [
+ "syn 2.0.100",
+]
+
[[package]]
name = "diff"
version = "0.1.13"
@@ -1123,6 +1245,20 @@ dependencies = [
"syn 2.0.100",
]
+[[package]]
+name = "dsl_auto_type"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "139ae9aca7527f85f26dd76483eb38533fd84bd571065da1739656ef71c5ff5b"
+dependencies = [
+ "darling",
+ "either",
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
[[package]]
name = "dummy"
version = "0.8.0"
@@ -1135,6 +1271,18 @@ dependencies = [
"syn 2.0.100",
]
+[[package]]
+name = "dummy"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abcba80bdf851db5616e27ff869399468e2d339d7c6480f5887681e6bdfc2186"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
[[package]]
name = "dummy"
version = "0.11.0"
@@ -1172,6 +1320,7 @@ dependencies = [
"thiserror 2.0.12",
"tracing",
"tracing-subscriber",
+ "vec1",
]
[[package]]
@@ -1223,12 +1372,24 @@ dependencies = [
"uuid",
]
+[[package]]
+name = "fake"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aef603df4ba9adbca6a332db7da6f614f21eafefbaf8e087844e452fdec152d0"
+dependencies = [
+ "deunicode",
+ "dummy 0.9.2",
+ "rand 0.8.5",
+]
+
[[package]]
name = "fake"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b591050272097cc85b2f3c1cc4817ba4560057d10fcae6f7339f1cf622da0a0f"
dependencies = [
+ "chrono",
"deunicode",
"dummy 0.11.0",
"rand 0.9.0",
@@ -2034,6 +2195,12 @@ dependencies = [
"regex-automata 0.1.10",
]
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
[[package]]
name = "md-5"
version = "0.10.6"
@@ -3328,6 +3495,16 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
[[package]]
name = "serde_spanned"
version = "0.6.8"
@@ -3905,6 +4082,7 @@ dependencies = [
"tokio",
"tower-layer",
"tower-service",
+ "tracing",
]
[[package]]
@@ -4134,6 +4312,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+[[package]]
+name = "vec1"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eab68b56840f69efb0fefbe3ab6661499217ffdc58e2eef7c3f6f69835386322"
+
[[package]]
name = "version_check"
version = "0.9.5"
diff --git a/docs/errors.md b/docs/errors.md
index 7e5faceb..1105d064 100644
--- a/docs/errors.md
+++ b/docs/errors.md
@@ -314,7 +314,7 @@ For example:
## Unknown Column
-The column has an encrypted type (PostgreSQL `cs_encrypted_v1` type ) with no encryption configuration.
+The column has an encrypted type (PostgreSQL `eql_v1_encrypted` type ) with no encryption configuration.
Without the configuration, Cipherstash Proxy does not know how to encrypt the column.
Any data is unprotected and unencrypted.
@@ -341,7 +341,7 @@ Column 'column_name' in table 'table_name' has no Encrypt configuration
## Unknown Table
-The table has one or more encrypted columns (PostgreSQL `cs_encrypted_v1` type ) with no encryption configuration.
+The table has one or more encrypted columns (PostgreSQL `eql_v1_encrypted` type ) with no encryption configuration.
Without the configuration, Cipherstash Proxy does not know how to encrypt the column.
Any data is unprotected and unencrypted.
diff --git a/docs/getting-started/schema-example.sql b/docs/getting-started/schema-example.sql
index 29e3e743..0120cde4 100644
--- a/docs/getting-started/schema-example.sql
+++ b/docs/getting-started/schema-example.sql
@@ -1,12 +1,12 @@
-TRUNCATE TABLE cs_configuration_v1;
+TRUNCATE TABLE public.eql_v1_configuration;
-- Exciting cipherstash table
DROP TABLE IF EXISTS users;
CREATE TABLE users (
id SERIAL PRIMARY KEY,
- encrypted_email cs_encrypted_v1,
- encrypted_dob cs_encrypted_v1,
- encrypted_salary cs_encrypted_v1
+ encrypted_email eql_v1_encrypted,
+ encrypted_dob eql_v1_encrypted,
+ encrypted_salary eql_v1_encrypted
);
SELECT cs_add_index_v1(
diff --git a/docs/how-to.md b/docs/how-to.md
index a38f80cb..5f906a84 100644
--- a/docs/how-to.md
+++ b/docs/how-to.md
@@ -153,7 +153,7 @@ You can also install EQL by running [the installation script](https://github.com
Once you have installed EQL, you can see what version is installed by querying the database:
```sql
-SELECT cs_eql_version();
+SELECT eql_v1.version();
```
This will output the version of EQL installed.
@@ -162,22 +162,22 @@ This will output the version of EQL installed.
In your existing PostgreSQL database, you store your data in tables and columns.
Those columns have types like `integer`, `text`, `timestamp`, and `boolean`.
-When storing encrypted data in PostgreSQL with Proxy, you use a special column type called `cs_encrypted_v1`, which is [provided by EQL](#setting-up-the-database-schema).
-`cs_encrypted_v1` is a container column type that can be used for any type of encrypted data you want to store or search, whether they are numbers (`int`, `small_int`, `big_int`), text (`text`), dates and times (`date`), or booleans (`boolean`).
+When storing encrypted data in PostgreSQL with Proxy, you use a special column type called `eql_v1_encrypted`, which is [provided by EQL](#setting-up-the-database-schema).
+`eql_v1_encrypted` is a container column type that can be used for any type of encrypted data you want to store or search, whether they are numbers (`int`, `small_int`, `big_int`), text (`text`), dates and times (`date`), or booleans (`boolean`).
Create a table with an encrypted column for `email`:
```sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
- email cs_encrypted_v1
+ email eql_v1_encrypted
)
```
This creates a `users` table with two columns:
- `id`, an autoincrementing integer column that is the primary key for the record
- - `email`, a `cs_encrypted_v1` column
+ - `email`, a `eql_v1_encrypted` column
There are important differences between the plaintext columns you've traditionally used in PostgreSQL and encrypted columns with CipherStash Proxy:
diff --git a/mise.toml b/mise.toml
index 9cf04e2a..64e563df 100644
--- a/mise.toml
+++ b/mise.toml
@@ -409,27 +409,28 @@ fi
"""
[tasks."postgres:setup"]
+depends = ["postgres:eql:teardown"]
alias = 's'
description = "Installs EQL and applies schema to database"
run = """
#!/bin/bash
cd tests
mise run postgres:fail_if_not_running
-mise run postgres:eql:download
-cat sql/cipherstash-encrypt.sql | docker exec -i postgres${CONTAINER_SUFFIX} psql postgresql://${CS_DATABASE__USERNAME}:${CS_DATABASE__PASSWORD}@${CS_DATABASE__HOST}:${CS_DATABASE__PORT}/${CS_DATABASE__NAME} -f-
+cat sql/schema-uninstall.sql | docker exec -i postgres${CONTAINER_SUFFIX} psql postgresql://${CS_DATABASE__USERNAME}:${CS_DATABASE__PASSWORD}@${CS_DATABASE__HOST}:${CS_DATABASE__PORT}/${CS_DATABASE__NAME} -f-
+cat ../cipherstash-encrypt-uninstall.sql | docker exec -i postgres${CONTAINER_SUFFIX} psql postgresql://${CS_DATABASE__USERNAME}:${CS_DATABASE__PASSWORD}@${CS_DATABASE__HOST}:${CS_DATABASE__PORT}/${CS_DATABASE__NAME} -f-
+cat ../cipherstash-encrypt.sql | docker exec -i postgres${CONTAINER_SUFFIX} psql postgresql://${CS_DATABASE__USERNAME}:${CS_DATABASE__PASSWORD}@${CS_DATABASE__HOST}:${CS_DATABASE__PORT}/${CS_DATABASE__NAME} -f-
cat sql/schema.sql | docker exec -i postgres${CONTAINER_SUFFIX} psql postgresql://${CS_DATABASE__USERNAME}:${CS_DATABASE__PASSWORD}@${CS_DATABASE__HOST}:${CS_DATABASE__PORT}/${CS_DATABASE__NAME} -f-
"""
[tasks."postgres:eql:teardown"]
-alias = 's'
+depends = ["eql:download"]
description = "Uninstalls EQL and removes schema from database"
run = """
#!/bin/bash
cd tests
mise run postgres:fail_if_not_running
-mise run postgres:eql:download
cat sql/schema-uninstall.sql | docker exec -i postgres${CONTAINER_SUFFIX} psql postgresql://${CS_DATABASE__USERNAME}:${CS_DATABASE__PASSWORD}@${CS_DATABASE__HOST}:${CS_DATABASE__PORT}/${CS_DATABASE__NAME} -f-
-cat sql/cipherstash-encrypt-uninstall.sql | docker exec -i postgres${CONTAINER_SUFFIX} psql postgresql://${CS_DATABASE__USERNAME}:${CS_DATABASE__PASSWORD}@${CS_DATABASE__HOST}:${CS_DATABASE__PORT}/${CS_DATABASE__NAME} -f-
+cat ../cipherstash-encrypt-uninstall.sql | docker exec -i postgres${CONTAINER_SUFFIX} psql postgresql://${CS_DATABASE__USERNAME}:${CS_DATABASE__PASSWORD}@${CS_DATABASE__HOST}:${CS_DATABASE__PORT}/${CS_DATABASE__NAME} -f-
"""
[tasks."postgres:up"]
@@ -490,34 +491,32 @@ for d in tests/pg/data-*; do
done
"""
-
-[tasks."postgres:eql:download"]
+[tasks."eql:download"]
alias = 'e'
-description = "Download latest EQL release"
+description = "Download latest EQL release or use local copy"
dir = "{{config_root}}/tests"
outputs = [
- "{{config_root}}/tests/sql/cipherstash-encrypt.sql",
- "{{config_root}}/tests/sql/cipherstash-encrypt-uninstall.sql",
+ "{{config_root}}/cipherstash-encrypt.sql",
+ "{{config_root}}/cipherstash-encrypt-uninstall.sql",
]
run = """
# install script
if [ -z "$CS_EQL_PATH" ]; then
- curl -sLo sql/cipherstash-encrypt.sql https://github.com/cipherstash/encrypt-query-language/releases/download/${CS_EQL_VERSION}/cipherstash-encrypt.sql
+ curl -sLo "{{config_root}}/cipherstash-encrypt.sql" https://github.com/cipherstash/encrypt-query-language/releases/download/${CS_EQL_VERSION}/cipherstash-encrypt.sql
else
- echo "Using EQL: ${CS_EQL_PATH}"
- cp "$CS_EQL_PATH" sql/cipherstash-encrypt.sql
+ echo "Using EQL: ${CS_EQL_PATH}/cipherstash-encrypt.sql"
+ cp "$CS_EQL_PATH/cipherstash-encrypt.sql" "{{config_root}}/cipherstash-encrypt.sql"
fi
# uninstall script
-if [ -z "$CS_EQL_UNINSTALL_PATH" ]; then
- curl -sLo sql/cipherstash-encrypt-uninstall.sql https://github.com/cipherstash/encrypt-query-language/releases/download/${CS_EQL_VERSION}/cipherstash-encrypt-uninstall.sql
+if [ -z "$CS_EQL_PATH" ]; then
+ curl -sLo "{{config_root}}/cipherstash-encrypt-uninstall.sql" https://github.com/cipherstash/encrypt-query-language/releases/download/${CS_EQL_VERSION}/cipherstash-encrypt-uninstall.sql
else
- echo "Using EQL: ${CS_EQL_PATH}"
- cp "$CS_EQL_UNINSTALL_PATH" sql/cipherstash-encrypt-uninstall.sql
+ echo "Using EQL: ${CS_EQL_PATH}/cipherstash-encrypt-uninstall.sql"
+ cp "$CS_EQL_PATH/cipherstash-encrypt-uninstall.sql" "{{config_root}}/cipherstash-encrypt-uninstall.sql"
fi
"""
-
[tasks."python:test"]
dir = "{{config_root}}/tests"
description = "Runs python tests"
@@ -567,7 +566,7 @@ cp -v {{config_root}}/target/{{ target }}/release/cipherstash-proxy {{config_roo
"""
[tasks."build:docker"]
-depends = ["build:docker:fetch_eql"]
+depends = ["eql:download"]
description = "Build a Docker image for cipherstash-proxy"
run = """
{% set default_platform = "linux/" ~ arch() | replace(from="x86_64", to="amd64") %}
diff --git a/packages/cipherstash-proxy-integration/Cargo.toml b/packages/cipherstash-proxy-integration/Cargo.toml
index 546e1ff8..74ea4334 100644
--- a/packages/cipherstash-proxy-integration/Cargo.toml
+++ b/packages/cipherstash-proxy-integration/Cargo.toml
@@ -24,5 +24,9 @@ tracing-subscriber = { workspace = true }
webpki-roots = "0.26.7"
[dev-dependencies]
+cipherstash-client = { version = "0.20.0", features = ["tokio"] }
+cipherstash-config = "0.2.3"
clap = "4.5.32"
-fake = { version = "4", features = ["derive"] }
+fake = { version = "4", features = ["chrono", "derive"] }
+hex = "0.4.3"
+uuid = { version = "1.11.0", features = ["serde", "v4"] }
diff --git a/packages/cipherstash-proxy-integration/src/extended_protocol_error_messages.rs b/packages/cipherstash-proxy-integration/src/extended_protocol_error_messages.rs
index c45ec4af..37ac99ea 100644
--- a/packages/cipherstash-proxy-integration/src/extended_protocol_error_messages.rs
+++ b/packages/cipherstash-proxy-integration/src/extended_protocol_error_messages.rs
@@ -67,10 +67,10 @@ mod tests {
let msg = err.to_string();
// This is similar to below. The error message comes from tokio-postgres when Proxy
- // returns cs_encrypted_v1 and the client cannot convert to a string.
+ // returns eql_v1_encrypted and the client cannot convert to a string.
// If mapping errors are enabled (enable_mapping_errors or CS_DEVELOPMENT__ENABLE_MAPPING_ERRORS),
// then Proxy will return an error that says "Column X in table Y has no Encrypt configuration"
- assert_eq!(msg, "error serializing parameter 1: cannot convert between the Rust type `&str` and the Postgres type `cs_encrypted_v1`");
+ assert_eq!(msg, "error serializing parameter 1: cannot convert between the Rust type `&str` and the Postgres type `eql_v1_encrypted`");
} else {
unreachable!();
}
diff --git a/packages/cipherstash-proxy-integration/src/generate.rs b/packages/cipherstash-proxy-integration/src/generate.rs
new file mode 100644
index 00000000..13d11820
--- /dev/null
+++ b/packages/cipherstash-proxy-integration/src/generate.rs
@@ -0,0 +1,259 @@
+#[cfg(test)]
+mod tests {
+ use crate::common::trace;
+ use cipherstash_client::config::EnvSource;
+ use cipherstash_client::credentials::auto_refresh::AutoRefresh;
+ use cipherstash_client::encryption::{
+ Encrypted, EncryptedSteVecTerm, JsonIndexer, JsonIndexerOptions, OreTerm, Plaintext,
+ PlaintextTarget, ReferencedPendingPipeline,
+ };
+ use cipherstash_client::{encryption::ScopedCipher, zerokms::EncryptedRecord};
+ use cipherstash_client::{ConsoleConfig, CtsConfig, ZeroKMSConfig};
+ use cipherstash_config::column::{Index, IndexType};
+ use cipherstash_config::{ColumnConfig, ColumnType};
+ use cipherstash_proxy::Identifier;
+ use serde::{Deserialize, Serialize};
+ use std::sync::Arc;
+ use tracing::info;
+ use uuid::Uuid;
+
+ pub mod option_mp_base85 {
+ use cipherstash_client::zerokms::encrypted_record::formats::mp_base85;
+ use cipherstash_client::zerokms::EncryptedRecord;
+ use serde::{Deserialize, Deserializer, Serializer};
+
+ pub fn serialize(
+ value: &Option,
+ serializer: S,
+ ) -> Result
+ where
+ S: Serializer,
+ {
+ match value {
+ Some(record) => mp_base85::serialize(record, serializer),
+ None => serializer.serialize_none(),
+ }
+ }
+
+ pub fn deserialize<'de, D>(deserializer: D) -> Result