distributed-state-network 0.0.4__tar.gz → 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/PKG-INFO +1 -1
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/documentation/ds-node-config.md +6 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/pyproject.toml +1 -1
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/dsnode.py +9 -5
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/handler.py +9 -8
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/objects/config.py +10 -1
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/objects/hello_packet.py +4 -3
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/util/ecdsa.py +1 -1
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/util/https.py +5 -8
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/util/key_manager.py +2 -2
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/tests/connections.py +10 -2
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/.github/workflows/publish.yml +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/.gitignore +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/LICENSE +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/README.md +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/coverage.sh +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/documentation/ds-node-server.md +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/documentation/ds-node.md +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/documentation/protocol.md +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/documentation/usage.md +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/__init__.py +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/create_key.py +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/objects/endpoint.py +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/objects/peers_packet.py +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/objects/signed_packet.py +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/objects/state_packet.py +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/util/__init__.py +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/util/aes.py +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/src/distributed_state_network/util/byte_helper.py +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/technical.md +0 -0
- {distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: distributed-state-network
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: A tool to distribute the state of a network device to other devices on the network
|
|
5
5
|
Project-URL: Homepage, https://github.com/erinclemmer/distributed_state_network
|
|
6
6
|
Project-URL: Issues, https://github.com/erinclemmer/distributed_state_network/issues
|
{distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/documentation/ds-node-config.md
RENAMED
|
@@ -12,6 +12,8 @@ from distributed_state_network import DSNodeConfig
|
|
|
12
12
|
class DSNodeConfig:
|
|
13
13
|
node_id: str
|
|
14
14
|
port: int
|
|
15
|
+
https: bool
|
|
16
|
+
network_ip: str
|
|
15
17
|
aes_key_file: str
|
|
16
18
|
bootstrap_nodes: List[Endpoint]
|
|
17
19
|
```
|
|
@@ -20,6 +22,8 @@ class DSNodeConfig:
|
|
|
20
22
|
- **node_id** (`str`): Unique identifier for the node
|
|
21
23
|
- **port** (`int`): Port number for the node to listen on
|
|
22
24
|
- **aes_key_file** (`str`): Path to the AES key file for encryption/decryption
|
|
25
|
+
- **https** (`bool`): whether to use HTTPS network requests (true) or http (false)
|
|
26
|
+
- **network_ip** (`str`): Must match network address of the current computer (only if https is on)
|
|
23
27
|
- **bootstrap_nodes** (`List[Endpoint]`): List of initial nodes to connect to when joining the network
|
|
24
28
|
|
|
25
29
|
### Methods
|
|
@@ -39,6 +43,8 @@ config_dict = {
|
|
|
39
43
|
"node_id": "node1",
|
|
40
44
|
"port": 8000,
|
|
41
45
|
"aes_key_file": "/path/to/key.aes",
|
|
46
|
+
"https": false,
|
|
47
|
+
"network_ip": "192.168.0.1",
|
|
42
48
|
"bootstrap_nodes": [
|
|
43
49
|
{"address": "127.0.0.1", "port": 8001}
|
|
44
50
|
]
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "distributed-state-network"
|
|
7
|
-
version = "0.0
|
|
7
|
+
version = "0.1.0"
|
|
8
8
|
authors = [{name="Erin Clemmer", email="erin.c.clemmer@gmail.com"}]
|
|
9
9
|
description = "A tool to distribute the state of a network device to other devices on the network"
|
|
10
10
|
keywords = ["distributed", "networking"]
|
|
@@ -38,7 +38,8 @@ class DSNode:
|
|
|
38
38
|
self.cert_manager = CertManager(config.node_id)
|
|
39
39
|
self.cred_manager = CredentialManager(config.node_id)
|
|
40
40
|
|
|
41
|
-
self.
|
|
41
|
+
if self.config.https:
|
|
42
|
+
self.cert_manager.generate_keys(self.config.network_ip)
|
|
42
43
|
self.cred_manager.generate_keys()
|
|
43
44
|
|
|
44
45
|
self.node_states = {
|
|
@@ -94,8 +95,10 @@ class DSNode:
|
|
|
94
95
|
def send_request(self, con: Endpoint, path: str, payload: bytes, verify, retries: int = 0) -> Tuple[requests.Response, bytes]:
|
|
95
96
|
try:
|
|
96
97
|
# Always send a ping first to throw an error if https validation does not work
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
protocol = "https" if self.config.https else "http"
|
|
99
|
+
verify = verify if self.config.https else None
|
|
100
|
+
requests.post(f'{protocol}://{con.to_string()}/ping', data=self.encrypt_data(payload), verify=verify, timeout=2)
|
|
101
|
+
res = requests.post(f'{protocol}://{con.to_string()}/{path}', data=self.encrypt_data(payload), verify=verify, timeout=2)
|
|
99
102
|
except Exception as e:
|
|
100
103
|
self.logger.error(e)
|
|
101
104
|
time.sleep(1)
|
|
@@ -178,7 +181,8 @@ class DSNode:
|
|
|
178
181
|
self.logger.error(msg)
|
|
179
182
|
raise Exception(505) # Version not supported
|
|
180
183
|
|
|
181
|
-
self.
|
|
184
|
+
if self.config.https:
|
|
185
|
+
self.cert_manager.ensure_public(pkt.node_id, pkt.https_certificate)
|
|
182
186
|
self.cred_manager.ensure_public(pkt.node_id, pkt.ecdsa_public_key)
|
|
183
187
|
|
|
184
188
|
if pkt.node_id not in self.address_book:
|
|
@@ -196,7 +200,7 @@ class DSNode:
|
|
|
196
200
|
self.my_con(),
|
|
197
201
|
self.cred_manager.my_public(),
|
|
198
202
|
None,
|
|
199
|
-
self.cert_manager.my_public()
|
|
203
|
+
self.cert_manager.my_public() if self.config.https else None
|
|
200
204
|
)
|
|
201
205
|
pkt.sign(self.cred_manager.my_private())
|
|
202
206
|
return pkt
|
|
@@ -73,7 +73,7 @@ class DSNodeServer(HTTPServer):
|
|
|
73
73
|
disconnect_callback: Optional[Callable] = None,
|
|
74
74
|
update_callback: Optional[Callable] = None
|
|
75
75
|
):
|
|
76
|
-
super().__init__(("
|
|
76
|
+
super().__init__(("0.0.0.0", config.port), DSNodeHandler)
|
|
77
77
|
self.config = config
|
|
78
78
|
self.node = DSNode(config, VERSION, disconnect_callback, update_callback)
|
|
79
79
|
self.node.logger.info(f'Started DSNode on port {config.port}')
|
|
@@ -93,13 +93,14 @@ class DSNodeServer(HTTPServer):
|
|
|
93
93
|
@staticmethod
|
|
94
94
|
def start(config: DSNodeConfig, disconnect_callback: Optional[Callable] = None, update_callback: Optional[Callable] = None) -> 'NodeServer':
|
|
95
95
|
n = DSNodeServer(config, disconnect_callback, update_callback)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
if config.https:
|
|
97
|
+
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
98
|
+
public_path = n.node.cert_manager.public_path(n.config.node_id)
|
|
99
|
+
ssl_context.load_cert_chain(
|
|
100
|
+
certfile=public_path,
|
|
101
|
+
keyfile=public_path.replace(".crt", ".key")
|
|
102
|
+
)
|
|
103
|
+
n.socket = ssl_context.wrap_socket(n.socket, server_side=True)
|
|
103
104
|
n.thread = threading.Thread(target=serve, args=(n, ))
|
|
104
105
|
n.thread.start()
|
|
105
106
|
|
|
@@ -7,9 +7,18 @@ from distributed_state_network.objects.endpoint import Endpoint
|
|
|
7
7
|
class DSNodeConfig:
|
|
8
8
|
node_id: str
|
|
9
9
|
port: int
|
|
10
|
+
https: bool
|
|
11
|
+
network_ip: str
|
|
10
12
|
aes_key_file: str
|
|
11
13
|
bootstrap_nodes: List[Endpoint]
|
|
12
14
|
|
|
13
15
|
@staticmethod
|
|
14
16
|
def from_dict(data: Dict) -> 'DSNodeConfig':
|
|
15
|
-
return DSNodeConfig(
|
|
17
|
+
return DSNodeConfig(
|
|
18
|
+
data["node_id"],
|
|
19
|
+
data["port"],
|
|
20
|
+
data["https"],
|
|
21
|
+
data["network_ip"],
|
|
22
|
+
data["aes_key_file"],
|
|
23
|
+
[Endpoint.from_json(e) for e in data["bootstrap_nodes"]]
|
|
24
|
+
)
|
|
@@ -37,9 +37,10 @@ class HelloPacket(SignedPacket):
|
|
|
37
37
|
bts.write_string(self.node_id)
|
|
38
38
|
bts.write_bytes(self.connection.to_bytes())
|
|
39
39
|
bts.write_bytes(self.ecdsa_public_key)
|
|
40
|
-
bts.write_bytes(self.https_certificate)
|
|
41
40
|
if include_signature:
|
|
42
41
|
bts.write_bytes(self.ecdsa_signature)
|
|
42
|
+
if self.https_certificate is not None:
|
|
43
|
+
bts.write_bytes(self.https_certificate)
|
|
43
44
|
|
|
44
45
|
return bts.get_bytes()
|
|
45
46
|
|
|
@@ -50,10 +51,10 @@ class HelloPacket(SignedPacket):
|
|
|
50
51
|
node_id = bts.read_string()
|
|
51
52
|
connection = Endpoint.from_bytes(bts.read_bytes())
|
|
52
53
|
ecdsa_public_key = bts.read_bytes()
|
|
53
|
-
https_certificate = bts.read_bytes()
|
|
54
54
|
ecdsa_signature = bts.read_bytes()
|
|
55
|
+
https_certificate = bts.read_bytes() or None
|
|
55
56
|
|
|
56
|
-
if version == '' or node_id == '' or ecdsa_public_key == b''
|
|
57
|
+
if version == '' or node_id == '' or ecdsa_public_key == b'':
|
|
57
58
|
raise Exception(406) # Not acceptable
|
|
58
59
|
|
|
59
60
|
return HelloPacket(version, node_id, connection, ecdsa_public_key, ecdsa_signature, https_certificate)
|
|
@@ -2,7 +2,7 @@ import hashlib
|
|
|
2
2
|
|
|
3
3
|
from ecdsa import SigningKey, VerifyingKey, SECP256k1
|
|
4
4
|
|
|
5
|
-
def generate_key_pair():
|
|
5
|
+
def generate_key_pair(_):
|
|
6
6
|
private_key = SigningKey.generate(curve=SECP256k1)
|
|
7
7
|
public_key = private_key.get_verifying_key()
|
|
8
8
|
return public_key.to_string(), private_key.to_string()
|
|
@@ -9,7 +9,7 @@ from cryptography.hazmat.backends import default_backend
|
|
|
9
9
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
10
10
|
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
|
|
11
11
|
|
|
12
|
-
def generate_cert():
|
|
12
|
+
def generate_cert(network_ip: str):
|
|
13
13
|
# Generate private key
|
|
14
14
|
private_key = rsa.generate_private_key(
|
|
15
15
|
public_exponent=65537,
|
|
@@ -19,11 +19,8 @@ def generate_cert():
|
|
|
19
19
|
|
|
20
20
|
# Generate a self-signed certificate
|
|
21
21
|
subject = issuer = x509.Name([
|
|
22
|
-
x509.NameAttribute(NameOID.
|
|
23
|
-
x509.NameAttribute(NameOID.
|
|
24
|
-
x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
|
|
25
|
-
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Organization"),
|
|
26
|
-
x509.NameAttribute(NameOID.COMMON_NAME, "localhost"),
|
|
22
|
+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "DSN"),
|
|
23
|
+
x509.NameAttribute(NameOID.COMMON_NAME, "DSN"),
|
|
27
24
|
])
|
|
28
25
|
|
|
29
26
|
certificate = (
|
|
@@ -36,8 +33,8 @@ def generate_cert():
|
|
|
36
33
|
.not_valid_after(datetime.utcnow() + timedelta(days=365))
|
|
37
34
|
.add_extension(
|
|
38
35
|
x509.SubjectAlternativeName([
|
|
39
|
-
x509.DNSName(
|
|
40
|
-
IPAddress(ipaddress.IPv4Address(
|
|
36
|
+
x509.DNSName(network_ip),
|
|
37
|
+
IPAddress(ipaddress.IPv4Address(network_ip))
|
|
41
38
|
]),
|
|
42
39
|
critical=False
|
|
43
40
|
)
|
|
@@ -63,11 +63,11 @@ class KeyManager:
|
|
|
63
63
|
with open(f'{self.folder}/{self.node_id}/{self.node_id}.{self.private_extension}', 'rb') as f:
|
|
64
64
|
return f.read()
|
|
65
65
|
|
|
66
|
-
def generate_keys(self):
|
|
66
|
+
def generate_keys(self, network_ip: str = None):
|
|
67
67
|
if os.path.exists(f'{self.folder}/{self.node_id}/{self.node_id}.{self.private_extension}'):
|
|
68
68
|
return
|
|
69
69
|
logging.getLogger("DSN: " + self.node_id).info(f"Generating {self.key_type} Keys ...")
|
|
70
|
-
cert_bytes, key_bytes = self.gen_keys()
|
|
70
|
+
cert_bytes, key_bytes = self.gen_keys(network_ip)
|
|
71
71
|
if not os.path.exists(self.folder):
|
|
72
72
|
os.mkdir(self.folder)
|
|
73
73
|
if not os.path.exists(f'{self.folder}/{self.node_id}'):
|
|
@@ -17,12 +17,14 @@ cb_test = 0
|
|
|
17
17
|
|
|
18
18
|
current_port = 5000
|
|
19
19
|
|
|
20
|
-
def start_node(node_id: str, bootstrap_port: int = None, disconnect_cb: Optional[Callable] = None, update_cb: Optional[Callable] = None):
|
|
20
|
+
def start_node(node_id: str, bootstrap_port: int = None, disconnect_cb: Optional[Callable] = None, update_cb: Optional[Callable] = None, https: bool = False):
|
|
21
21
|
global current_port
|
|
22
22
|
args = {
|
|
23
23
|
"node_id": node_id,
|
|
24
24
|
"port": current_port,
|
|
25
|
-
"aes_key_file": KEY_FILE
|
|
25
|
+
"aes_key_file": KEY_FILE,
|
|
26
|
+
"https": https,
|
|
27
|
+
"network_ip": "127.0.0.1"
|
|
26
28
|
}
|
|
27
29
|
current_port += 1
|
|
28
30
|
if bootstrap_port is not None:
|
|
@@ -46,6 +48,12 @@ class ConnectionsTest(unittest.TestCase):
|
|
|
46
48
|
self.assertEqual(["node-1", "node-2"], sorted(node1.node.peers()))
|
|
47
49
|
self.assertEqual(["node-1", "node-2"], sorted(node2.node.peers()))
|
|
48
50
|
|
|
51
|
+
def test_https(self):
|
|
52
|
+
node1 = start_node("node-1", https=True)
|
|
53
|
+
node2 = start_node("node-2", node1.config.port, https=True)
|
|
54
|
+
self.assertEqual(["node-1", "node-2"], sorted(node1.node.peers()))
|
|
55
|
+
self.assertEqual(["node-1", "node-2"], sorted(node2.node.peers()))
|
|
56
|
+
|
|
49
57
|
def test_many(self):
|
|
50
58
|
bootstrap_node = start_node("bootstrap")
|
|
51
59
|
nodes = []
|
{distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/.github/workflows/publish.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/documentation/ds-node-server.md
RENAMED
|
File without changes
|
{distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/documentation/ds-node.md
RENAMED
|
File without changes
|
{distributed_state_network-0.0.4 → distributed_state_network-0.1.0}/documentation/protocol.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|