Browse Source

ping/pong (wip). stop validating nonce - claiming its pointless. changes to the output

master
Benedikt Kristinsson 6 years ago
parent
commit
69512041e8
  1. 2
      cryptotools.py
  2. 37
      messages.py
  3. 66
      network.py
  4. 12
      tests/test_messages.py

2
cryptotools.py

@ -6,5 +6,3 @@ import hashlib
def generate_nodeid():
return hashlib.sha256(os.urandom(256/8)).hexdigest()
def generate_random(bits):
return os.urandom(bits).encode("hex")

37
messages.py

@ -2,15 +2,22 @@ import hmac
import json
import cryptotools
nonce = lambda: cryptotools.generate_random(64)
incr_nonce = lambda env: format(int(env["data"]["nonce"], 16) + 1, 'x')
# generate_nodeid() uses SHA256 so this will prevent replay-attacks,
# because every message will have a different nonce.
# It's not nessecary to compare the nonce, HMAC already gives message
# integrety.
nonce = lambda: cryptotools.generate_nodeid()
incr_nonce = lambda env: format(int(env["nonce"], 16) + 1, 'x')
class InvalidSignatureError(Exception):
pass
def make_envelope(msgtype, msg, nodeid, thisnonce=None):
class InvalidNonceError(Exception):
pass
def make_envelope(msgtype, msg, nodeid):
msg['nodeid'] = nodeid
msg['nonce'] = thisnonce or nonce()
msg['nonce'] = nonce()
sign = hmac.new(nodeid, json.dumps(msg))
envelope = {'data': msg,
'sign': sign.hexdigest(),
@ -31,25 +38,25 @@ def create_ping(nodeid):
msg = {}
return make_envelope("ping", msg, nodeid)
def create_pong(nodeid, pingmsg):
nonce = incr_nonce(pingmsg)
def create_pong(nodeid):
msg = {}
return make_envelope("pong", msg, nodeid, nonce)
return make_envelope("pong", msg, nodeid)
# -------
def read_message(envelope):
# Nonce should be validated one level above this
envelope = json.loads(envelope)
def read_envelope(message):
return json.loads(message)
def read_message(message):
"""Read and parse the message into json. Validate the signature
and return envelope['data']
"""
envelope = json.loads(message)
nodeid = str(envelope['data']['nodeid'])
signature = str(envelope['sign'])
msg = json.dumps(envelope['data'])
verify_sign = hmac.new(nodeid, msg)
if hmac.compare_digest(verify_sign.hexdigest(), signature):
return envelope
return envelope['data']
else:
raise InvalidSignatureError

66
network.py

@ -1,10 +1,11 @@
import sys
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory
from twisted.internet.endpoints import TCP4ServerEndpoint, TCP4ClientEndpoint
from twisted.internet.endpoints import connectProtocol
from twisted.internet import reactor
from twisted.internet.error import CannotListenError
from twisted.internet.task import LoopingCall
import messages
import cryptotools
@ -21,6 +22,8 @@ class NCProtocol(Protocol):
self.VERSION = 0
self.remote_nodeid = None
self.kind = kind
self.nodeid = self.factory.nodeid
self.lc_ping = LoopingCall(self.send_PING)
def connectionMade(self):
r_ip = self.transport.getPeer()
@ -29,48 +32,77 @@ class NCProtocol(Protocol):
self.host_ip = h_ip.host + ":" + str(h_ip.port)
def print_peers(self):
print " [ ] STATE:", self.state
if len(self.factory.peers) == 0:
print " [ ] PEERS: No peers connected."
print " [!] PEERS: No peers connected."
else:
print " [ ] PEERS:"
for peer in self.factory.peers:
addr, kind = self.factory.peers[peer]
print " [*]", peer, addr, kind
def write(self, line):
self.transport.write(line + "\n")
def connectionLost(self, reason):
print " [ ] LEAVES:", self.remote_nodeid
try:
# ghost peers?
self.factory.peers.pop(self.remote_nodeid)
self.print_peers()
except KeyError:
pass
if self.remote_nodeid != self.nodeid:
try:
self.factory.peers.pop(self.remote_nodeid)
self.print_peers()
except KeyError:
print " [?] GHOST:", self.remote_nodeid, self.remote_ip
def dataReceived(self, data):
if self.state == "GETHELLO":
if self.state != "READY":
# Force first message to be HELLO or crash
self.handle_HELLO(data)
else:
print "else"
envelope = messages.read_envelope(data)
if envelope['msgtype'] == 'ping':
print "ping"
self.handle_PING(data)
elif envelope['msgtype'] == 'pong':
self.handle_PONG(data)
def send_PING(self):
print " [ ] SEND_PING:", self.nodeid, "to", self.remote_nodeid
ping = messages.create_ping(self.nodeid)
self.write(ping)
def handle_PING(self, ping):
print " [ ] RECV_PING:", self.remote_nodeid
ping = messages.read_message(ping)
pong = messages.create_pong(self.nodeid, ping)
print " [ ] SEND_PONG"
self.write(pong)
def handle_PONG(self, pong):
# TODO: somehow get the ping nonce and check?
pong = messages.read_message(pong)
def send_HELLO(self):
hello = messages.create_hello(self.factory.nodeid, self.VERSION)
print " [ ] SEND_HELLO:", self.factory.nodeid, "to", self.remote_ip
hello = messages.create_hello(self.nodeid, self.VERSION)
print " [ ] SEND_HELLO:", self.nodeid, "to", self.remote_ip
self.transport.write(hello + "\n")
self.state = "GETHELLO"
def handle_HELLO(self, hello):
try:
hello = messages.read_message(hello)
self.remote_nodeid = hello['data']['nodeid']
if self.remote_nodeid == self.factory.nodeid:
print " [!] Dropping connection to self on", self.host_ip
self.remote_nodeid = hello['nodeid']
if self.remote_nodeid == self.nodeid:
print " [!] Dropping connection to self on", self.host_ip
self.transport.loseConnection()
else:
my_hello = messages.create_hello(self.factory.nodeid, self.VERSION)
my_hello = messages.create_hello(self.nodeid, self.VERSION)
self.transport.write(my_hello + "\n")
self.factory.peers[self.remote_nodeid] = (self.remote_ip, self.kind)
self.state = "READY"
print " [ ] JOINED:", self.remote_nodeid
self.print_peers()
self.lc_ping.start(4.20)
except (ValueError, ):
print " [!] Disconnecting peer. ",
print "Unable to read hello msg from", self.remote_ip

12
tests/test_messages.py

@ -21,15 +21,15 @@ class TestMessages(unittest.TestCase):
ackhello = messages.create_ackhello(self.nodeid)
return messages.read_message(ackhello)
def test_pingpong(self):
def test_ping(self):
ping = messages.create_ping(self.nodeid)
# check that InvalidSignatureError isn't raised
read_ping = messages.read_message(ping)
pong = messages.create_pong(self.nodeid, read_ping)
return messages.read_message(ping)
def test_pong(self):
pong = messages.create_pong(self.nodeid)
# exceptions?
read_pong = messages.read_message(pong)
expected_nonce = messages.incr_nonce(read_ping)
self.assertEquals(read_pong['data']['nonce'], expected_nonce)
return messages.read_message(pong)
if __name__ == "__main__":
unittest.main()

Loading…
Cancel
Save