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.
Files changed (21) hide show
  1. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_app.py +69 -29
  2. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_client.py +19 -13
  3. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/models.py +38 -30
  4. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer.egg-info/PKG-INFO +1 -1
  5. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/PKG-INFO +1 -1
  6. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/setup.py +1 -1
  7. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/__init__.py +0 -0
  8. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_cors_resolver.py +0 -0
  9. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_crt.py +0 -0
  10. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_func_params_validation.py +0 -0
  11. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_routes.py +0 -0
  12. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer/_template_resolver.py +0 -0
  13. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer.egg-info/SOURCES.txt +0 -0
  14. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer.egg-info/dependency_links.txt +0 -0
  15. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer.egg-info/requires.txt +0 -0
  16. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/GNServer.egg-info/top_level.txt +0 -0
  17. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/LICENSE +0 -0
  18. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/GNServer/mmbConfig.json +0 -0
  19. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/LICENSE +0 -0
  20. {gnserver-0.0.0.0.60 → gnserver-0.0.0.0.62}/MANIFEST.in +0 -0
  21. {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 _handle_request(self, request: GNRequest, mode: int):
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.__domain = domain
164
+ self._domain = domain
165
165
 
166
166
  self._kdc: Optional[KDCObject] = None
167
167
 
168
+
168
169
  if server_key:
169
- if isinstance(server_key, bytes):
170
- self.__server_key = server_key
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(server_key):
173
- self.__server_key = open(server_key, 'rb').read()
178
+ if os.path.exists(key):
179
+ self._dns_key = open(key, 'rb').read()
174
180
  else:
175
- self.__server_key = userFriendly.decode(server_key)
181
+ self._dns_key = userFriendly.decode(key)
176
182
  else:
177
- self.__server_key = None
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.__server_key is not None:
352
- s = s2.sign(self.__server_key)
353
- data = m1.encrypt(s, domain.encode(), serialize({'domain': domain}), hash(self.__server_key))
354
- payload = {'sign': {'alg': 'KeyisB-c-s-m1', 'data': s, 'domain': self.__domain}, 'data': data}
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
- payload = self._requested_domains
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
- return request
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
- print(domain_h, 'not in', self._servers_keys_hash_domain)
87
- return r, None
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GNServer
3
- Version: 0.0.0.0.60
3
+ Version: 0.0.0.0.62
4
4
  Summary: GNServer
5
5
  Home-page: https://github.com/KeyisB/libs/tree/main/GNServer
6
6
  Author: KeyisB
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GNServer
3
- Version: 0.0.0.0.60
3
+ Version: 0.0.0.0.62
4
4
  Summary: GNServer
5
5
  Home-page: https://github.com/KeyisB/libs/tree/main/GNServer
6
6
  Author: KeyisB
@@ -5,7 +5,7 @@ filesName = 'GNServer'
5
5
 
6
6
  setup(
7
7
  name=name,
8
- version='0.0.0.0.60',
8
+ version='0.0.0.0.62',
9
9
  author="KeyisB",
10
10
  author_email="keyisb.pip@gmail.com",
11
11
  description=name,
File without changes
File without changes
File without changes