Skip to content

Commit 81f0348

Browse files
committed
Test case: incorrect key ID in Get Key with Key IDs
1 parent c887d8e commit 81f0348

6 files changed

Lines changed: 67 additions & 20 deletions

File tree

TODO

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
TODO: Tests get wrong key-id over ETSI QKD API
2-
31
TODO: Put .out files in a sub-directory
42

53
TODO: Add pool thresholds to configuration (and document this)

client/client.py

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"""
44

55
import asyncio
6+
from typing import Any, List, Tuple
67
from uuid import UUID
8+
from fastapi import status
79
from common import exceptions
810
from common import shamir
911
from common import utils
@@ -170,11 +172,13 @@ async def scatter_key_amongst_peer_hubs(
170172
f"for key ID {key.key_id}"
171173
)
172174
if nr_shares_successfully_scattered < _MIN_NR_SHARES:
173-
causes = [
174-
str(result) for result in results if isinstance(result, Exception)
175-
]
175+
causes, status_code = self.summarize_failure(results)
176176
raise exceptions.CouldNotScatterEnoughSharesError(
177-
key.key_id, nr_shares_successfully_scattered, _MIN_NR_SHARES, causes
177+
key.key_id,
178+
nr_shares_successfully_scattered,
179+
_MIN_NR_SHARES,
180+
status_code,
181+
causes,
178182
)
179183

180184
async def gather_key_from_peer_hubs(
@@ -201,11 +205,13 @@ async def gather_key_from_peer_hubs(
201205
f"for key ID {key_id}"
202206
)
203207
if nr_shares_successfully_gathered < _MIN_NR_SHARES:
204-
causes = [
205-
str(result) for result in results if isinstance(result, Exception)
206-
]
208+
causes, status_code = self.summarize_failure(results)
207209
raise exceptions.CouldNotGatherEnoughSharesError(
208-
key_id, nr_shares_successfully_gathered, _MIN_NR_SHARES, causes
210+
key_id,
211+
nr_shares_successfully_gathered,
212+
_MIN_NR_SHARES,
213+
status_code,
214+
causes,
209215
)
210216
shamir_input = [(share.share_index, share.value) for share in shares]
211217
try:
@@ -216,3 +222,22 @@ async def gather_key_from_peer_hubs(
216222
raise exceptions.ShamirReconstructError(key_id, str(exc)) from exc
217223
key = UserKey(key_id, key_value)
218224
return key
225+
226+
@staticmethod
227+
def summarize_failure(hub_results: List[Any]) -> Tuple[List[str], int]:
228+
"""
229+
Map multiple peer hub failures to a single summary ETSI failure.
230+
- A list of cause strings (included in the details of the ETSI exception)
231+
- The status code to be used in the ETSI exception, the worst of the peer status codes.
232+
"""
233+
status_code = None
234+
causes = []
235+
for hub_result in hub_results:
236+
if isinstance(hub_result, Exception):
237+
causes.append(str(hub_result))
238+
if isinstance(hub_result, exceptions.DSKEException):
239+
if status_code is None or hub_result.status_code > status_code:
240+
status_code = hub_result.status_code
241+
if status_code is None:
242+
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
243+
return (causes, status_code)

common/exceptions.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Exceptions.
33
"""
44

5+
import json
56
from typing import List
67
from uuid import UUID
78
from fastapi import status
@@ -61,7 +62,6 @@ def __init__(
6162
response: str | None = None,
6263
exception: str | None = None,
6364
):
64-
message = "HTTP request failed."
6565
details = {}
6666
details["method"] = method
6767
details["url"] = url
@@ -77,8 +77,17 @@ def __init__(
7777
details["response"] = response
7878
if exception is not None:
7979
details["exception"] = exception
80+
explain = ""
81+
if response is not None:
82+
try:
83+
response_json = json.loads(response) # type: ignore
84+
if "message" in response_json:
85+
explain = f" - {response_json['message']}"
86+
except Exception: # pylint: disable=broad-except
87+
pass
88+
message = f"HTTP request failed ({method} {url}: {status_code}{explain})."
8089
super().__init__(
81-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
90+
status_code=status_code or status.HTTP_500_INTERNAL_SERVER_ERROR,
8291
message=message,
8392
details=details,
8493
)
@@ -129,11 +138,11 @@ class UnknownKeyIDError(DSKEException):
129138
Exception raised when an unknown key ID is provided.
130139
"""
131140

132-
def __init__(self, key_id: str):
141+
def __init__(self, key_id: UUID):
133142
super().__init__(
134143
status_code=status.HTTP_400_BAD_REQUEST,
135144
message="Unknown key ID",
136-
details={"key_ID": key_id},
145+
details={"key_ID": str(key_id)},
137146
)
138147

139148

@@ -147,6 +156,7 @@ def __init__(
147156
key_id: UUID,
148157
nr_successful_shares: int,
149158
nr_required_shares: int,
159+
status_code: int,
150160
causes=List[str],
151161
):
152162
details = {
@@ -157,7 +167,7 @@ def __init__(
157167
if causes:
158168
details["causes"] = causes
159169
super().__init__(
160-
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
170+
status_code=status_code,
161171
message="Could not scatter enough shares for key.",
162172
details=details,
163173
)
@@ -173,6 +183,7 @@ def __init__(
173183
key_id: UUID,
174184
nr_successful_shares: int,
175185
nr_required_shares: int,
186+
status_code: int,
176187
causes=List[str],
177188
):
178189
details = {
@@ -183,7 +194,7 @@ def __init__(
183194
if causes:
184195
details["causes"] = causes
185196
super().__init__(
186-
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
197+
status_code=status_code,
187198
message="Could not gather enough shares for key.",
188199
details=details,
189200
)

hub/__main__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,11 @@ async def dske_exception_handler(_request: fastapi.Request, exc: DSKEException):
9494
Handle DSKE exceptions.
9595
"""
9696
# Error responses are not signed.
97-
return fastapi.responses.JSONResponse(
97+
response = fastapi.responses.JSONResponse(
9898
status_code=exc.status_code,
9999
content={"message": exc.message, "details": exc.details},
100100
)
101+
return response
101102

102103

103104
@_APP.put(f"/hub/{_HUB.name}/dske/oob/v1/registration")

system_tests/system_test_common.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,10 @@ def extract_key_id(output) -> None | str:
214214
Extract the key ID from the output of a Get Key request.
215215
"""
216216
for line in output:
217-
print(f"Searching for key ID in line: <{line}>", file=sys.stderr) ### DEBUG
218217
match = re.search(r'"key_ID": "(\S+)"', line)
219-
print(f"{match=}", file=sys.stderr) ### DEBUG
220218
if match:
221-
return match.group(1)
219+
key_id = match.group(1)
220+
return key_id
222221
return None
223222

224223

system_tests/test_etsi_qkd.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
System test for the ETSI QKD API.
33
"""
44

5+
import uuid
56
import pytest
67
from . import system_test_common
78

@@ -44,3 +45,15 @@ def test_key_id_not_uuid():
4445
system_test_common.get_key_with_key_ids(
4546
"sam", "sofia", "not-a-uuid", expected_status_code=400
4647
)
48+
49+
50+
def test_wrong_key_id():
51+
"""
52+
ETSI QKD Get key with key IDs, using a key ID that was not returned by Get Key (expect error).
53+
"""
54+
key_id = system_test_common.get_key("sam", "sofia")
55+
assert key_id is not None
56+
wrong_uuid = str(uuid.uuid4())
57+
system_test_common.get_key_with_key_ids(
58+
"sam", "sofia", wrong_uuid, expected_status_code=400
59+
)

0 commit comments

Comments
 (0)