List of commits:
Subject Hash Author Date (UTC)
Working ping 090854611163edc23aec1b460b285770c6f5b806 GDR! 2017-11-09 20:30:37
Initial 5492ec94c8cab618b3e617e0ac2f8d5b27335f83 GDR! 2017-11-09 17:41:55
Commit 090854611163edc23aec1b460b285770c6f5b806 - Working ping
Author: GDR!
Author date (UTC): 2017-11-09 20:30
Committer name: GDR!
Committer date (UTC): 2017-11-09 20:30
Parent(s): 5492ec94c8cab618b3e617e0ac2f8d5b27335f83
Signer:
Signing key:
Signing status: N
Tree: eaf5d9c5eeff29451515ee4ba6d3fed4d2a6c3c6
File Lines added Lines deleted
crap.py 21 8
tcp.py 106 20
File crap.py changed (mode: 100755) (index 9292361..ae3237b)
... ... import socket
4 4
5 5 from identity import Identity from identity import Identity
6 6 from session import Session from session import Session
7 from tcp import TcpPacket, TcpRelay
7 from tcp import TcpPacket, TcpRelay, TcpServerHelloPacket, TcpPingPacket, h
8 8
9 9 if __name__ == "__main__": if __name__ == "__main__":
10 10 identity = Identity() identity = Identity()
11 sess = Session()
12 print("PK", sess.get_printable_public_key())
11 session = Session()
12 print("PK", session.get_printable_public_key())
13 13 print("toxid", identity.get_printable_public_key()) print("toxid", identity.get_printable_public_key())
14 14
15 relay = TcpRelay("biribiri.org", 3389, "F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67")
16 packet = relay.get_client_hello_packet(sess, identity)
15 #relay = TcpRelay("biribiri.org", 3389, "F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67")
16 relay = TcpRelay("localhost", 4443, "6192C734814F1BC762110A5190DBECB9A98346138EC062E0EA8F0A61087D7D28")
17 packet = relay.get_client_hello_packet(session, identity)
17 18
18 19 s = socket.socket() s = socket.socket()
19 s.connect(("biribiri.org", 3389))
20 s.connect((relay.hostname, relay.port))
20 21 print("connected") print("connected")
21 22
22 23 packet.send(s) packet.send(s)
23 24
24 print ("RECEIVED:")
25 print(s.recv(4096))
25 received = s.recv(4096)
26 packet = TcpServerHelloPacket()
27 packet.parse(received)
28 packet.decrypt_payload(relay, session)
29 print ("relay key", h(packet.relay_public_key))
30 print ("base nonce", h(packet.base_nonce))
31 relay.set_outgoing_nonce(packet.base_nonce)
32 relay.set_outgoing_public_key(packet.relay_public_key)
33
34 ping = TcpPingPacket(relay)
35 ping.send(s)
36
37 print (s.recv(4096))
38
26 39 s.close() s.close()
File tcp.py changed (mode: 100644) (index f7847f7..ca938da)
... ... import struct
5 5 import nacl.bindings import nacl.bindings
6 6 import nacl.utils import nacl.utils
7 7
8 import util
9
8 10 from nacl.public import PrivateKey, PublicKey, Box from nacl.public import PrivateKey, PublicKey, Box
9 11
10 12 from exceptions import * from exceptions import *
11 13
12 14 class SerializationError(ToxException): class SerializationError(ToxException):
13 15 pass pass
16 class ParseError(ToxException):
17 pass
18
19 def h(b):
20 return binascii.hexlify(b)
14 21
15 22 class TcpPacket: class TcpPacket:
16 23 """ """
 
... ... class TcpPacket:
26 33 serialized_b = self.serialize() serialized_b = self.serialize()
27 34 print(binascii.hexlify(serialized_b)) print(binascii.hexlify(serialized_b))
28 35 socket.send(serialized_b) socket.send(serialized_b)
36 print ("sent %d bytes" % len(serialized_b))
37
38 def encrypt(self, data):
39 """
40 Identity function - to be overridden
41 """
42 return data
29 43
30 44 def serialize(self): def serialize(self):
31 45 """ """
32 46 Return wire-ready serialized packet Return wire-ready serialized packet
33 47 """ """
34 serialized_b = self.bytes
48 serialized_b = self.encrypt(self.bytes)
35 49 if len(serialized_b) >= 2**16: if len(serialized_b) >= 2**16:
36 50 raise SerializationError('TCP packet too long: %d' % len(serialized_b)) raise SerializationError('TCP packet too long: %d' % len(serialized_b))
37 51
38 52 length_b = struct.pack(">H", len(serialized_b)) length_b = struct.pack(">H", len(serialized_b))
39 53 return length_b + serialized_b return length_b + serialized_b
40 54
41 class TestTcpPacket(TcpPacket):
42 def __init__(self):
43 self.bytes = "Hi!".encode("utf-8")
55 class EncryptedOutgoingPacket(TcpPacket):
56 def __init__(self, relay):
57 self.relay = relay
58
59 def encrypt(self, payload):
60 """
61 Encrypt with outgoing_public_key, sign with relay_private_key
62 """
63 relay = self.relay
64 combined = nacl.bindings.crypto_box_beforenm(bytes(relay.get_outgoing_public_key()), bytes(relay.get_private_key()))
65 nonce = relay.get_incoming_nonce()
66 print ("combined key", binascii.hexlify(combined))
67 print ("outgoing nonce", h(nonce))
68 encrypted = nacl.bindings.crypto_box_afternm(payload, nonce, combined)
69 return encrypted
70
71 class TcpPingPacket(EncryptedOutgoingPacket):
72 """
73 Ping packet (0x04)
74 """
75 def __init__(self, relay):
76 super().__init__(relay)
77 self.bytes = b"\x04puretoxc"
44 78
45 79 class TcpClientHelloPacket(TcpPacket): class TcpClientHelloPacket(TcpPacket):
80 """
81 The TCP packet sent just after opening the connection
82 """
46 83 def __init__(self, relay, session, identity): def __init__(self, relay, session, identity):
47 84 # The nonce used in hello packet only # The nonce used in hello packet only
48 85 self.nonce = bytes(session.get_nonce()) self.nonce = bytes(session.get_nonce())
 
... ... class TcpClientHelloPacket(TcpPacket):
53 90 self.bytes += self.get_payload(relay, session, identity) self.bytes += self.get_payload(relay, session, identity)
54 91
55 92 def get_payload(self, relay, session, identity): def get_payload(self, relay, session, identity):
56 payload = bytes(identity.get_public_key())
57 payload += bytes(relay.get_tcp_nonce())
58 print (payload)
59
60 combined = nacl.bindings.crypto_box_beforenm(bytes(relay.get_public_key()), bytes(session.get_private_key()))
61 encrypted = nacl.bindings.crypto_box_afternm(payload, relay.get_tcp_nonce(), combined)
93 payload = bytes(relay.get_incoming_public_key())
94 payload += bytes(relay.get_incoming_nonce())
95
96 combined = nacl.bindings.crypto_box_beforenm(bytes(relay.get_dht_public_key()), bytes(session.get_private_key()))
97 print ("combined key", binascii.hexlify(combined))
98 print ("relay incoming nonce", h(relay.get_incoming_nonce()))
99 print ("self nonce", h(self.nonce))
100 encrypted = nacl.bindings.crypto_box_afternm(payload, self.nonce, combined)
62 101 print(encrypted) print(encrypted)
63 102
64 #box = Box(session.get_private_key(), relay.get_public_key())
65 #encrypted = box.encrypt(payload, relay.get_tcp_nonce())
66 103 return bytes(encrypted) return bytes(encrypted)
67 104
68 105 def serialize(self): def serialize(self):
 
... ... class TcpClientHelloPacket(TcpPacket):
71 108 """ """
72 109 return self.bytes return self.bytes
73 110
111 class TcpServerHelloPacket(TcpPacket):
112 """
113 TCP packet received in response to TcpClientHelloPacket
114 """
115 def parse(self, blob):
116 if len(blob) != 24+72:
117 raise ParseError("Packet length %d instead of %d" % (len(blob), 24+72))
118
119 self.nonce = blob[:24]
120 self.encrypted_payload = blob[24:]
121
122 def decrypt_payload(self, relay, session):
123 combined = nacl.bindings.crypto_box_beforenm(bytes(relay.get_dht_public_key()), bytes(session.get_private_key()))
124 plaintext = nacl.bindings.crypto_box_open_afternm(self.encrypted_payload, self.nonce, combined)
125 if len(plaintext) != 32+24:
126 raise ParseError("Plaintext length %d instead of %d" % (len(plaintext), 32+24))
127 self.relay_public_key = plaintext[:32]
128 self.base_nonce = plaintext[32:32+24]
129
130
131
74 132 class TcpRelay: class TcpRelay:
75 133 """ """
76 134 Represents a connection to TCP relay Represents a connection to TCP relay
77 135 """ """
78 136 def __init__(self, hostname, port, public_key): def __init__(self, hostname, port, public_key):
79 137 """ """
80 public_key is a hex-encoded string containing relay's public key
138 public_key is a hex-encoded string containing relay's public DHT key
81 139 """ """
82 140 self.hostname = hostname self.hostname = hostname
83 141 self.port = int(port) self.port = int(port)
84 self.public_key = PublicKey(binascii.unhexlify(public_key))
85 # "Base Nonce" from documentation
142 self.dht_public_key = PublicKey(binascii.unhexlify(public_key))
143 # The base nonce is the one TCP client wants the TCP server to use to encrypt the packets sent to the TCP client.
144 self.tcp_incoming_nonce = nacl.utils.random(Box.NONCE_SIZE)
145 # temporary key that will be used for encryption during the connection and will be discarded after
146 self.private_key = PrivateKey.generate()
86 147 # The base nonce is the one the TCP server wants the TCP client to use to encrypt the packets sent to the TCP server. # The base nonce is the one the TCP server wants the TCP client to use to encrypt the packets sent to the TCP server.
87 self.tcp_nonce = nacl.utils.random(Box.NONCE_SIZE)
148 self.tcp_outgoing_nonce = None
149 # a temporary public key tied to this connection
150 self.outgoing_public_key = None
88 151
89 152 def get_client_hello_packet(self, session, identity): def get_client_hello_packet(self, session, identity):
90 153 return TcpClientHelloPacket(self, session, identity) return TcpClientHelloPacket(self, session, identity)
91 154
92 def get_tcp_nonce(self):
93 return self.tcp_nonce
155 def get_incoming_nonce(self):
156 return self.tcp_incoming_nonce
157
158 def get_incoming_public_key(self):
159 return self.private_key.public_key
160
161 def get_private_key(self):
162 return self.private_key
163
164 def get_dht_public_key(self):
165 return self.dht_public_key
166
167 def set_outgoing_nonce(self, nonce):
168 self.tcp_outgoing_nonce = nonce
169
170 def set_outgoing_public_key(self, key):
171 self.tcp_outgoing_public_key = PublicKey(key)
172
173 def get_outgoing_nonce(self):
174 rv = self.tcp_outgoing_nonce
175 #self.increment_outgoing_nonce()
176 return rv
177
178 def increment_outgoing_nonce(self):
179 self.tcp_outgoing_nonce = util.increment_nonce(self.tcp_outgoing_nonce)
94 180
95 def get_public_key(self):
96 return self.public_key
181 def get_outgoing_public_key(self):
182 return self.tcp_outgoing_public_key
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/gdr/PurePyTox

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/gdr/PurePyTox

Clone this repository using git:
git clone git://git.rocketgit.com/user/gdr/PurePyTox

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main