ctp-py 1.5.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.
- ctp_py-1.5.0/PKG-INFO +10 -0
- ctp_py-1.5.0/README.md +2 -0
- ctp_py-1.5.0/pyproject.toml +13 -0
- ctp_py-1.5.0/setup.cfg +4 -0
- ctp_py-1.5.0/setup.py +2 -0
- ctp_py-1.5.0/src/ctp/__init__.py +190 -0
- ctp_py-1.5.0/src/ctp_py.egg-info/PKG-INFO +10 -0
- ctp_py-1.5.0/src/ctp_py.egg-info/SOURCES.txt +9 -0
- ctp_py-1.5.0/src/ctp_py.egg-info/dependency_links.txt +1 -0
- ctp_py-1.5.0/src/ctp_py.egg-info/requires.txt +1 -0
- ctp_py-1.5.0/src/ctp_py.egg-info/top_level.txt +1 -0
ctp_py-1.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ctp-py
|
|
3
|
+
Version: 1.5.0
|
|
4
|
+
Summary: Crypto Transfer Protocol v1.5
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: cryptography>=42.0.0
|
|
8
|
+
|
|
9
|
+
# CTP v1.5
|
|
10
|
+
Crypto Transfer Protocol for secure, high-speed data exchange.
|
ctp_py-1.5.0/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build-meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ctp-py" # The name used for 'pip install'
|
|
7
|
+
version = "1.5.0"
|
|
8
|
+
description = "Crypto Transfer Protocol v1.5"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"cryptography>=42.0.0",
|
|
13
|
+
]
|
ctp_py-1.5.0/setup.cfg
ADDED
ctp_py-1.5.0/setup.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import socket, threading, struct, os, hashlib, hmac, time
|
|
2
|
+
from cryptography.hazmat.primitives.asymmetric import ec
|
|
3
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
|
4
|
+
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
|
5
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
6
|
+
|
|
7
|
+
# --- CONSTANTS ---
|
|
8
|
+
CTP_SECRET = b"CTP_CRYPTO_TRANSFER_SHARED_SECRET_2026"
|
|
9
|
+
VERSION = b"CTP/1.5"
|
|
10
|
+
MAX_SKEW = 5.0
|
|
11
|
+
|
|
12
|
+
MSG_HANDSHAKE = 0x01
|
|
13
|
+
MSG_AUTH = 0x02
|
|
14
|
+
MSG_DATA = 0x03
|
|
15
|
+
MSG_ERROR = 0xFF
|
|
16
|
+
|
|
17
|
+
class CTPEngine:
|
|
18
|
+
def __init__(self, is_server=False):
|
|
19
|
+
self.is_server = is_server
|
|
20
|
+
self.private_key = ec.generate_private_key(ec.SECP521R1())
|
|
21
|
+
self.public_key = self.private_key.public_key()
|
|
22
|
+
self.session_key = None
|
|
23
|
+
self.aesgcm = None
|
|
24
|
+
self.transcript = hashlib.sha3_512()
|
|
25
|
+
self.my_nonce = os.urandom(16)
|
|
26
|
+
|
|
27
|
+
pub_bytes = self.public_key.public_bytes(
|
|
28
|
+
encoding=serialization.Encoding.X962,
|
|
29
|
+
format=serialization.PublicFormat.UncompressedPoint
|
|
30
|
+
)
|
|
31
|
+
ts = struct.pack('>Q', int(time.time() * 1000000))
|
|
32
|
+
self.local_handshake_blob = pub_bytes + self.my_nonce + VERSION + ts
|
|
33
|
+
|
|
34
|
+
def get_handshake_blob(self) -> bytes:
|
|
35
|
+
return self.local_handshake_blob
|
|
36
|
+
|
|
37
|
+
def finalize_handshake(self, peer_blob: bytes):
|
|
38
|
+
if not peer_blob or len(peer_blob) < 164:
|
|
39
|
+
raise Exception("INVALID_HANDSHAKE_BLOB")
|
|
40
|
+
|
|
41
|
+
peer_pub_bytes = peer_blob[:133]
|
|
42
|
+
peer_nonce = peer_blob[133:149]
|
|
43
|
+
peer_version = peer_blob[149:156]
|
|
44
|
+
peer_ts = struct.unpack('>Q', peer_blob[156:164])[0] / 1000000.0
|
|
45
|
+
|
|
46
|
+
if abs(time.time() - peer_ts) > MAX_SKEW:
|
|
47
|
+
raise Exception(f"REPLAY/SKEW_DETECTED: {abs(time.time() - peer_ts):.2f}s")
|
|
48
|
+
|
|
49
|
+
if peer_version != VERSION:
|
|
50
|
+
raise Exception("VERSION_MISMATCH")
|
|
51
|
+
|
|
52
|
+
if self.is_server:
|
|
53
|
+
client_blob, server_blob = peer_blob, self.local_handshake_blob
|
|
54
|
+
c_nonce, s_nonce = peer_nonce, self.my_nonce
|
|
55
|
+
else:
|
|
56
|
+
client_blob, server_blob = self.local_handshake_blob, peer_blob
|
|
57
|
+
c_nonce, s_nonce = self.my_nonce, peer_nonce
|
|
58
|
+
|
|
59
|
+
self.transcript.update(b"CTP_1.5_BIND")
|
|
60
|
+
self.transcript.update(b"C:")
|
|
61
|
+
self.transcript.update(client_blob)
|
|
62
|
+
self.transcript.update(b"S:")
|
|
63
|
+
self.transcript.update(server_blob)
|
|
64
|
+
|
|
65
|
+
peer_pk = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP521R1(), peer_pub_bytes)
|
|
66
|
+
shared = self.private_key.exchange(ec.ECDH(), peer_pk)
|
|
67
|
+
|
|
68
|
+
self.session_key = HKDF(
|
|
69
|
+
algorithm=hashes.SHA256(), length=32,
|
|
70
|
+
salt=c_nonce + s_nonce,
|
|
71
|
+
info=CTP_SECRET + b"CTP_BIND_v1.5",
|
|
72
|
+
).derive(shared)
|
|
73
|
+
|
|
74
|
+
self.aesgcm = AESGCM(self.session_key)
|
|
75
|
+
|
|
76
|
+
def get_auth_verify(self):
|
|
77
|
+
return hmac.new(CTP_SECRET, self.transcript.digest(), hashlib.sha256).digest()
|
|
78
|
+
|
|
79
|
+
def seal(self, data: bytes) -> bytes:
|
|
80
|
+
if not self.aesgcm: raise Exception("ENGINE_NOT_READY")
|
|
81
|
+
n = os.urandom(12)
|
|
82
|
+
return n + self.aesgcm.encrypt(n, data, None)
|
|
83
|
+
|
|
84
|
+
def unseal(self, data: bytes) -> bytes:
|
|
85
|
+
if not self.aesgcm or not data: raise Exception("DECRYPTION_FAILED")
|
|
86
|
+
return self.aesgcm.decrypt(data[:12], data[12:], None)
|
|
87
|
+
|
|
88
|
+
# --- UTILS ---
|
|
89
|
+
def _send(s, t, p): s.sendall(struct.pack('>BI', t, len(p)) + p)
|
|
90
|
+
def _recvall(s, n):
|
|
91
|
+
d = bytearray()
|
|
92
|
+
while len(d) < n:
|
|
93
|
+
p = s.recv(n - len(d))
|
|
94
|
+
if not p: return None
|
|
95
|
+
d.extend(p)
|
|
96
|
+
return bytes(d)
|
|
97
|
+
def _recv(s):
|
|
98
|
+
h = _recvall(s, 5)
|
|
99
|
+
if not h: return None, None
|
|
100
|
+
t, l = struct.unpack('>BI', h)
|
|
101
|
+
return t, _recvall(s, l)
|
|
102
|
+
|
|
103
|
+
# --- PUBLIC API ---
|
|
104
|
+
|
|
105
|
+
class CTPServer:
|
|
106
|
+
def __init__(self, host='127.0.0.1', port=8937):
|
|
107
|
+
self.host = host
|
|
108
|
+
self.port = port
|
|
109
|
+
self.running = False
|
|
110
|
+
|
|
111
|
+
def start(self, handler_callback):
|
|
112
|
+
"""handler_callback(engine, conn) will be called after Auth."""
|
|
113
|
+
s = socket.socket()
|
|
114
|
+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
115
|
+
s.bind((self.host, self.port))
|
|
116
|
+
s.listen(10)
|
|
117
|
+
self.running = True
|
|
118
|
+
print(f"[*] CTP Server listening on {self.port}...")
|
|
119
|
+
|
|
120
|
+
while self.running:
|
|
121
|
+
try:
|
|
122
|
+
conn, _ = s.accept()
|
|
123
|
+
threading.Thread(target=self._session_thread, args=(conn, handler_callback), daemon=True).start()
|
|
124
|
+
except KeyboardInterrupt: break
|
|
125
|
+
s.close()
|
|
126
|
+
|
|
127
|
+
def _session_thread(self, conn, callback):
|
|
128
|
+
try:
|
|
129
|
+
engine = CTPEngine(is_server=True)
|
|
130
|
+
t, c_blob = _recv(conn)
|
|
131
|
+
if t != MSG_HANDSHAKE: return
|
|
132
|
+
_send(conn, MSG_HANDSHAKE, engine.get_handshake_blob())
|
|
133
|
+
engine.finalize_handshake(c_blob)
|
|
134
|
+
|
|
135
|
+
t, enc_auth = _recv(conn)
|
|
136
|
+
if t != MSG_AUTH: return
|
|
137
|
+
if not hmac.compare_digest(engine.unseal(enc_auth), engine.get_auth_verify()):
|
|
138
|
+
return # Auth Failure
|
|
139
|
+
|
|
140
|
+
_send(conn, MSG_AUTH, engine.seal(engine.get_auth_verify()))
|
|
141
|
+
callback(engine, conn) # Pass off to user logic
|
|
142
|
+
except Exception as e: print(f"[!] Server Error: {e}")
|
|
143
|
+
finally: conn.close()
|
|
144
|
+
|
|
145
|
+
class CTPClient:
|
|
146
|
+
def __init__(self, host='127.0.0.1', port=8937):
|
|
147
|
+
self.host = host
|
|
148
|
+
self.port = port
|
|
149
|
+
self.engine = CTPEngine(is_server=False)
|
|
150
|
+
self.conn = socket.socket()
|
|
151
|
+
|
|
152
|
+
def connect(self):
|
|
153
|
+
self.conn.connect((self.host, self.port))
|
|
154
|
+
_send(self.conn, MSG_HANDSHAKE, self.engine.get_handshake_blob())
|
|
155
|
+
t, s_blob = _recv(self.conn)
|
|
156
|
+
self.engine.finalize_handshake(s_blob)
|
|
157
|
+
|
|
158
|
+
_send(self.conn, MSG_AUTH, self.engine.seal(self.engine.get_auth_verify()))
|
|
159
|
+
t, enc_s_auth = _recv(self.conn)
|
|
160
|
+
if not hmac.compare_digest(self.engine.unseal(enc_s_auth), self.engine.get_auth_verify()):
|
|
161
|
+
raise Exception("SERVER_AUTH_FAILED")
|
|
162
|
+
return True
|
|
163
|
+
|
|
164
|
+
def send_data(self, data: bytes):
|
|
165
|
+
_send(self.conn, MSG_DATA, self.engine.seal(data))
|
|
166
|
+
|
|
167
|
+
def recv_data(self):
|
|
168
|
+
t, enc_p = _recv(self.conn)
|
|
169
|
+
if t == MSG_DATA: return self.engine.unseal(enc_p)
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
def close(self):
|
|
173
|
+
self.conn.close()
|
|
174
|
+
|
|
175
|
+
def runserver(port=8937):
|
|
176
|
+
def default_logic(engine, conn):
|
|
177
|
+
t, enc_req = _recv(conn)
|
|
178
|
+
if t == MSG_DATA:
|
|
179
|
+
print(f"[>] Request: {engine.unseal(enc_req).decode()}")
|
|
180
|
+
_send(conn, MSG_DATA, engine.seal(b"CTP_ACK"))
|
|
181
|
+
|
|
182
|
+
srv = CTPServer(port=port)
|
|
183
|
+
srv.start(default_logic)
|
|
184
|
+
|
|
185
|
+
def runclient(port=8937):
|
|
186
|
+
client = CTPClient(port=port)
|
|
187
|
+
if client.connect():
|
|
188
|
+
client.send_data(b"HELLO_CTP")
|
|
189
|
+
print(f"[<] Response: {client.recv_data().decode()}")
|
|
190
|
+
client.close()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ctp-py
|
|
3
|
+
Version: 1.5.0
|
|
4
|
+
Summary: Crypto Transfer Protocol v1.5
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: cryptography>=42.0.0
|
|
8
|
+
|
|
9
|
+
# CTP v1.5
|
|
10
|
+
Crypto Transfer Protocol for secure, high-speed data exchange.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cryptography>=42.0.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ctp
|