Skip to content

Commit 2dc952a

Browse files
committed
Add server and other things
1 parent f7a2350 commit 2dc952a

File tree

13 files changed

+171
-142
lines changed

13 files changed

+171
-142
lines changed

irrd/conf/__init__.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,7 @@ def _check_staging_config(self) -> List[str]:
244244
Validate the current staging configuration.
245245
Returns a list of any errors, or an empty list for a valid config.
246246
"""
247-
from irrd.utils.crypto import (
248-
ed25519_private_key_from_str,
249-
ed25519_public_key_from_str,
250-
)
247+
from irrd.utils.crypto import eckey_from_str
251248

252249
errors = []
253250
config = self.user_config_staging
@@ -433,7 +430,7 @@ def _validate_subconfig(key, value):
433430

434431
if details.get("nrtm4_client_initial_public_key"):
435432
try:
436-
ed25519_public_key_from_str(details["nrtm4_client_initial_public_key"])
433+
eckey_from_str(details["nrtm4_client_initial_public_key"])
437434
except ValueError as ve:
438435
errors.append(
439436
f"Invalid value for setting nrtm4_client_initial_public_key for source {name}: {ve}"
@@ -469,15 +466,15 @@ def _validate_subconfig(key, value):
469466

470467
if details.get("nrtm4_server_private_key"):
471468
try:
472-
ed25519_private_key_from_str(details["nrtm4_server_private_key"])
469+
eckey_from_str(details["nrtm4_server_private_key"], require_private=True)
473470
except ValueError as ve:
474471
errors.append(
475472
f"Invalid value for setting nrtm4_server_private_key for source {name}: {ve}"
476473
)
477474

478475
if details.get("nrtm4_server_private_key_next"):
479476
try:
480-
ed25519_private_key_from_str(details["nrtm4_server_private_key_next"])
477+
eckey_from_str(details["nrtm4_server_private_key_next"], require_private=True)
481478
except ValueError as ve:
482479
errors.append(
483480
f"Invalid value for setting nrtm4_server_private_key_next for source {name}: {ve}"

irrd/conf/test_conf.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
import pytest
77
import yaml
88

9+
from ..mirroring.nrtm4.tests import (
10+
MOCK_UNF_PRIVATE_KEY,
11+
MOCK_UNF_PRIVATE_KEY_OTHER_STR,
12+
MOCK_UNF_PRIVATE_KEY_STR,
13+
)
14+
from ..utils.crypto import eckey_public_key_as_str
915
from . import (
1016
ConfigurationError,
1117
config_init,
@@ -119,10 +125,10 @@ def test_load_valid_reload_valid_config(self, monkeypatch, save_yaml_config, tmp
119125
"TESTDB-NRTM4": {
120126
"keep_journal": True,
121127
"nrtm4_client_notification_file_url": "https://testhost/",
122-
"nrtm4_client_initial_public_key": "kL7kSk56ASeaHl6Nj0eXC3XCHkCzktoPA3ceKz/cjOo=",
128+
"nrtm4_client_initial_public_key": eckey_public_key_as_str(MOCK_UNF_PRIVATE_KEY),
123129
"nrtm4_server_base_url": "https://example.com",
124-
"nrtm4_server_private_key": "FalXchs8HIU22Efc3ipNcxVwYwB+Mp0x9TCM9BFtig0=",
125-
"nrtm4_server_private_key_next": "4YDgaXpRDIU8vJbFYeYgPQqEa4YAdHeRF1s6SLdXCsE=",
130+
"nrtm4_server_private_key": MOCK_UNF_PRIVATE_KEY_STR,
131+
"nrtm4_server_private_key_next": MOCK_UNF_PRIVATE_KEY_OTHER_STR,
126132
"nrtm4_server_local_path": str(tmpdir),
127133
"nrtm4_server_snapshot_frequency": 3600 * 2,
128134
},
@@ -457,14 +463,8 @@ def test_load_invalid_config(self, save_yaml_config, tmpdir):
457463
assert "Unknown setting key: log.unknown" in str(ce.value)
458464
assert "Unknown key(s) under source TESTDB: unknown" in str(ce.value)
459465

460-
assert (
461-
"Invalid value for setting nrtm4_server_private_key for source TESTDB: Incorrect padding"
462-
in str(ce.value)
463-
)
464-
assert (
465-
"Invalid value for setting nrtm4_server_private_key_next for source TESTDB: Incorrect padding"
466-
in str(ce.value)
467-
)
466+
assert "Invalid value for setting nrtm4_server_private_key for source TESTDB:" in str(ce.value)
467+
assert "Invalid value for setting nrtm4_server_private_key_next for source TESTDB:" in str(ce.value)
468468
assert "Setting nrtm4_server_base_url for source TESTDB is not a valid https or file URL." in str(
469469
ce.value
470470
)

irrd/integration_tests/run.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import ujson
1616
import yaml
1717
from alembic import command, config
18-
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
18+
from joserfc.rfc7518.ec_key import ECKey
1919
from python_graphql_client import GraphqlClient
2020

2121
from irrd.conf import PASSWORD_HASH_DUMMY_VALUE, config_init
@@ -41,7 +41,7 @@
4141
)
4242
from irrd.utils.whois_client import whois_query, whois_query_irrd
4343

44-
from ..utils.crypto import ed25519_private_key_as_str, ed25519_public_key_as_str
44+
from ..utils.crypto import eckey_private_key_as_str, eckey_public_key_as_str
4545
from .constants import (
4646
EMAIL_DISCARD_MSGS_COMMAND,
4747
EMAIL_END,
@@ -924,7 +924,7 @@ def _start_irrds(self):
924924
with open(self.config_path1, "w") as yaml_file:
925925
yaml.safe_dump(config1, yaml_file)
926926

927-
self.nrtm4_private_key = Ed25519PrivateKey.generate()
927+
self.nrtm4_private_key = ECKey.generate_key()
928928
config2 = base_config.copy()
929929
config2["irrd"]["piddir"] = self.piddir2
930930
config2["irrd"]["database_url"] = self.database_url2
@@ -944,7 +944,7 @@ def _start_irrds(self):
944944
"nrtm_host": "127.0.0.1",
945945
"nrtm_port": str(self.port_whois1),
946946
"nrtm_access_list": "localhost",
947-
"nrtm4_server_private_key": ed25519_private_key_as_str(self.nrtm4_private_key),
947+
"nrtm4_server_private_key": eckey_private_key_as_str(self.nrtm4_private_key),
948948
"nrtm4_server_local_path": self.nrtm4_dir2,
949949
"nrtm4_server_base_url": f"file://{self.nrtm4_dir2}",
950950
"nrtm4_server_snapshot_frequency": 3600,
@@ -964,7 +964,7 @@ def _start_irrds(self):
964964
config3["irrd"]["sources"]["TEST"] = {
965965
"keep_journal": True,
966966
"nrtm4_client_notification_file_url": f"file://{self.nrtm4_dir2}update-notification-file.json",
967-
"nrtm4_client_initial_public_key": ed25519_public_key_as_str(self.nrtm4_private_key.public_key()),
967+
"nrtm4_client_initial_public_key": eckey_public_key_as_str(self.nrtm4_private_key),
968968
}
969969
with open(self.config_path3, "w") as yaml_file:
970970
yaml.safe_dump(config3, yaml_file)

irrd/mirroring/nrtm4/nrtm4_client.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
from urllib.parse import urlparse
55

66
import pydantic
7-
from joserfc import jws
8-
from joserfc.errors import BadSignatureError, JoseError
97
from joserfc.rfc7515.model import CompactSignature
10-
from joserfc.rfc7518.ec_key import ECKey
118

129
from irrd.conf import get_setting
1310
from irrd.mirroring.nrtm4.jsonseq import jsonseq_decode
@@ -30,6 +27,7 @@
3027
NRTM4ClientDatabaseStatus,
3128
)
3229
from irrd.storage.queries import DatabaseStatusQuery
30+
from irrd.utils.crypto import eckey_from_config, eckey_from_str, jws_deserialize
3331
from irrd.utils.misc import format_pydantic_errors
3432

3533
logger = logging.getLogger(__name__)
@@ -155,24 +153,27 @@ def _deserialize_unf(self, unf_content: str) -> Tuple[bytes, str]:
155153
keys = [get_setting(f"sources.{self.source}.nrtm4_client_initial_public_key")]
156154

157155
for key in keys:
158-
if not key:
156+
if not key: # pragma: no cover
159157
continue
158+
pubkey = eckey_from_str(key)
160159
try:
161-
pubkey = ECKey.import_key(key)
162-
except JoseError as error:
163-
logger.error(f"{self.source}: Invalid public key, ignoring: {key}", exc_info=error)
164-
continue
165-
try:
166-
compact_signature = jws.deserialize_compact(unf_content_bytes, pubkey)
160+
compact_signature = jws_deserialize(unf_content_bytes, pubkey)
167161
return compact_signature.payload, key
168-
except BadSignatureError:
162+
except ValueError:
169163
continue
170164

171165
if self.last_status.current_key:
166+
compact_signature = None
167+
172168
try:
173-
compact_signature = jws.deserialize_compact(unf_content_bytes, ECKey.import_key(config_key))
174-
except JoseError:
175-
compact_signature = None
169+
ec_key = eckey_from_config(f"sources.{self.source}.nrtm4_client_initial_public_key")
170+
if ec_key:
171+
compact_signature = jws_deserialize(
172+
unf_content_bytes,
173+
ec_key,
174+
)
175+
except ValueError: # pragma: no cover
176+
pass
176177
if compact_signature:
177178
# While technically just a "signature not valid case", it is a rather
178179
# confusing situation for the user, so gets a special message.

irrd/mirroring/nrtm4/nrtm4_server.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import base64
21
import datetime
32
import gzip
4-
import hashlib
53
import logging
64
import os
75
import secrets
@@ -24,7 +22,7 @@
2422
RPSLDatabaseJournalStatisticsQuery,
2523
RPSLDatabaseQuery,
2624
)
27-
from irrd.utils.crypto import ed25519_private_key_from_config, ed25519_public_key_as_str
25+
from irrd.utils.crypto import eckey_from_config, eckey_public_key_as_str, jws_serialize
2826
from irrd.utils.text import remove_auth_hashes
2927

3028
from ...utils.process_support import get_lockfile
@@ -222,13 +220,11 @@ def _write_unf(self) -> None:
222220
This is based on settings and self.status.
223221
"""
224222
assert self.status
225-
next_signing_private_key = ed25519_private_key_from_config(
223+
next_signing_private_key = eckey_from_config(
226224
f"sources.{self.source}.nrtm4_server_private_key_next", permit_empty=True
227225
)
228226
next_signing_public_key = (
229-
ed25519_public_key_as_str(next_signing_private_key.public_key())
230-
if next_signing_private_key
231-
else None
227+
eckey_public_key_as_str(next_signing_private_key) if next_signing_private_key else None
232228
)
233229
unf = NRTM4UpdateNotificationFile(
234230
nrtm_version=4,
@@ -251,14 +247,11 @@ def _write_unf(self) -> None:
251247
],
252248
)
253249
unf_content = unf.model_dump_json(exclude_none=True, include=unf.model_fields_set).encode("ascii")
254-
private_key = ed25519_private_key_from_config(f"sources.{self.source}.nrtm4_server_private_key")
250+
private_key = eckey_from_config(f"sources.{self.source}.nrtm4_server_private_key")
255251
assert private_key
256-
signature = private_key.sign(unf_content)
257-
unf_hash = hashlib.sha256(unf_content).hexdigest()
258-
with open(self.path / f"update-notification-file-signature-{unf_hash}.sig", "wb") as sig_file:
259-
sig_file.write(base64.b64encode(signature))
260-
with open(self.path / "update-notification-file.json", "wb") as unf_file:
261-
unf_file.write(unf_content)
252+
unf_serialized = jws_serialize(unf_content, private_key)
253+
with open(self.path / "update-notification-file.json", "w") as unf_file:
254+
unf_file.write(unf_serialized)
262255
self.status.last_update_notification_file_update = unf.timestamp
263256

264257
def _expire_deltas(self) -> None:

irrd/mirroring/nrtm4/nrtm4_types.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from uuid import UUID
55

66
import pydantic
7-
from joserfc.errors import JoseError
87
from joserfc.rfc7518.ec_key import ECKey
98
from pytz import UTC
109
from typing_extensions import Self
@@ -145,7 +144,7 @@ def validate_next_signing_key(cls, next_signing_key: Optional[str]):
145144
if next_signing_key:
146145
try:
147146
ECKey.import_key(next_signing_key)
148-
except JoseError as ve:
147+
except ValueError as ve:
149148
raise ValueError(
150149
f"Update Notification File has invalid next_signing_key {next_signing_key}: {ve}"
151150
)
Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1-
from joserfc.rfc7518.ec_key import ECKey
1+
from irrd.utils.crypto import eckey_from_str, eckey_public_key_as_str
22

3-
MOCK_UNF_PRIVATE_KEY = ECKey.import_key(
3+
MOCK_UNF_PRIVATE_KEY_STR = (
44
"-----BEGIN PRIVATE"
55
" KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGQrdALKHTVC4sVav\nmKUjXaPB22CWZP3t5XSkLqKHMO2hRANCAAQ9U/aaZwLV4koey4Jvu9cRaxiXna9k\naQ3YwrPzZlwd5MQSZ59kfT2+LAbQmXbZg0NGzptqHoOK0YD3YVBjv4kc\n-----END"
66
" PRIVATE KEY-----\n"
77
)
88

9-
MOCK_UNF_PUBLIC_KEY = MOCK_UNF_PRIVATE_KEY.as_pem(private=False).decode("ascii")
9+
MOCK_UNF_PRIVATE_KEY = eckey_from_str(MOCK_UNF_PRIVATE_KEY_STR)
1010

11-
MOCK_UNF_PRIVATE_KEY_OTHER = ECKey.import_key(
11+
MOCK_UNF_PUBLIC_KEY = eckey_public_key_as_str(MOCK_UNF_PRIVATE_KEY)
12+
13+
MOCK_UNF_PRIVATE_KEY_OTHER_STR = (
1214
"-----BEGIN PRIVATE"
1315
" KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmHtbXrQ0uEcrzeZK\niaK8UnpD5c/YAmqdUHqoHLz997ShRANCAAQ18hSL1o3ynp1kLsfXgZBtlWSYwKvc\nLT2qRj7QeJPxHA6X3XMk7eD6xbdeyNFnLXiKwNPFMPcwRLC6oLN81Fvb\n-----END"
1416
" PRIVATE KEY-----\n"
1517
)
1618

17-
MOCK_UNF_PUBLIC_KEY_OTHER = MOCK_UNF_PRIVATE_KEY_OTHER.as_pem(private=False).decode("ascii")
19+
20+
MOCK_UNF_PRIVATE_KEY_OTHER = eckey_from_str(MOCK_UNF_PRIVATE_KEY_OTHER_STR)
21+
22+
MOCK_UNF_PUBLIC_KEY_OTHER = eckey_public_key_as_str(MOCK_UNF_PRIVATE_KEY_OTHER)

irrd/mirroring/nrtm4/tests/test_nrtm4_server.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import base64
21
import dataclasses
32
import gzip
4-
import hashlib
53
import json
64
import os
75
import time
@@ -11,7 +9,7 @@
119
from irrd.conf import NRTM4_SERVER_DELTA_EXPIRY_TIME, PASSWORD_HASH_DUMMY_VALUE
1210
from irrd.mirroring.nrtm4.jsonseq import jsonseq_decode
1311
from irrd.mirroring.nrtm4.nrtm4_server import NRTM4Server, NRTM4ServerWriter
14-
from irrd.mirroring.nrtm4.tests import MOCK_UNF_PRIVATE_KEY, MOCK_UNF_PUBLIC_KEY
12+
from irrd.mirroring.nrtm4.tests import MOCK_UNF_PRIVATE_KEY, MOCK_UNF_PRIVATE_KEY_STR
1513
from irrd.mirroring.retrieval import check_file_hash_sha256
1614
from irrd.storage.models import DatabaseOperation, NRTM4ServerDatabaseStatus
1715
from irrd.storage.queries import (
@@ -20,7 +18,7 @@
2018
RPSLDatabaseJournalStatisticsQuery,
2119
RPSLDatabaseQuery,
2220
)
23-
from irrd.utils.crypto import ed25519_private_key_as_str, ed25519_public_key_from_str
21+
from irrd.utils.crypto import jws_deserialize
2422
from irrd.utils.rpsl_samples import SAMPLE_MNTNER
2523
from irrd.utils.test_utils import MockDatabaseHandler
2624
from irrd.utils.text import remove_auth_hashes
@@ -64,7 +62,7 @@ def test_nrtm4_server(self, tmpdir, config_override):
6462
"piddir": pid_path,
6563
"sources": {
6664
"TEST": {
67-
"nrtm4_server_private_key": ed25519_private_key_as_str(MOCK_UNF_PRIVATE_KEY),
65+
"nrtm4_server_private_key": MOCK_UNF_PRIVATE_KEY_STR,
6866
"nrtm4_server_local_path": str(nrtm_path),
6967
"nrtm4_server_base_url": BASE_URL,
7068
# "nrtm4_server_snapshot_frequency": 0,
@@ -253,16 +251,11 @@ def test_nrtm4_server(self, tmpdir, config_override):
253251
def _load_unf(self, nrtm_path):
254252
with open(nrtm_path / "update-notification-file.json", "rb") as f:
255253
unf_content = f.read()
256-
unf = json.loads(unf_content)
254+
unf_payload = jws_deserialize(unf_content, MOCK_UNF_PRIVATE_KEY)
255+
unf = json.loads(unf_payload.payload)
257256
assert unf["nrtm_version"] == 4
258257
assert unf["source"] == "TEST"
259258
assert unf["type"] == "notification"
260-
261-
unf_hash = hashlib.sha256(unf_content).hexdigest()
262-
with open(nrtm_path / f"update-notification-file-signature-{unf_hash}.sig", "r") as sig_file:
263-
sig_content = base64.b64decode(sig_file.read())
264-
public_key = ed25519_public_key_from_str(MOCK_UNF_PUBLIC_KEY)
265-
public_key.verify(sig_content, unf_content)
266259
return unf
267260

268261
def _status_to_dict(self, status: NRTM4ServerDatabaseStatus, force_reload=False):

0 commit comments

Comments
 (0)