Skip to content

Commit

Permalink
Add multi-node support; simplify operations specification; update tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
lapets committed Dec 5, 2024
1 parent 204ed0d commit d15fcc9
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 30 deletions.
24 changes: 10 additions & 14 deletions src/nilql/nilql.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
_PLAINTEXT_UNSIGNED_INTEGER_MAX = 4294967296
_PLAINTEXT_STRING_BUFFER_LEN_MAX = 4096

def secret_key(cluster: dict, operations: dict = None) -> dict:
def secret_key(cluster: dict = None, operations: dict = None) -> dict:
"""
Return a secret key built according to what is specified in the supplied
cluster configuration and operation list.
Expand All @@ -23,23 +23,16 @@ def secret_key(cluster: dict, operations: dict = None) -> dict:
operations = {} or operations
instance = {
'value': None,
'cluster': {
'decentralized': False
},
'operations': {
'match': False,
'sum': False
}
'cluster': cluster,
'operations': operations
}
instance['cluster'].update(cluster)
instance['operations'].update(operations)

if (instance['operations']['match'] and instance['operations']['sum']):
if len([op for (op, status) in instance['operations'].items() if status]) > 1:
raise ValueError(
'cannot create secret key that supports both match and sum operations'
'cannot create secret key that supports multiple operations'
)

if (not instance['operations']['match'] and not instance['operations']['sum']):
if len([op for (op, status) in instance['operations'].items() if status]) < 1:
raise ValueError(
'cannot create secret key that supports no operations'
)
Expand All @@ -58,7 +51,7 @@ def encrypt(key: dict, plaintext: Union[int, str]) -> bytes:
instance = None

# Encrypting (i.e., hashing) a value for matching.
if 'salt' in key['value'] and key['operations']['match'] and not key['operations']['sum']:
if 'salt' in key['value'] and key['operations']['match']:
buffer = None

# Encrypting (i.e., hashing) an integer for matching.
Expand All @@ -77,6 +70,9 @@ def encrypt(key: dict, plaintext: Union[int, str]) -> bytes:

instance = hashlib.sha512(key['value']['salt'] + buffer).digest()

if len(key['cluster']['nodes']) > 1:
instance = [instance for _ in key['cluster']['nodes']]

return instance

if __name__ == '__main__':
Expand Down
46 changes: 30 additions & 16 deletions test/test_nilql.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ def test_secret_key_creation(self):
"""
Test key generation.
"""
cluster = {'decentralized': False}
operations = {'match': True, 'sum': False}
cluster = {'nodes': [{}]}
operations = {'match': True}
sk = nilql.secret_key(cluster, operations)
self.assertTrue('value' in sk)

Expand All @@ -35,42 +35,56 @@ def test_secret_key_creation_errors(self):
"""
with pytest.raises(
ValueError,
match='cannot create secret key that supports both match and sum operations'
match='cannot create secret key that supports multiple operations'
):
cluster = {'decentralized': False}
cluster = {'nodes': [{}]}
operations = {'match': True, 'sum': True}
nilql.secret_key(cluster, operations)

with pytest.raises(
ValueError,
match='cannot create secret key that supports no operations'
):
cluster = {'decentralized': False}
operations = {'match': False, 'sum': False}
cluster = {'nodes': [{}]}
operations = {}
nilql.secret_key(cluster, operations)

def test_encrypt_of_int_for_match(self):
"""
Test encryption of integer for matching.
"""
cluster = {'decentralized': False}
operations = {'match': True, 'sum': False}
cluster = {'nodes': [{}]}
operations = {'match': True}
sk = nilql.secret_key(cluster, operations)
plaintext = 123
ciphertext = nilql.encrypt(sk, plaintext)
self.assertTrue(isinstance(ciphertext, bytes) and len(ciphertext) == 64)

def test_encrypt_of_str_for_match(self):
def test_encrypt_of_str_for_match_single(self):
"""
Test encryption of string for matching.
"""
cluster = {'decentralized': False}
operations = {'match': True, 'sum': False}
sk = nilql.secret_key(cluster, operations)
sk = nilql.secret_key({'nodes': [{}]}, {'match': True})
plaintext = 'ABC'
ciphertext = nilql.encrypt(sk, plaintext)
self.assertTrue(isinstance(ciphertext, bytes) and len(ciphertext) == 64)

def test_encrypt_of_str_for_match_multiple(self):
"""
Test encryption of string for matching.
"""
sk = nilql.secret_key({'nodes': [{}, {}]}, {'match': True})
plaintext = 'ABC'
ciphertext = nilql.encrypt(sk, plaintext)
self.assertTrue(
len(ciphertext) == 2
and
all(
isinstance(c, bytes) and len(c) == 64
for c in ciphertext
)
)

def test_encrypt_of_int_for_match_error(self):
"""
Test range error during encryption of integer for matching.
Expand All @@ -79,8 +93,8 @@ def test_encrypt_of_int_for_match_error(self):
ValueError,
match='plaintext must be 32-bit nonnegative integer value'
):
cluster = {'decentralized': False}
operations = {'match': True, 'sum': False}
cluster = {'nodes': [{}]}
operations = {'match': True}
sk = nilql.secret_key(cluster, operations)
plaintext = 2**32
nilql.encrypt(sk, plaintext)
Expand All @@ -93,8 +107,8 @@ def test_encrypt_of_str_for_match_error(self):
ValueError,
match='plaintext string must be possible to encode in 4096 bytes or fewer'
):
cluster = {'decentralized': False}
operations = {'match': True, 'sum': False}
cluster = {'nodes': [{}]}
operations = {'match': True}
sk = nilql.secret_key(cluster, operations)
plaintext = 'X' * 4097
nilql.encrypt(sk, plaintext)

0 comments on commit d15fcc9

Please sign in to comment.