-
Notifications
You must be signed in to change notification settings - Fork 0
ORE Operators #83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ORE Operators #83
Conversation
da620ec
to
bfa139f
Compare
bfa139f
to
7442ae8
Compare
o boolean; | ||
BEGIN | ||
BEGIN | ||
u := (SELECT cs_unique_v1(a) = cs_unique_v1(b)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% what the threat model might be but its worth noting that this comparison won't run in constant time. It might leave the possibility for an attacker to learn the value of the plaintext in certain circumstances.
There's a constant time comparison function in the EQL repo somewhere I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this:
CREATE FUNCTION __bytea_ct_eq(a bytea, b bytea) RETURNS boolean AS $$ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cs_unique_v1
is the existing function, and simply extracts the index value from the eql::ciphertext
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unique indexes are cast as text
, so this ends up using the default text eq operators.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's what I figured. The standard equality operator will bail as soon as a character differs which can lead to timing side-channel attacks. A constant time comparison should always take the same amount of time regardless of which character index differs first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh right. We should fix that for unique in general then
END; | ||
|
||
BEGIN | ||
o := (SELECT cs_ore_64_8_v1(a) = cs_ore_64_8_v1(b)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cs_ore_64_8_v1
is same as unique, pulls the field from the json
The returned data is cast as ore_64_8_v1
and then the underlying ore operators are used for the comparison
DROP OPERATOR IF EXISTS @> (cs_encrypted_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS @> (cs_encrypted_v1, cs_match_index_v1); | ||
DROP OPERATOR IF EXISTS @> (cs_match_index_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS <@ (cs_encrypted_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS <@ (cs_encrypted_v1, cs_match_index_v1); | ||
DROP OPERATOR IF EXISTS <@ (cs_match_index_v1, cs_encrypted_v1); | ||
|
||
|
||
DROP OPERATOR IF EXISTS <= (ore_64_8_v1, cs_encrypted_v1); | ||
DROP OPERATOR IF EXISTS <= (cs_encrypted_v1, ore_64_8_v1); | ||
DROP OPERATOR IF EXISTS <= (jsonb, cs_encrypted_v1); | ||
DROP OPERATOR IF EXISTS <= (cs_encrypted_v1, jsonb); | ||
DROP OPERATOR IF EXISTS <= (cs_encrypted_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS >=(ore_64_8_v1, cs_encrypted_v1); | ||
DROP OPERATOR IF EXISTS >= (jsonb, cs_encrypted_v1); | ||
DROP OPERATOR IF EXISTS >= (cs_encrypted_v1, ore_64_8_v1); | ||
DROP OPERATOR IF EXISTS >= (cs_encrypted_v1, jsonb); | ||
DROP OPERATOR IF EXISTS >=(cs_encrypted_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS <(ore_64_8_v1, cs_encrypted_v1); | ||
DROP OPERATOR IF EXISTS <(cs_encrypted_v1, ore_64_8_v1); | ||
DROP OPERATOR IF EXISTS < (jsonb, cs_encrypted_v1); | ||
DROP OPERATOR IF EXISTS < (cs_encrypted_v1, jsonb); | ||
DROP OPERATOR IF EXISTS < (cs_encrypted_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS > (ore_64_8_v1, cs_encrypted_v1); | ||
DROP OPERATOR IF EXISTS > (jsonb, cs_encrypted_v1); | ||
DROP OPERATOR IF EXISTS > (cs_encrypted_v1, ore_64_8_v1); | ||
DROP OPERATOR IF EXISTS > (cs_encrypted_v1, jsonb); | ||
DROP OPERATOR IF EXISTS > (cs_encrypted_v1, cs_encrypted_v1); | ||
|
||
|
||
DROP OPERATOR IF EXISTS = (cs_encrypted_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS = (cs_encrypted_v1, jsonb); | ||
DROP OPERATOR IF EXISTS = (jsonb, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS = (cs_encrypted_v1, cs_unique_index_v1); | ||
DROP OPERATOR IF EXISTS = (cs_unique_index_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS = (cs_encrypted_v1, ore_64_8_v1); | ||
DROP OPERATOR IF EXISTS = (ore_64_8_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, jsonb); | ||
DROP OPERATOR IF EXISTS <> (jsonb, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, cs_unique_index_v1); | ||
DROP OPERATOR IF EXISTS <> (cs_unique_index_v1, cs_encrypted_v1); | ||
|
||
DROP OPERATOR IF EXISTS <> (ore_64_8_v1, cs_encrypted_v1); | ||
DROP OPERATOR IF EXISTS <> (cs_encrypted_v1, ore_64_8_v1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: I haven't checked that you've captured all the operators here. I'm assuming running the script will show if that's not the case!
@@ -0,0 +1,90 @@ | |||
\set ON_ERROR_STOP on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL.
tests/operators-match.sql
Outdated
-- MATCH <@ OPERATORS | ||
DO $$ | ||
BEGIN | ||
-- SANITY CHECK FOR UNIQUE payloads |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the comment. Is that a copy-paste error or am I missing something about what this test does?
ASSERT (SELECT EXISTS ( | ||
SELECT id FROM users WHERE name_encrypted < ore_cs_encrypted::jsonb | ||
)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests are fine but in my experience ORE tests with hard-coded values can give false confidence. Because an incorrect implementation will still "work" ~50% of the time. Not for this PR but we may want to think about how to generate random ORE encrypted values for every test (like a property test) to validate correctness more robustly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EPIC PR. Nice work.
A couple of suggestions for future improvements.
Add ORE operators for the
cs_encrypted_v1
typeAlso generalises equality operators to support both unique and ore indexes.
Addresses:
https://linear.app/cipherstash/issue/CIP-1148/implement-ore-operators-for-cs-encrypted-v1
https://linear.app/cipherstash/issue/CIP-1149/equality-operator-needs-to-handle-both-unique-and-ore-index-types