GNServer 0.0.0.0.60__tar.gz → 0.0.0.0.62__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.
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_app.py +69 -29
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_client.py +19 -13
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/models.py +38 -30
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer.egg-info/PKG-INFO +1 -1
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/PKG-INFO +1 -1
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/setup.py +1 -1
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/__init__.py +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_cors_resolver.py +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_crt.py +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_func_params_validation.py +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_routes.py +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_template_resolver.py +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer.egg-info/SOURCES.txt +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer.egg-info/dependency_links.txt +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer.egg-info/requires.txt +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer.egg-info/top_level.txt +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/LICENSE +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/mmbConfig.json +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/LICENSE +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/MANIFEST.in +0 -0
- {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/setup.cfg +0 -0
@@ -18,14 +18,15 @@ from typing import Any, AsyncGenerator, Union
|
|
18
18
|
from gnobjects.net.objects import GNRequest, GNResponse, FileObject, CORSObject, TemplateObject
|
19
19
|
from gnobjects.net.fastcommands import AllGNFastCommands, GNFastCommand
|
20
20
|
|
21
|
+
from KeyisBTools.cryptography.bytes import userFriendly
|
21
22
|
|
22
23
|
from ._func_params_validation import register_schema_by_key, validate_params_by_key
|
23
24
|
from ._cors_resolver import resolve_cors
|
24
|
-
|
25
25
|
from ._routes import Route, _compile_path, _ensure_async, _convert_value
|
26
|
-
|
27
26
|
from .models import KDCObject
|
27
|
+
from ._client import AsyncClient
|
28
28
|
|
29
|
+
from pathlib import Path
|
29
30
|
|
30
31
|
try:
|
31
32
|
if not sys.platform.startswith("win"):
|
@@ -122,6 +123,8 @@ class App:
|
|
122
123
|
|
123
124
|
self._kdc: Optional[KDCObject] = None
|
124
125
|
|
126
|
+
self.client = AsyncClient()
|
127
|
+
|
125
128
|
def setKDC(self, kdc: KDCObject):
|
126
129
|
self._kdc = kdc
|
127
130
|
|
@@ -341,30 +344,7 @@ class App:
|
|
341
344
|
|
342
345
|
if not stream: # если не стрим, то ждем конец quic стрима и запускаем обработку ответа
|
343
346
|
if event.end_stream:
|
344
|
-
|
345
|
-
if self._api._kdc is not None:
|
346
|
-
buf, domain = self._api._kdc.decode(bytes(buf))
|
347
|
-
else:
|
348
|
-
domain = None
|
349
|
-
|
350
|
-
if buf is not None:
|
351
|
-
try:
|
352
|
-
request = GNRequest.deserialize(buf, mode)
|
353
|
-
if domain is not None:
|
354
|
-
request.client._data['domain'] = domain
|
355
|
-
except:
|
356
|
-
asyncio.create_task(self.sendRawResponse(stream_id, AllGNFastCommands.KDCDecryptRequestFailed().serialize(mode=mode)))
|
357
|
-
self._buffer.pop(event.stream_id, None)
|
358
|
-
return
|
359
|
-
|
360
|
-
else:
|
361
|
-
raise Exception('Не удалось расшифровать от KDC')
|
362
|
-
|
363
|
-
request.stream_id = stream_id # type: ignore
|
364
|
-
logger.debug(f'[<] Request: {request.method} {request.url}')
|
365
|
-
asyncio.create_task(self._handle_request(request, mode))
|
366
|
-
|
367
|
-
self._buffer.pop(event.stream_id, None)
|
347
|
+
asyncio.create_task(self._resolve_raw_request(stream_id, buf, mode))
|
368
348
|
return
|
369
349
|
|
370
350
|
# если стрим, то смотрим сколько пришло данных
|
@@ -380,6 +360,9 @@ class App:
|
|
380
360
|
del buf[:lenght]
|
381
361
|
|
382
362
|
# формируем запрос
|
363
|
+
|
364
|
+
asyncio.create_task(self.sendRawResponse(stream_id, AllGNFastCommands.NotImplemented().serialize(mode=mode)))
|
365
|
+
return
|
383
366
|
|
384
367
|
if self._api._kdc is not None:
|
385
368
|
data, domain = self._api._kdc.decode(data)
|
@@ -422,9 +405,37 @@ class App:
|
|
422
405
|
request._stream = w # type: ignore
|
423
406
|
asyncio.create_task(self._handle_request(request, mode))
|
424
407
|
|
425
|
-
async def
|
408
|
+
async def _resolve_raw_request(self, stream_id: int, data: bytes, mode: int):
|
409
|
+
|
410
|
+
if self._api._kdc is not None:
|
411
|
+
data, domain = await self._api._kdc.decode(bytes(data))
|
412
|
+
else:
|
413
|
+
domain = None
|
414
|
+
|
415
|
+
if data is None:
|
416
|
+
self._buffer.pop(stream_id, None)
|
417
|
+
raise Exception('Не удалось расшифровать от KDC')
|
418
|
+
|
419
|
+
try:
|
420
|
+
request = GNRequest.deserialize(data, mode)
|
421
|
+
if domain is not None:
|
422
|
+
request.client._data['domain'] = domain
|
423
|
+
except:
|
424
|
+
self._buffer.pop(stream_id, None)
|
425
|
+
await self.sendRawResponse(stream_id, AllGNFastCommands.KDCDecryptRequestFailed().serialize(mode=mode))
|
426
|
+
return
|
426
427
|
|
428
|
+
logger.debug(f'[<] Request: {request.method} {request.url}')
|
429
|
+
|
427
430
|
request.client._data['remote_addr'] = self._quic._network_paths[0].addr
|
431
|
+
request.stream_id = stream_id # type: ignore
|
432
|
+
|
433
|
+
self._buffer.pop(stream_id, None)
|
434
|
+
await self._handle_request(request, mode)
|
435
|
+
|
436
|
+
|
437
|
+
async def _handle_request(self, request: GNRequest, mode: int):
|
438
|
+
|
428
439
|
|
429
440
|
try:
|
430
441
|
response = await self._api.dispatchRequest(request)
|
@@ -465,7 +476,7 @@ class App:
|
|
465
476
|
|
466
477
|
|
467
478
|
if self._api._kdc is not None:
|
468
|
-
blob = self._api._kdc.encode(request.client.domain, blob)
|
479
|
+
blob = await self._api._kdc.encode(request.client.domain, blob)
|
469
480
|
|
470
481
|
await self.sendRawResponse(request.stream_id, blob=blob, end_stream=end_stream)
|
471
482
|
|
@@ -479,21 +490,50 @@ class App:
|
|
479
490
|
port: int,
|
480
491
|
cert_path: str,
|
481
492
|
key_path: str,
|
493
|
+
server_key: Optional[Union[str, Path]] = None,
|
482
494
|
*,
|
483
495
|
host: str = '0.0.0.0',
|
484
496
|
idle_timeout: float = 20.0,
|
485
497
|
wait: bool = True,
|
486
|
-
run: Optional[Callable] = None
|
498
|
+
run: Optional[Callable] = None,
|
499
|
+
kdc_passive_key_sync_domains: List[str] = [],
|
500
|
+
kdc_active_key_synchronization: bool = True
|
487
501
|
):
|
488
502
|
"""
|
489
503
|
# Запустить сервер
|
490
504
|
|
491
505
|
Запускает сервер в главном процессе asyncio.run()
|
506
|
+
|
507
|
+
server_key: `:kdc.core:<...>:<...>:`
|
492
508
|
"""
|
493
509
|
|
494
510
|
self.domain = domain
|
495
511
|
|
496
512
|
|
513
|
+
if server_key is not None:
|
514
|
+
if isinstance(server_key, Path):
|
515
|
+
server_key = server_key.read_text('utf-8')
|
516
|
+
|
517
|
+
if server_key[0] == ':' and server_key[0] == ':':
|
518
|
+
kdc_domain, dns_key, kdc_key = server_key.split(':')
|
519
|
+
|
520
|
+
dns_key = userFriendly.decode(dns_key)
|
521
|
+
kdc_key = userFriendly.decode(kdc_key)
|
522
|
+
|
523
|
+
kdc = KDCObject(self.domain, kdc_domain, kdc_key, kdc_passive_key_sync_domains, active_key_synchronization=kdc_active_key_synchronization)
|
524
|
+
self.setKDC(kdc)
|
525
|
+
|
526
|
+
if self.client._domain is None:
|
527
|
+
self.client._domain = domain
|
528
|
+
if self.client._dns_key is None:
|
529
|
+
self.client.setDNSkey(dns_key)
|
530
|
+
|
531
|
+
|
532
|
+
|
533
|
+
|
534
|
+
|
535
|
+
|
536
|
+
|
497
537
|
self._init_sys_routes()
|
498
538
|
|
499
539
|
cfg = QuicConfiguration(
|
@@ -161,20 +161,26 @@ class AsyncClient:
|
|
161
161
|
self.__dns_client: Optional[AsyncClient] = None
|
162
162
|
self._dns_cache: TTLDict = TTLDict()
|
163
163
|
|
164
|
-
self.
|
164
|
+
self._domain = domain
|
165
165
|
|
166
166
|
self._kdc: Optional[KDCObject] = None
|
167
167
|
|
168
|
+
|
168
169
|
if server_key:
|
169
|
-
|
170
|
-
|
170
|
+
self.setDNSkey(server_key)
|
171
|
+
|
172
|
+
|
173
|
+
def setDNSkey(self, key: Union[bytes, str]):
|
174
|
+
if key:
|
175
|
+
if isinstance(key, bytes):
|
176
|
+
self._dns_key = key
|
171
177
|
else:
|
172
|
-
if os.path.exists(
|
173
|
-
self.
|
178
|
+
if os.path.exists(key):
|
179
|
+
self._dns_key = open(key, 'rb').read()
|
174
180
|
else:
|
175
|
-
self.
|
181
|
+
self._dns_key = userFriendly.decode(key)
|
176
182
|
else:
|
177
|
-
self.
|
183
|
+
self._dns_key = None
|
178
184
|
|
179
185
|
def setKDC(self, kdc: KDCObject):
|
180
186
|
self._kdc = kdc
|
@@ -348,10 +354,10 @@ class AsyncClient:
|
|
348
354
|
if self.__dns_client is None:
|
349
355
|
self.__dns_client = AsyncClient()
|
350
356
|
|
351
|
-
if self.
|
352
|
-
s = s2.sign(self.
|
353
|
-
data = m1.encrypt(s, domain.encode(), serialize({'domain': domain}), hash(self.
|
354
|
-
payload = {'sign': {'alg': 'KeyisB-c-s-m1', 'data': s, 'domain': self.
|
357
|
+
if self._dns_key is not None:
|
358
|
+
s = s2.sign(self._dns_key)
|
359
|
+
data = m1.encrypt(s, domain.encode(), serialize({'domain': domain}), hash(self._dns_key))
|
360
|
+
payload = {'sign': {'alg': 'KeyisB-c-s-m1', 'data': s, 'domain': self._domain}, 'data': data}
|
355
361
|
else:
|
356
362
|
payload = None
|
357
363
|
|
@@ -617,7 +623,7 @@ class RawQuicClient(QuicConnectionProtocol):
|
|
617
623
|
blob = request.serialize(_sys_s_mode)
|
618
624
|
|
619
625
|
if self.quicClient._client._kdc is not None:
|
620
|
-
blob = self.quicClient._client._kdc.encode(request.url.hostname, blob)
|
626
|
+
blob = await self.quicClient._client._kdc.encode(request.url.hostname, blob)
|
621
627
|
|
622
628
|
sid = self._quic.get_next_available_stream_id()
|
623
629
|
self._enqueue(sid, blob, True, False)
|
@@ -629,7 +635,7 @@ class RawQuicClient(QuicConnectionProtocol):
|
|
629
635
|
data = await fut
|
630
636
|
|
631
637
|
if self.quicClient._client._kdc is not None:
|
632
|
-
data, domain = self.quicClient._client._kdc.decode(data)
|
638
|
+
data, domain = await self.quicClient._client._kdc.decode(data)
|
633
639
|
|
634
640
|
if data is not None:
|
635
641
|
r = GNResponse.deserialize(data, _sys_s_mode)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
from typing import List, Optional, Dict
|
2
|
+
from typing import List, Optional, Dict, Union
|
3
3
|
from KeyisBTools.cryptography.sign import s2
|
4
4
|
from KeyisBTools.cryptography import m1
|
5
5
|
|
@@ -9,12 +9,13 @@ from gnobjects.net.objects import Url
|
|
9
9
|
from KeyisBTools.cryptography.bytes import hash3
|
10
10
|
|
11
11
|
class KDCObject:
|
12
|
-
def __init__(self, domain: str, kdc_domain: str, kdc_key: bytes, requested_domains: List[str]):
|
12
|
+
def __init__(self, domain: str, kdc_domain: str, kdc_key: bytes, requested_domains: List[str], active_key_synchronization: bool = True):
|
13
13
|
self._domain = domain
|
14
14
|
self._domain_hash = hash3(domain.encode())
|
15
15
|
self._kdc_domain = kdc_domain
|
16
16
|
self._kdc_key = kdc_key
|
17
17
|
self._requested_domains = requested_domains
|
18
|
+
self._active_key_synchronization = active_key_synchronization
|
18
19
|
|
19
20
|
from ._client import AsyncClient
|
20
21
|
self._client = AsyncClient(domain)
|
@@ -28,13 +29,6 @@ class KDCObject:
|
|
28
29
|
|
29
30
|
async def init(self, servers_keys: Optional[Dict[str, bytes]] = None): # type: ignore
|
30
31
|
|
31
|
-
if self._kdc_domain not in self._servers_keys:
|
32
|
-
self._servers_keys[self._kdc_domain] = self._kdc_key
|
33
|
-
h = hash3(self._kdc_domain.encode())
|
34
|
-
self._servers_keys_hash_domain[h] = self._kdc_domain
|
35
|
-
self._servers_keys_domain_hash[self._kdc_domain] = h
|
36
|
-
|
37
|
-
|
38
32
|
if servers_keys is not None:
|
39
33
|
for i in self._requested_domains:
|
40
34
|
if i in servers_keys:
|
@@ -43,39 +37,49 @@ class KDCObject:
|
|
43
37
|
servers_keys = {}
|
44
38
|
|
45
39
|
if len(self._requested_domains) > 0:
|
46
|
-
|
47
|
-
r = await self._client.request(GNRequest('GET', Url(f'gn://{self._kdc_domain}/api/sys/server/keys'), payload=payload))
|
48
|
-
|
49
|
-
if not r.command.ok:
|
50
|
-
print(f'ERROR: {r.command} {r.payload}')
|
51
|
-
raise r
|
52
|
-
|
53
|
-
if servers_keys is None:
|
54
|
-
print(f'ERROR: {r.command} {r.payload}')
|
55
|
-
raise r
|
56
|
-
|
57
|
-
servers_keys.update(r.payload)
|
58
|
-
|
59
|
-
|
60
|
-
self._servers_keys.update(servers_keys)
|
40
|
+
await self.requestKDC(self._requested_domains) # type: ignore
|
61
41
|
|
42
|
+
|
43
|
+
def _update(self):
|
62
44
|
for domain in self._servers_keys.keys():
|
63
45
|
h = hash3(domain.encode())
|
64
46
|
self._servers_keys_hash_domain[h] = domain
|
65
47
|
self._servers_keys_domain_hash[domain] = h
|
66
48
|
|
49
|
+
async def requestKDC(self, domain_or_hash: Union[str, bytes, List[Union[str, bytes]]]):
|
50
|
+
if self._kdc_domain not in self._servers_keys:
|
51
|
+
self._servers_keys[self._kdc_domain] = self._kdc_key
|
52
|
+
h = hash3(self._kdc_domain.encode())
|
53
|
+
self._servers_keys_hash_domain[h] = self._kdc_domain
|
54
|
+
self._servers_keys_domain_hash[self._kdc_domain] = h
|
55
|
+
|
67
56
|
|
57
|
+
if not isinstance(domain_or_hash, list):
|
58
|
+
domain_or_hash = [domain_or_hash]
|
59
|
+
|
60
|
+
r = await self._client.request(GNRequest('GET', Url(f'gn://{self._kdc_domain}/api/sys/server/keys'), payload=domain_or_hash))
|
68
61
|
|
62
|
+
|
63
|
+
if not r.command.ok:
|
64
|
+
print(f'ERROR: {r.command} {r.payload}')
|
65
|
+
raise r
|
66
|
+
|
67
|
+
self._servers_keys.update(r.payload)
|
68
|
+
self._update()
|
69
69
|
|
70
|
-
def encode(self, domain: str, request: bytes):
|
70
|
+
async def encode(self, domain: str, request: bytes):
|
71
71
|
if domain not in self._servers_keys:
|
72
|
-
|
72
|
+
if domain is None or not self._active_key_synchronization:
|
73
|
+
return request
|
74
|
+
else:
|
75
|
+
await self.requestKDC(domain)
|
76
|
+
|
73
77
|
key = self._servers_keys[domain]
|
74
78
|
sig = s2.sign(key)
|
75
79
|
data = m1.encrypt(domain.encode(), sig, request[8:], key)
|
76
80
|
return request[:8] + sig + self._domain_hash + data
|
77
81
|
|
78
|
-
def decode(self, response: bytes):
|
82
|
+
async def decode(self, response: bytes):
|
79
83
|
r = response
|
80
84
|
if len(response) < 8+164+64:
|
81
85
|
return r, None
|
@@ -83,10 +87,14 @@ class KDCObject:
|
|
83
87
|
response = response[8:]
|
84
88
|
sig, domain_h, data = response[:164], response[164:164+64], response[164+64:]
|
85
89
|
if domain_h not in self._servers_keys_hash_domain:
|
86
|
-
|
87
|
-
|
90
|
+
if domain_h is None or not self._active_key_synchronization:
|
91
|
+
print(domain_h, 'not in', self._servers_keys_hash_domain)
|
92
|
+
return r, None
|
93
|
+
else:
|
94
|
+
await self.requestKDC(domain_h)
|
95
|
+
|
88
96
|
d = self._servers_keys_hash_domain[domain_h]
|
89
97
|
key = self._servers_keys[d]
|
90
98
|
if not s2.verify(key, sig):
|
91
99
|
return None, None
|
92
|
-
return h + m1.decrypt(self._domain.encode(), sig, data, key), d
|
100
|
+
return h + m1.decrypt(self._domain.encode(), sig, data, key), d
|
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
|
File without changes
|
File without changes
|