Skip to content

Commit 199e957

Browse files
committed
Make many things configurable
1 parent f6fcce0 commit 199e957

12 files changed

Lines changed: 405 additions & 181 deletions

File tree

TODO

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
TODO: Put .out files in a sub-directory
22

3+
TODO: Error message if --client or --hub does not exist in configuration
4+
35
TODO: Add pool thresholds to configuration (and document this)
46

57
TODO: Cleanup Shamir's Secret Sharing (SSS) code (no back-and-forth conversion to RawShare)
@@ -8,8 +10,6 @@ TODO: Move code coverage data files to subdirectory
810

911
TODO: Stop all nodes after each test (to make sure they are stopped if test case fails)
1012

11-
TODO: Add a test cases with a netcat (nc) listener squatting the port
12-
1313
TODO: Add Sphinx documentation
1414
Use https://github.com/tox-dev/sphinx-autodoc-typehints
1515

@@ -28,8 +28,6 @@ TODO: OpenAPI (Swagger) documentation should show proper type for request and re
2828
TODO: Use data (instead of value) in Fragment for consistency. Also in documentation (including
2929
figures)
3030

31-
TODO: Avoid usage: __main__.py in --help output for client / hub
32-
3331
TODO: Add test case: start hub later than client (registration retry logic in client)
3432

3533
TODO: Forbid extra query parameters for API calls
@@ -55,6 +53,4 @@ TODO: Testcase for invalid signature
5553

5654
TODO: Test case for: if signature validation fails, return fragment to pool
5755

58-
TODO: Error message if --client or --hub does not exist in configuration
59-
6056
TODO: Check SAE IDs on GET share, just as we do for POST share

client/__main__.py

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,18 @@
22
Main module for a DSKE client.
33
"""
44

5-
import argparse
65
import contextlib
76
import os
87
import signal
98
from typing import Annotated
109
import fastapi
1110
import uvicorn
12-
from common import configuration
1311
from common import utils
1412
from common.exceptions import DSKEException, MissingAuthorizationHeaderError
13+
from .cli import parse_command_line_arguments
1514
from .client import Client
1615

1716

18-
def parse_command_line_arguments():
19-
"""
20-
Parse command line arguments.
21-
"""
22-
parser = argparse.ArgumentParser(description="DSKE Client")
23-
parser.add_argument("name", type=str, help="Client name")
24-
parser.add_argument(
25-
"--port", type=int, default=configuration._DEFAULT_BASE_PORT, help="Port number"
26-
)
27-
parser.add_argument(
28-
"--hubs",
29-
nargs="+",
30-
type=str,
31-
help=f"Base URLs for hubs (e.g., http://127.0.0.1:{configuration._DEFAULT_BASE_PORT})",
32-
)
33-
parser.add_argument(
34-
"--encryptors",
35-
nargs="+",
36-
type=str,
37-
help="Names (SAE IDs) of encryptors consuming keys from this client (KME).",
38-
)
39-
args = parser.parse_args()
40-
return args
41-
42-
4317
_ARGS = parse_command_line_arguments()
4418
peer_hub_urls = _ARGS.hubs
4519
if peer_hub_urls is None:
@@ -48,7 +22,15 @@ def parse_command_line_arguments():
4822
encryptor_names = []
4923
else:
5024
encryptor_names = _ARGS.encryptors
51-
_CLIENT = Client(_ARGS.name, encryptor_names, peer_hub_urls)
25+
_CLIENT = Client(
26+
_ARGS.name,
27+
_ARGS.start_request_psrd_threshold,
28+
_ARGS.stop_request_psrd_threshold,
29+
_ARGS.get_psrd_block_size,
30+
_ARGS.min_nr_shares,
31+
encryptor_names,
32+
peer_hub_urls,
33+
)
5234

5335

5436
@contextlib.asynccontextmanager

client/cli.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""
2+
Command line interface for DSKE client.
3+
"""
4+
5+
import argparse
6+
from common import configuration
7+
8+
9+
def _check_start_request_psrd_threshold(value):
10+
int_value = int(value)
11+
if (
12+
int_value <= configuration.MIN_START_REQUEST_PSRD_THRESHOLD
13+
or int_value > configuration.MAX_START_REQUEST_PSRD_THRESHOLD
14+
):
15+
raise argparse.ArgumentTypeError(
16+
f"value {value} is invalid; "
17+
f"it must be between "
18+
f"{configuration.MIN_START_REQUEST_PSRD_THRESHOLD} and "
19+
f"{configuration.MAX_START_REQUEST_PSRD_THRESHOLD}"
20+
)
21+
return int_value
22+
23+
24+
def _check_get_psrd_block_size(value):
25+
int_value = int(value)
26+
if (
27+
int_value <= configuration.MIN_GET_PSRD_BLOCK_SIZE
28+
or int_value > configuration.MAX_GET_PSRD_BLOCK_SIZE
29+
):
30+
raise argparse.ArgumentTypeError(
31+
f"value {value} is invalid; "
32+
f"it must be between "
33+
f"{configuration.MIN_GET_PSRD_BLOCK_SIZE} and "
34+
f"{configuration.MAX_GET_PSRD_BLOCK_SIZE}"
35+
)
36+
return int_value
37+
38+
39+
def _check_stop_request_psrd_threshold(value):
40+
int_value = int(value)
41+
if (
42+
int_value <= configuration.MIN_STOP_REQUEST_PSRD_THRESHOLD
43+
or int_value > configuration.MAX_STOP_REQUEST_PSRD_THRESHOLD
44+
):
45+
raise argparse.ArgumentTypeError(
46+
f"value {value} is invalid; "
47+
f"it must be between "
48+
f"{configuration.MIN_STOP_REQUEST_PSRD_THRESHOLD} and "
49+
f"{configuration.MAX_STOP_REQUEST_PSRD_THRESHOLD}"
50+
)
51+
return int_value
52+
53+
54+
def _check_min_nr_shares(value):
55+
int_value = int(value)
56+
if (
57+
int_value <= configuration.MIN_MIN_NR_SHARES
58+
or int_value > configuration.MAX_MIN_NR_SHARES
59+
):
60+
raise argparse.ArgumentTypeError(
61+
f"value {value} is invalid; "
62+
f"it must be between "
63+
f"{configuration.MIN_MIN_NR_SHARES} and "
64+
f"{configuration.MAX_MIN_NR_SHARES}"
65+
)
66+
return int_value
67+
68+
69+
def parse_command_line_arguments():
70+
"""
71+
Parse command line arguments.
72+
"""
73+
parser = argparse.ArgumentParser(description="DSKE Client", prog="client")
74+
parser.add_argument("name", type=str, help="Client name")
75+
parser.add_argument(
76+
"--port",
77+
type=int,
78+
help="Port number",
79+
)
80+
parser.add_argument(
81+
"--start-request-psrd_threshold",
82+
type=_check_start_request_psrd_threshold,
83+
default=configuration.DEFAULT_START_REQUEST_PSRD_THRESHOLD,
84+
help=(
85+
f"Start request PSRD threshold "
86+
f"(default: {configuration.DEFAULT_START_REQUEST_PSRD_THRESHOLD})"
87+
),
88+
)
89+
parser.add_argument(
90+
"--stop-request-psrd-threshold",
91+
type=_check_start_request_psrd_threshold,
92+
default=configuration.DEFAULT_STOP_REQUEST_PSRD_THRESHOLD,
93+
help=(
94+
f"Stop request PSRD threshold "
95+
f"(default: {configuration.DEFAULT_STOP_REQUEST_PSRD_THRESHOLD})"
96+
),
97+
)
98+
parser.add_argument(
99+
"--get-psrd-block-size",
100+
type=_check_get_psrd_block_size,
101+
default=configuration.DEFAULT_GET_PSRD_BLOCK_SIZE,
102+
help=(
103+
f"Request PSRD block size "
104+
f"(default: {configuration.DEFAULT_GET_PSRD_BLOCK_SIZE})"
105+
),
106+
)
107+
parser.add_argument(
108+
"--min-nr-shares",
109+
type=_check_min_nr_shares,
110+
default=configuration.DEFAULT_MIN_NR_SHARES,
111+
help=(
112+
f"Minimum number of shares "
113+
f"(default: {configuration.DEFAULT_MIN_NR_SHARES})"
114+
),
115+
)
116+
parser.add_argument(
117+
"--hubs",
118+
nargs="+",
119+
type=str,
120+
help=f"Base URLs for hubs (e.g., http://127.0.0.1:{configuration.DEFAULT_BASE_PORT})",
121+
)
122+
parser.add_argument(
123+
"--encryptors",
124+
nargs="+",
125+
type=str,
126+
help="Names (SAE IDs) of encryptors consuming keys from this client (KME).",
127+
)
128+
args = parser.parse_args()
129+
return args

client/client.py

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@
1313
from common.user_key import UserKey
1414
from .peer_hub import PeerHub
1515

16-
# TODO: Make this configurable
17-
# TODO: The Shamir code also has a max (is that really needed?)
18-
_MIN_NR_SHARES = 3 # The minimum number of key shares required to reconstruct the key.
19-
2016

2117
class Client:
2218
"""
@@ -29,40 +25,42 @@ class Client:
2925
_MAX_STORED_KEY_COUNT = 1_000 # Arbitrary large value
3026
_MAX_KEYS_PER_REQUEST = 1 # We don't support the number parameter for Get Key calls
3127

32-
_name: str
33-
_encryptor_names: list[str]
34-
_peer_hubs: list[PeerHub]
28+
name: str
29+
encryptor_names: list[str]
30+
peer_hubs: list[PeerHub]
3531

36-
def __init__(self, name: str, encryptor_names: list[str], peer_hub_urls: list[str]):
37-
self._name = name
38-
self._encryptor_names = encryptor_names
39-
self._peer_hubs = []
32+
def __init__(
33+
self,
34+
name: str,
35+
start_request_psrd_threshold: int,
36+
stop_request_psrd_threshold: int,
37+
get_psrd_block_size: int,
38+
min_nr_shares: int,
39+
encryptor_names: list[str],
40+
peer_hub_urls: list[str],
41+
):
42+
self.name = name
43+
self.start_request_psrd_threshold = start_request_psrd_threshold
44+
self.stop_request_psrd_threshold = stop_request_psrd_threshold
45+
self.get_psrd_block_size = get_psrd_block_size
46+
self.min_nr_shares = min_nr_shares
47+
self.encryptor_names = encryptor_names
48+
self.peer_hubs = []
4049
for peer_hub_url in peer_hub_urls:
4150
peer_hub = PeerHub(self, peer_hub_url)
42-
self._peer_hubs.append(peer_hub)
43-
44-
@property
45-
def name(self):
46-
"""
47-
Get the name.
48-
"""
49-
return self._name
50-
51-
@property
52-
def encryptor_names(self):
53-
"""
54-
Get the encryptor names.
55-
"""
56-
return self._encryptor_names
51+
self.peer_hubs.append(peer_hub)
5752

5853
def to_mgmt(self):
5954
"""
6055
Get the management status.
6156
"""
62-
peer_hubs_status = [peer_hub.to_mgmt() for peer_hub in self._peer_hubs]
57+
peer_hubs_status = [peer_hub.to_mgmt() for peer_hub in self.peer_hubs]
6358
return {
64-
"name": self._name,
65-
"encryptor_names": self._encryptor_names,
59+
"name": self.name,
60+
"start_request_psrd_threshold": self.start_request_psrd_threshold,
61+
"stop_request_psrd_threshold": self.stop_request_psrd_threshold,
62+
"get_psrd_block_size": self.get_psrd_block_size,
63+
"encryptor_names": self.encryptor_names,
6664
"peer_hubs": peer_hubs_status,
6765
}
6866

@@ -85,7 +83,7 @@ async def etsi_status(self, master_sae_id: str, slave_sae_id: str):
8583
# it is). For that reason, we return an arbitrary number as the stored key count.
8684
#
8785
return {
88-
"source_kme_id": self._name,
86+
"source_kme_id": self.name,
8987
"target_kme_id": "", # See comment above
9088
"master_sae_id": master_sae_id,
9189
"slave_sae_id": slave_sae_id,
@@ -154,7 +152,7 @@ def start_all_peer_hubs(self) -> None:
154152
"""
155153
Start all peer hubs.
156154
"""
157-
for peer_hub in self._peer_hubs:
155+
for peer_hub in self.peer_hubs:
158156
peer_hub.start_register_task()
159157

160158
async def scatter_key_amongst_peer_hubs(
@@ -166,14 +164,14 @@ async def scatter_key_amongst_peer_hubs(
166164
"""
167165
Split the key into key shares, and send each key share to a peer hub.
168166
"""
169-
nr_shares = len(self._peer_hubs)
167+
nr_shares = len(self.peer_hubs)
170168
shares = key.split_into_shares(
171-
master_sae_id, slave_sae_id, nr_shares, _MIN_NR_SHARES
169+
master_sae_id, slave_sae_id, nr_shares, self.min_nr_shares
172170
)
173171
assert len(shares) == nr_shares
174172
coroutines = [
175173
peer_hub.post_share(master_sae_id, slave_sae_id, share)
176-
for peer_hub, share in zip(self._peer_hubs, shares)
174+
for peer_hub, share in zip(self.peer_hubs, shares)
177175
]
178176
results = await asyncio.gather(*coroutines, return_exceptions=True)
179177
success_results = [
@@ -184,12 +182,12 @@ async def scatter_key_amongst_peer_hubs(
184182
f"Successfully scattered {nr_shares_successfully_scattered} out of {nr_shares} shares "
185183
f"for key ID {key.key_id}"
186184
)
187-
if nr_shares_successfully_scattered < _MIN_NR_SHARES:
185+
if nr_shares_successfully_scattered < self.min_nr_shares:
188186
causes, status_code = self.summarize_failure(results)
189187
raise exceptions.CouldNotScatterEnoughSharesError(
190188
key.key_id,
191189
nr_shares_successfully_scattered,
192-
_MIN_NR_SHARES,
190+
self.min_nr_shares,
193191
status_code,
194192
causes,
195193
)
@@ -204,10 +202,10 @@ async def gather_key_from_peer_hubs(
204202
Gather key shares from the peer hubs, and reconstruct the key out of (a subset of)
205203
the key shares.
206204
"""
207-
nr_shares_attempted_to_gather = len(self._peer_hubs)
205+
nr_shares_attempted_to_gather = len(self.peer_hubs)
208206
coroutines = [
209207
peer_hub.get_share(master_sae_id, slave_sae_id, key_id)
210-
for peer_hub in self._peer_hubs
208+
for peer_hub in self.peer_hubs
211209
]
212210
results = await asyncio.gather(*coroutines, return_exceptions=True)
213211
shares = [result for result in results if not isinstance(result, Exception)]
@@ -217,19 +215,19 @@ async def gather_key_from_peer_hubs(
217215
f"out of {nr_shares_attempted_to_gather} attempted "
218216
f"for key ID {key_id}"
219217
)
220-
if nr_shares_successfully_gathered < _MIN_NR_SHARES:
218+
if nr_shares_successfully_gathered < self.min_nr_shares:
221219
causes, status_code = self.summarize_failure(results)
222220
raise exceptions.CouldNotGatherEnoughSharesError(
223221
key_id,
224222
nr_shares_successfully_gathered,
225-
_MIN_NR_SHARES,
223+
self.min_nr_shares,
226224
status_code,
227225
causes,
228226
)
229227
shamir_input = [(share.share_index, share.value) for share in shares]
230228
try:
231229
key_value = shamir.reconstruct_binary_secret_from_shares(
232-
_MIN_NR_SHARES, shamir_input
230+
self.min_nr_shares, shamir_input
233231
)
234232
except ValueError as exc:
235233
raise exceptions.ShamirReconstructError(key_id, str(exc)) from exc

0 commit comments

Comments
 (0)