Skip to content

Commit 9bb0cf1

Browse files
authored
Merge pull request #35 from cipherstash/chore/add-custom-types
Add ore custom types to eql install
2 parents 38c3cec + 556c235 commit 9bb0cf1

File tree

5 files changed

+655
-17
lines changed

5 files changed

+655
-17
lines changed

justfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ build:
2727
#!/usr/bin/env bash
2828
set -euxo pipefail
2929

30-
cat sql/dsl-core.sql sql/dsl-config-schema.sql sql/dsl-config-functions.sql sql/dsl-encryptindex.sql > release/cipherstash-encrypt-dsl.sql
30+
cat sql/database-extensions/postgresql/install.sql sql/dsl-core.sql sql/dsl-config-schema.sql sql/dsl-config-functions.sql sql/dsl-encryptindex.sql > release/cipherstash-encrypt-dsl.sql
3131

3232

3333
psql:

languages/go/xorm/migrations.go

-16
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,10 @@ import (
88

99
func InstallEql(engine *sql.DB) {
1010
log.Println("Start installing custom types and function")
11-
installCsCustomTypes(engine)
1211
installDsl(engine)
1312

1413
}
1514

16-
// These are our base custom types and functions for ore search.
17-
func installCsCustomTypes(engine *sql.DB) {
18-
path := "./custom-types.sql"
19-
sql, err := os.ReadFile(path)
20-
if err != nil {
21-
log.Fatalf("Failed to read custom types SQL file: %v", err)
22-
}
23-
24-
_, err = engine.Exec(string(sql))
25-
if err != nil {
26-
log.Fatalf("Failed to execute SQL query: %v", err)
27-
}
28-
log.Println("Custom types installed!")
29-
}
30-
3115
// Installing EQL
3216
func installDsl(engine *sql.DB) {
3317
path := "../../../release/cipherstash-encrypt-dsl.sql"

release/cipherstash-encrypt-dsl.sql

+317
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,320 @@
1+
CREATE EXTENSION IF NOT EXISTS pgcrypto;
2+
3+
CREATE TYPE ore_64_8_v1_term AS (
4+
bytes bytea
5+
);
6+
7+
CREATE TYPE ore_64_8_v1 AS (
8+
terms ore_64_8_v1_term[]
9+
);
10+
11+
CREATE OR REPLACE FUNCTION compare_ore_64_8_v1_term(a ore_64_8_v1_term, b ore_64_8_v1_term) returns integer AS $$
12+
DECLARE
13+
eq boolean := true;
14+
unequal_block smallint := 0;
15+
hash_key bytea;
16+
target_block bytea;
17+
18+
left_block_size CONSTANT smallint := 16;
19+
right_block_size CONSTANT smallint := 32;
20+
right_offset CONSTANT smallint := 136; -- 8 * 17
21+
22+
indicator smallint := 0;
23+
BEGIN
24+
IF a IS NULL AND b IS NULL THEN
25+
RETURN 0;
26+
END IF;
27+
28+
IF a IS NULL THEN
29+
RETURN -1;
30+
END IF;
31+
32+
IF b IS NULL THEN
33+
RETURN 1;
34+
END IF;
35+
36+
IF bit_length(a.bytes) != bit_length(b.bytes) THEN
37+
RAISE EXCEPTION 'Ciphertexts are different lengths';
38+
END IF;
39+
40+
FOR block IN 0..7 LOOP
41+
-- Compare each PRP (byte from the first 8 bytes) and PRF block (8 byte
42+
-- chunks of the rest of the value).
43+
-- NOTE:
44+
-- * Substr is ordinally indexed (hence 1 and not 0, and 9 and not 8).
45+
-- * We are not worrying about timing attacks here; don't fret about
46+
-- the OR or !=.
47+
IF
48+
substr(a.bytes, 1 + block, 1) != substr(b.bytes, 1 + block, 1)
49+
OR substr(a.bytes, 9 + left_block_size * block, left_block_size) != substr(b.bytes, 9 + left_block_size * BLOCK, left_block_size)
50+
THEN
51+
-- set the first unequal block we find
52+
IF eq THEN
53+
unequal_block := block;
54+
END IF;
55+
eq = false;
56+
END IF;
57+
END LOOP;
58+
59+
IF eq THEN
60+
RETURN 0::integer;
61+
END IF;
62+
63+
-- Hash key is the IV from the right CT of b
64+
hash_key := substr(b.bytes, right_offset + 1, 16);
65+
66+
-- first right block is at right offset + nonce_size (ordinally indexed)
67+
target_block := substr(b.bytes, right_offset + 17 + (unequal_block * right_block_size), right_block_size);
68+
69+
indicator := (
70+
get_bit(
71+
encrypt(
72+
substr(a.bytes, 9 + (left_block_size * unequal_block), left_block_size),
73+
hash_key,
74+
'aes-ecb'
75+
),
76+
0
77+
) + get_bit(target_block, get_byte(a.bytes, unequal_block))) % 2;
78+
79+
IF indicator = 1 THEN
80+
RETURN 1::integer;
81+
ELSE
82+
RETURN -1::integer;
83+
END IF;
84+
END;
85+
$$ LANGUAGE plpgsql;
86+
87+
88+
CREATE OR REPLACE FUNCTION ore_64_8_v1_term_eq(a ore_64_8_v1_term, b ore_64_8_v1_term) RETURNS boolean AS $$
89+
SELECT compare_ore_64_8_v1_term(a, b) = 0
90+
$$ LANGUAGE SQL;
91+
92+
CREATE OR REPLACE FUNCTION ore_64_8_v1_term_neq(a ore_64_8_v1_term, b ore_64_8_v1_term) RETURNS boolean AS $$
93+
SELECT compare_ore_64_8_v1_term(a, b) <> 0
94+
$$ LANGUAGE SQL;
95+
96+
CREATE OR REPLACE FUNCTION ore_64_8_v1_term_lt(a ore_64_8_v1_term, b ore_64_8_v1_term) RETURNS boolean AS $$
97+
SELECT compare_ore_64_8_v1_term(a, b) = -1
98+
$$ LANGUAGE SQL;
99+
100+
CREATE OR REPLACE FUNCTION ore_64_8_v1_term_lte(a ore_64_8_v1_term, b ore_64_8_v1_term) RETURNS boolean AS $$
101+
SELECT compare_ore_64_8_v1_term(a, b) != 1
102+
$$ LANGUAGE SQL;
103+
104+
CREATE OR REPLACE FUNCTION ore_64_8_v1_term_gt(a ore_64_8_v1_term, b ore_64_8_v1_term) RETURNS boolean AS $$
105+
SELECT compare_ore_64_8_v1_term(a, b) = 1
106+
$$ LANGUAGE SQL;
107+
108+
CREATE OR REPLACE FUNCTION ore_64_8_v1_term_gte(a ore_64_8_v1_term, b ore_64_8_v1_term) RETURNS boolean AS $$
109+
SELECT compare_ore_64_8_v1_term(a, b) != -1
110+
$$ LANGUAGE SQL;
111+
112+
CREATE OPERATOR = (
113+
PROCEDURE="ore_64_8_v1_term_eq",
114+
LEFTARG=ore_64_8_v1_term,
115+
RIGHTARG=ore_64_8_v1_term,
116+
NEGATOR = <>,
117+
RESTRICT = eqsel,
118+
JOIN = eqjoinsel,
119+
HASHES,
120+
MERGES
121+
);
122+
123+
CREATE OPERATOR <> (
124+
PROCEDURE="ore_64_8_v1_term_neq",
125+
LEFTARG=ore_64_8_v1_term,
126+
RIGHTARG=ore_64_8_v1_term,
127+
NEGATOR = =,
128+
RESTRICT = eqsel,
129+
JOIN = eqjoinsel,
130+
HASHES,
131+
MERGES
132+
);
133+
134+
CREATE OPERATOR > (
135+
PROCEDURE="ore_64_8_v1_term_gt",
136+
LEFTARG=ore_64_8_v1_term,
137+
RIGHTARG=ore_64_8_v1_term,
138+
COMMUTATOR = <,
139+
NEGATOR = <=,
140+
RESTRICT = scalargtsel,
141+
JOIN = scalargtjoinsel
142+
);
143+
144+
CREATE OPERATOR < (
145+
PROCEDURE="ore_64_8_v1_term_lt",
146+
LEFTARG=ore_64_8_v1_term,
147+
RIGHTARG=ore_64_8_v1_term,
148+
COMMUTATOR = >,
149+
NEGATOR = >=,
150+
RESTRICT = scalarltsel,
151+
JOIN = scalarltjoinsel
152+
);
153+
154+
CREATE OPERATOR <= (
155+
PROCEDURE="ore_64_8_v1_term_lte",
156+
LEFTARG=ore_64_8_v1_term,
157+
RIGHTARG=ore_64_8_v1_term,
158+
COMMUTATOR = >=,
159+
NEGATOR = >,
160+
RESTRICT = scalarlesel,
161+
JOIN = scalarlejoinsel
162+
);
163+
164+
CREATE OPERATOR >= (
165+
PROCEDURE="ore_64_8_v1_term_gte",
166+
LEFTARG=ore_64_8_v1_term,
167+
RIGHTARG=ore_64_8_v1_term,
168+
COMMUTATOR = <=,
169+
NEGATOR = <,
170+
RESTRICT = scalarlesel,
171+
JOIN = scalarlejoinsel
172+
);
173+
174+
CREATE OPERATOR FAMILY ore_64_8_v1_term_btree_ops USING btree;
175+
CREATE OPERATOR CLASS ore_64_8_v1_term_btree_ops DEFAULT FOR TYPE ore_64_8_v1_term USING btree FAMILY ore_64_8_v1_term_btree_ops AS
176+
OPERATOR 1 <,
177+
OPERATOR 2 <=,
178+
OPERATOR 3 =,
179+
OPERATOR 4 >=,
180+
OPERATOR 5 >,
181+
FUNCTION 1 compare_ore_64_8_v1_term(a ore_64_8_v1_term, b ore_64_8_v1_term);
182+
183+
-- Compare the "head" of each array and recurse if necessary
184+
-- This function assumes an empty string is "less than" everything else
185+
-- so if a is empty we return -1, if be is empty and a isn't, we return 1.
186+
-- If both are empty we return 0. This cases probably isn't necessary as equality
187+
-- doesn't always make sense but it's here for completeness.
188+
-- If both are non-empty, we compare the first element. If they are equal
189+
-- we need to consider the next block so we recurse, otherwise we return the comparison result.
190+
CREATE OR REPLACE FUNCTION compare_ore_array(a ore_64_8_v1_term[], b ore_64_8_v1_term[]) returns integer AS $$
191+
DECLARE
192+
cmp_result integer;
193+
BEGIN
194+
IF (array_length(a, 1) = 0 OR a IS NULL) AND (array_length(b, 1) = 0 OR b IS NULL) THEN
195+
RETURN 0;
196+
END IF;
197+
IF array_length(a, 1) = 0 OR a IS NULL THEN
198+
RETURN -1;
199+
END IF;
200+
IF array_length(b, 1) = 0 OR a IS NULL THEN
201+
RETURN 1;
202+
END IF;
203+
204+
cmp_result := compare_ore_64_8_v1_term(a[1], b[1]);
205+
IF cmp_result = 0 THEN
206+
-- Removes the first element in the array, and calls this fn again to compare the next element/s in the array.
207+
RETURN compare_ore_array(a[2:array_length(a,1)], b[2:array_length(b,1)]);
208+
END IF;
209+
210+
RETURN cmp_result;
211+
END
212+
$$ LANGUAGE plpgsql;
213+
214+
-- This function uses lexicographic comparison
215+
CREATE OR REPLACE FUNCTION compare_ore_64_8_v1(a ore_64_8_v1, b ore_64_8_v1) returns integer AS $$
216+
DECLARE
217+
cmp_result integer;
218+
BEGIN
219+
-- Recursively compare blocks bailing as soon as we can make a decision
220+
RETURN compare_ore_array(a.terms, b.terms);
221+
END
222+
$$ LANGUAGE plpgsql;
223+
224+
CREATE OR REPLACE FUNCTION ore_64_8_v1_eq(a ore_64_8_v1, b ore_64_8_v1) RETURNS boolean AS $$
225+
SELECT compare_ore_64_8_v1(a, b) = 0
226+
$$ LANGUAGE SQL;
227+
228+
CREATE OR REPLACE FUNCTION ore_64_8_v1_neq(a ore_64_8_v1, b ore_64_8_v1) RETURNS boolean AS $$
229+
SELECT compare_ore_64_8_v1(a, b) <> 0
230+
$$ LANGUAGE SQL;
231+
232+
CREATE OR REPLACE FUNCTION ore_64_8_v1_lt(a ore_64_8_v1, b ore_64_8_v1) RETURNS boolean AS $$
233+
SELECT compare_ore_64_8_v1(a, b) = -1
234+
$$ LANGUAGE SQL;
235+
236+
CREATE OR REPLACE FUNCTION ore_64_8_v1_lte(a ore_64_8_v1, b ore_64_8_v1) RETURNS boolean AS $$
237+
SELECT compare_ore_64_8_v1(a, b) != 1
238+
$$ LANGUAGE SQL;
239+
240+
CREATE OR REPLACE FUNCTION ore_64_8_v1_gt(a ore_64_8_v1, b ore_64_8_v1) RETURNS boolean AS $$
241+
SELECT compare_ore_64_8_v1(a, b) = 1
242+
$$ LANGUAGE SQL;
243+
244+
CREATE OR REPLACE FUNCTION ore_64_8_v1_gte(a ore_64_8_v1, b ore_64_8_v1) RETURNS boolean AS $$
245+
SELECT compare_ore_64_8_v1(a, b) != -1
246+
$$ LANGUAGE SQL;
247+
248+
CREATE OPERATOR = (
249+
PROCEDURE="ore_64_8_v1_eq",
250+
LEFTARG=ore_64_8_v1,
251+
RIGHTARG=ore_64_8_v1,
252+
NEGATOR = <>,
253+
RESTRICT = eqsel,
254+
JOIN = eqjoinsel,
255+
HASHES,
256+
MERGES
257+
);
258+
259+
CREATE OPERATOR <> (
260+
PROCEDURE="ore_64_8_v1_neq",
261+
LEFTARG=ore_64_8_v1,
262+
RIGHTARG=ore_64_8_v1,
263+
NEGATOR = =,
264+
RESTRICT = eqsel,
265+
JOIN = eqjoinsel,
266+
HASHES,
267+
MERGES
268+
);
269+
270+
CREATE OPERATOR > (
271+
PROCEDURE="ore_64_8_v1_gt",
272+
LEFTARG=ore_64_8_v1,
273+
RIGHTARG=ore_64_8_v1,
274+
COMMUTATOR = <,
275+
NEGATOR = <=,
276+
RESTRICT = scalargtsel,
277+
JOIN = scalargtjoinsel
278+
);
279+
280+
CREATE OPERATOR < (
281+
PROCEDURE="ore_64_8_v1_lt",
282+
LEFTARG=ore_64_8_v1,
283+
RIGHTARG=ore_64_8_v1,
284+
COMMUTATOR = >,
285+
NEGATOR = >=,
286+
RESTRICT = scalarltsel,
287+
JOIN = scalarltjoinsel
288+
);
289+
290+
CREATE OPERATOR <= (
291+
PROCEDURE="ore_64_8_v1_lte",
292+
LEFTARG=ore_64_8_v1,
293+
RIGHTARG=ore_64_8_v1,
294+
COMMUTATOR = >=,
295+
NEGATOR = >,
296+
RESTRICT = scalarlesel,
297+
JOIN = scalarlejoinsel
298+
);
299+
300+
CREATE OPERATOR >= (
301+
PROCEDURE="ore_64_8_v1_gte",
302+
LEFTARG=ore_64_8_v1,
303+
RIGHTARG=ore_64_8_v1,
304+
COMMUTATOR = <=,
305+
NEGATOR = <,
306+
RESTRICT = scalarlesel,
307+
JOIN = scalarlejoinsel
308+
);
309+
310+
CREATE OPERATOR FAMILY ore_64_8_v1_btree_ops USING btree;
311+
CREATE OPERATOR CLASS ore_64_8_v1_btree_ops DEFAULT FOR TYPE ore_64_8_v1 USING btree FAMILY ore_64_8_v1_btree_ops AS
312+
OPERATOR 1 <,
313+
OPERATOR 2 <=,
314+
OPERATOR 3 =,
315+
OPERATOR 4 >=,
316+
OPERATOR 5 >,
317+
FUNCTION 1 compare_ore_64_8_v1(a ore_64_8_v1, b ore_64_8_v1);
1318
DROP CAST IF EXISTS (text AS ore_64_8_v1_term);
2319

3320
DROP FUNCTION IF EXISTS cs_match_v1;

0 commit comments

Comments
 (0)