Struct JsonIndexer
pub struct JsonIndexer { /* private fields */ }Expand description
Indexer for searchable encryption of JSON documents.
A JsonIndexer flattens a JSON value into path/value pairs, applies any
configured case normalization, and produces an [SteVecPendingEncryption] ready
to be sealed with a data key. The same indexer (with the same key and
prefix) is used on the query side to produce SteQueryVec,
TokenizedSelector, and EncryptedSteVecTerm values that match the
stored index.
Construct one with JsonIndexer::new from JsonIndexerOptions, or use
Default for an indexer with an empty prefix, no term filters, and
ArrayIndexMode::ALL.
Implementations§
§impl JsonIndexer
impl JsonIndexer
pub fn new(opts: JsonIndexerOptions) -> Self
pub fn new(opts: JsonIndexerOptions) -> Self
Create a new indexer from the given options.
pub fn index(
&self,
json: Value,
index_key: &IndexKey,
) -> Result<SteVecPendingEncryption<16>, EncryptionError>
pub fn index( &self, json: Value, index_key: &IndexKey, ) -> Result<SteVecPendingEncryption<16>, EncryptionError>
Generates an [SteVecPendingEncryption] from a JSON value.
This represents the indexed form of the JSON, but with source plaintexts still present.
This can then be encrypted into a final SteVec using a crate::zerokms::DataKeyWithTag with [SteVecPendingEncryption::encrypt].
Once encrypted, the resulting SteVec can be stored in a database column or some other storage.
§Example
use cipherstash_client::encryption::{JsonIndexer, JsonIndexerOptions, SteVec};
use cipherstash_client::zerokms::{DataKey, DataKeyWithTag, IndexKey};
use zerokms_protocol::cipherstash_config::column::ArrayIndexMode;
use serde_json::Value;
let opts = JsonIndexerOptions {
prefix: "foo".to_string(),
term_filters: Vec::new(),
array_index_mode: ArrayIndexMode::ALL,
..Default::default()
};
let indexer = JsonIndexer::new(opts);
let index_key = IndexKey::from([0; 32]);
let json = serde_json::json!({ "a": 1, "b": { "c": 2 } });
let pending_encryption = indexer.index(json, &index_key).unwrap();
// Encrypt the pending entries with a DataKeyWithTag
// CAUTION: Always use a data key generated by Zerokms for production use.
// Attempting to generate your own keys will result in invalid tags and decryption failures.
let key = DataKey {
iv: [0; 16],
key: [0; 32],
};
let key_with_tag = DataKeyWithTag { key, tag: vec![0, 1, 2], decryption_policy: None };
let ste_vec: SteVec<16> = pending_encryption
.encrypt(key_with_tag, "table/column", None)
.unwrap();pub fn query(
&self,
json: Value,
index_key: &IndexKey,
) -> Result<SteQueryVec<16>, EncryptionError>
pub fn query( &self, json: Value, index_key: &IndexKey, ) -> Result<SteQueryVec<16>, EncryptionError>
Generate an SteQueryVec from a JSON value.
This is useful for building containment queries (e.g. @> operator in Postgres).
For example, given an SteVec column attrs, an SteQueryVec generated from a plaintext JSON value.
use cipherstash_client::encryption::{JsonIndexer, JsonIndexerOptions};
use cipherstash_client::zerokms::IndexKey;
use zerokms_protocol::cipherstash_config::column::ArrayIndexMode;
let opts = JsonIndexerOptions {
prefix: "foo".to_string(),
term_filters: Vec::new(),
array_index_mode: ArrayIndexMode::ALL,
..Default::default()
};
let indexer = JsonIndexer::new(opts);
let index_key = IndexKey::from([0; 32]);
let json = serde_json::json!({ "a": 1, "b": { "c": 2 } });
let q = indexer.query(json, &index_key).unwrap();This can then be used in a query like:
-- $1 is the query parameter, q
-- Example: q = [["aaa...", "bbb..."], ["ccc...", "ddd..."]]
SELECT * FROM table WHERE attrs @> $1;pub fn generate_selector(
&self,
selector: Selector,
index_key: &IndexKey,
) -> TokenizedSelector<16>
pub fn generate_selector( &self, selector: Selector, index_key: &IndexKey, ) -> TokenizedSelector<16>
Generate a TokenizedSelector from a JSON path.
This is useful for building queries that target specific paths in a JSON document.
For example, given an SteVec column attrs, a TokenizedSelector generated from a JSON path.
use cipherstash_client::encryption::{JsonIndexer, JsonIndexerOptions};
use cipherstash_client::zerokms::IndexKey;
use cipherstash_client::ejsonpath::Selector;
use zerokms_protocol::cipherstash_config::column::ArrayIndexMode;
let opts = JsonIndexerOptions { prefix: "foo".to_string(), term_filters: Vec::new(), array_index_mode: ArrayIndexMode::ALL, ..Default::default() };
let indexer = JsonIndexer::new(opts);
let index_key = IndexKey::from([0; 32]);
let json = serde_json::json!({ "a": 1, "b": { "c": 2 } });
let selector = Selector::parse("$.b.c").unwrap();
let tokenized_selector = indexer.generate_selector(selector, &index_key);This can then be used in a query like:
-- $1 is the tokenized selector
-- Example: "4eb62fb72d75cb53a309b3b091923daf"
SELECT jsonb_path_query(attrs, '$ ? (exists(@ ? (@[0] == $1)))[2]') FROM table;This is equivalent to the unencrypted query:
SELECT attrs->'b'->'c' FROM table WHERE attrs->'b'->'c' IS NOT NULL;pub fn generate_value_selector(
&self,
selector: Selector,
value: &Value,
index_key: &IndexKey,
) -> Result<TokenizedSelector<16>, EncryptionError>
pub fn generate_value_selector( &self, selector: Selector, value: &Value, index_key: &IndexKey, ) -> Result<TokenizedSelector<16>, EncryptionError>
Build the value-inclusive TokenizedSelector for value at
selector — the exact-match query operand. Its presence in a stored
sv means “this exact value is at this path”: storage emits the same
selector for every node (see ste_vec::priv_state::value_selector),
so the comparison is keyed-MAC equality per (path, scalar value), subject
to the documented high-precision numeric caveat.
Value-inclusive selectors are ALWAYS Blake3, like path selectors — the mode only controls the orderable term primitive.
String values pass through the indexer’s configured case normalization
first — symmetric with storage, where entries are built from the
preprocessed document — so a query for "John" against a
Downcase-filtered column matches the stored "john" entry. Stemmer
and Stop filters are rejected for JSON indexes because they alter JSON
equality rather than normalize case.
value must be a scalar (string, number, bool, or null). A single
value selector is injective only for scalars: a container MACs only its
structural tag (MAP0{} / ARRY[]) plus the path, so every object (or
every array) at a path would collapse to one selector — a silent false
positive on exact match. Objects and arrays are therefore rejected here;
container matching goes through the flattened containment query
(Self::query), not a single value selector.
Numbers outside serde_json’s exact integer representations currently
canonicalize through f64; distinct high-precision PostgreSQL numeric
values within one f64 step can therefore over-match. Arbitrary-precision
decimal canonicalization is not currently supported.
Trait Implementations§
§impl IndexerInit for JsonIndexer
impl IndexerInit for JsonIndexer
Auto Trait Implementations§
impl Freeze for JsonIndexer
impl RefUnwindSafe for JsonIndexer
impl Send for JsonIndexer
impl Sync for JsonIndexer
impl Unpin for JsonIndexer
impl UnsafeUnpin for JsonIndexer
impl UnwindSafe for JsonIndexer
Blanket Implementations§
impl<T> AuthStrategyBounds for T
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more