Exemplo n.º 1
0
class TestDHT(TestCase):
    dht = DHT()
    dht1 = DHT()

    dht.fingerTable.predecessor = "Alice"
    dht.fingerTable.successor = "Bob"
    dht.fingerTable.nodeid = "Trudy"

    def test_loadDHTInformation(self):

        self.dht.writeDHTInformation()
        self.dht1.loadDHTInformation()
        self.assertTrue(self.dht1.fingerTable.nodeid == "Trudy")

    def test_writeDHTInformation(self):
        self.dht.writeDHTInformation()
        file = Path("DHT.json")
        self.assertTrue(file.is_file())

    def test_updateSuccessor(self):
        self.dht.writeDHTInformation()
        self.dht.loadDHTInformation()
        self.dht.updatePredecessor("Bob")
        self.dht.loadDHTInformation()
        self.assertTrue(self.dht.fingerTable.predecessor == "Bob")

    def test_updatePredecessor(self):
        self.dht.writeDHTInformation()
        self.dht.loadDHTInformation()
        self.dht.updateSuccessor("Alice")
        self.dht.loadDHTInformation()
        self.assertTrue(self.dht.fingerTable.successor == "Alice")
Exemplo n.º 2
0
    def connectToExistingNetwork(self, bootstrapIP, bootstrapPort, localIP,
                                 localPort, username):
        global listbox
        bootstrapIP = str(bootstrapIP.get())
        bootstrapPort = str(bootstrapPort.get())
        localIP = str(localIP.get())
        localPort = str(localPort.get())
        username = str(username.get())
        """
        General data sanitation from the UI into things that our twisted application can understand
        Encoding as bytes for transfer
        """
        bootstrapPort = int(bootstrapPort)
        """Create our encryption keys"""
        self.encryption.generate_keys()
        logger.info("public key")
        logger.info(self.encryption.public_key)
        logger.info("Encryption public key")
        logger.info(self.encryption.getPublicKey())
        logger.info("Encryption private key")
        logger.info(self.encryption.getPrivateKey())
        """Initialize DHT"""
        initialization = DHT()

        user = User()
        user.ip = localIP
        user.port = localPort
        user.username = username
        user.publicKey = self.encryption.getPublicKey().decode("utf-8")
        user.nodeid = self.Utils.generateID(user.username)
        initialization.fingerTable.nodeid = user.toDict()
        initialization.writeDHTInformation()

        user.publicKey = self.encryption.getPublicKey().decode("utf-8")
        """Register username in a local file so we know who we are"""
        file = open("User.json", "w+")
        userstr = json.dumps(user.toDict())
        file.write(str(userstr))
        logger.info("Models Dict" + userstr)

        logging.info("BOOTSTRAP TIME START")

        reactor.connectTCP(bootstrapIP, bootstrapPort,
                           DHTRegistration(userstr))
Exemplo n.º 3
0
    def createNewNetwork(self, localip, localport, username):
        logging.info("Creating a new network")
        localip = str(localip.get())
        localport = str(localport.get())
        username = str(username.get())
        localport = str(localport)
        """Initialize DHT"""
        initialization = DHT()

        self.encryption.generate_keys()

        # Create a user object, set all user variables to the data gathered in the form
        user = User()
        user.ip = localip
        user.port = localport
        user.username = username
        user.publicKey = self.encryption.getPublicKey().decode("utf-8")
        user.nodeid = self.Utils.generateID(user.username)

        # Convert our user to json format so we can save it to file
        userAsJSON = json.dumps(user.toDict())
        # Register username in a local file so we know who we are
        file = open("User.json", "w+")
        file.write(str(userAsJSON))
        file.close()

        initialization = DHT()
        initialization.fingerTable.nodeid = user.toDict()
        initialization.writeDHTInformation()
Exemplo n.º 4
0
class TestAsymmetricEncryption(TestCase):
    dht = DHT()

    def setUp(self):
        self.asymmetricEncryption = AsymmetricEncryption()
        self.symmetricEncryption = SymmetricEncryption()

        self.onionrouter = OR()
        self.peers = open("../DHT.json", "r")
        self.peers = self.peers.read()
        self.peers = json.loads(self.peers)

    # Ensure that a public and private key pair are generated
    def test_generate_keys(self):
        self.asymmetricEncryption.generate_keys()
        self.assertTrue(self.asymmetricEncryption.private_key != None
                        and self.asymmetricEncryption.public_key != None)

    # Simple test to ensure that the generated private key is saved to the local file server. Its important that this key is not lost.
    def test_save_key(self):
        self.asymmetricEncryption.generate_keys()
        self.asymmetricEncryption.save_key(
            self.asymmetricEncryption.private_key, 'privatekey')
        privatekeyfile = Path("privatekey")
        self.assertTrue(privatekeyfile.is_file())

    # Test that encryption method works. Plain text is entered and checked against the cipher text
    def test_encrypt(self):
        self.asymmetricEncryption.generate_keys()
        plaintext = "plaintext"
        plaintext = plaintext.encode('utf-8')
        ciphertext = self.asymmetricEncryption.encrypt(
            self.asymmetricEncryption.getPublicKey().decode("utf-8"),
            plaintext)
        self.assertTrue(plaintext != ciphertext)

    def test_decrypt(self):
        self.asymmetricEncryption.generate_keys()
        initialText = "plaintext"
        encoded = initialText.encode('utf-8')
        ciphertext = self.asymmetricEncryption.encrypt(
            self.asymmetricEncryption.getPublicKey().decode("utf-8"), encoded)
        plaintextDecrypted = self.asymmetricEncryption.decrypt("2", ciphertext)
        plaintextDecrypted = plaintextDecrypted.decode('utf-8')

        self.assertTrue(initialText == plaintextDecrypted)
Exemplo n.º 5
0
    def secondNodeJoins(self, currentUser, incomingNode):
        # Sets the local finger tables predecessor and successor to the new incoming node
        self.dht.fingerTable.predecessor = incomingNode
        self.dht.fingerTable.successor = incomingNode
        self.dht.fingerTable.nodeid = currentUser
        # Writes this updated DHT information to file
        self.dht.writeDHTInformation()

        # Send back the inverse of our finger table to the new node. E.g.the local node is the bootstrapping nodes successor and predecessor
        dhtForNewNode = DHT()
        dhtForNewNode.fingerTable.successor = currentUser
        dhtForNewNode.fingerTable.predecessor = currentUser
        dhtForNewNode.fingerTable.nodeid = incomingNode

        # Establish a TCP connection and return the calculated finger table
        reactor.connectTCP(incomingNode['ip'], int(incomingNode['port']),
                           DHTReturn(dhtForNewNode.fingerTable.toDict()))
Exemplo n.º 6
0
    def newPredecessor(self, incomingNode):

        """This handles the remote node"""
        dhtForNewNode = DHT()
        dhtForNewNode.fingerTable.successor = self.dht.fingerTable.nodeid
        dhtForNewNode.fingerTable.nodeid = incomingNode
        dhtForNewNode.fingerTable.predecessor = self.dht.fingerTable.predecessor

        # Stabilise the network by updating old predecessor with its new successor
        reactor.connectTCP(self.dht.fingerTable.predecessor['ip'],
                           int(self.dht.fingerTable.predecessor['port']), DHTSuccessorUpdate(incomingNode))

        # Provide the node being bootstrapped with network information allowing it to position itself in the network

        reactor.connectTCP(incomingNode['ip'], int(incomingNode['port']),
                           DHTReturn(dhtForNewNode.fingerTable.toDict()))

        # Update the local node with its new predecessor
        self.dht.fingerTable.predecessor = incomingNode
        # Write this new information to file
        self.dht.writeDHTInformation()
Exemplo n.º 7
0
class DHTAlgoirthm():
    dht = DHT()

    """
    DHTPositionSearch()

    DHTPositionSearch is the bread and butter of the DHT. This is brain behind the DHT
    Logic is outsourced to other methods
    Determines how the incoming node should be bootstrapped
    
    Arguments 
    incomingNode (user): Takes a user object

    """

    def DHTPositionSearch(self, incomingNode):
        logging.info("DHT position search ")

        # Remove the network command from the message
        incomingNode = incomingNode.replace('==REGISTER==', '')
        incomingNode = json.loads(incomingNode)

        # Read in the current users information
        with open("User.json", "r") as f:
            contents = f.read()
        currentUser = json.loads(contents)

        # Load the current DHT information from file
        self.dht.loadDHTInformation()
        logging.info("Loaded DHT information")

        # Determine how the bootstrapping request should be satisfied

        # Determines whether the node joining will be the second node
        if (self.dht.fingerTable.successor['nodeid'] == '' and self.dht.fingerTable.predecessor['nodeid'] == ''):
            logging.info("Second node has joined the DHT")

            self.secondNodeJoins(currentUser, incomingNode)

        # Determines whether the node joining will be the third node
        elif(self.dht.fingerTable.successor['nodeid'] == self.dht.fingerTable.predecessor['nodeid']):
            logging.info("Third node has joined the DHT")

            self.thirdNodeJoins(incomingNode)

        # Determines whether the local node resides at the first location of the DHT
        elif (self.dht.fingerTable.nodeid['nodeid'] < self.dht.fingerTable.predecessor['nodeid']):
            logging.info("Edge case start of DHT")

            self.startOfDHTEdgeCase(currentUser, incomingNode)


        # Determines whether the local node resides at the end of the DHT
        elif (self.dht.fingerTable.nodeid['nodeid'] > self.dht.fingerTable.successor['nodeid']):
            logging.info("Edge case end of DHT")
            self.endOfDHTEdgeCase(currentUser, incomingNode)

            """Check for edge case when we are at the start of our DHT"""

        # Determines whether the incoming node should be the local nodes predecessor
        elif (incomingNode['nodeid'] < self.dht.fingerTable.nodeid['nodeid'] and incomingNode['nodeid'] > self.dht.fingerTable.predecessor['nodeid']):
            logging.info("New predecessor no edge case")
            self.newPredecessor(incomingNode)

        # Determines whether the incoming node should be the local nodes successor
        elif (incomingNode['nodeid'] > self.dht.fingerTable.nodeid['nodeid'] and incomingNode['nodeid'] < self.dht.fingerTable.successor['nodeid']):
            logging.info("New successsor no edge case")
            self.newSuccessor(incomingNode)

        # Cannot satisfy this request, forwarded to the local nodes successor
        elif (incomingNode['nodeid'] > self.dht.fingerTable.successor['nodeid']):
            self.fowardRequestToSuccessor(incomingNode)

        # Cannot satisfy this request, forwarded to the local nodes predecessor
        elif (incomingNode['nodeid'] < self.dht.fingerTable.predecessor['nodeid']):
            self.fowardRequestToPredecessor(incomingNode)

        # Error - Possibly corrupted data in transit
        else:
            logging.error("Error should never reach this position")

    """
    thirdNodeJoins()

    thirdNodeJoins - Handles the edge case upon which 2 nodes are currently in the network and a third wishes to join. 

    Arguments 
    incomingNode (user): Takes a user object

    """

    def thirdNodeJoins(self, incomingNode):
        if(incomingNode['nodeid'] < self.dht.fingerTable.nodeid['nodeid']):
            """This handles the remote node"""
            self.newPredecessor(incomingNode)
        else:
            self.newSuccessor(incomingNode)

    """
    secondNodeJoins()

    secondNodeJoins - Handles the edge case where only 1 node is currently in the network and a second wishes to join
    The DHT must always form a circle, to achieve both nodes successor and predecessor point to each other. 

    Arguments 
    incomingNode (user): Takes a user object
    currentUser  (user): Takes the local nodes user details

    """

    def secondNodeJoins(self, currentUser, incomingNode):
        # Sets the local finger tables predecessor and successor to the new incoming node
        self.dht.fingerTable.predecessor = incomingNode
        self.dht.fingerTable.successor = incomingNode
        self.dht.fingerTable.nodeid = currentUser
        # Writes this updated DHT information to file
        self.dht.writeDHTInformation()

        # Send back the inverse of our finger table to the new node. E.g.the local node is the bootstrapping nodes successor and predecessor
        dhtForNewNode = DHT()
        dhtForNewNode.fingerTable.successor = currentUser
        dhtForNewNode.fingerTable.predecessor = currentUser
        dhtForNewNode.fingerTable.nodeid = incomingNode

        # Establish a TCP connection and return the calculated finger table
        reactor.connectTCP(incomingNode['ip'], int(incomingNode['port']),
                           DHTReturn(dhtForNewNode.fingerTable.toDict()))

    """
    startOfDHTEdgeCase()

    startOfDHTEdgeCase - Handles the edge case if the bootstrapping node is at the start of the ring, its predecessor is greater than itself. This requires a special bootstrap procedure

    Arguments 
    incomingNode (user): Takes a user object

    """

    def startOfDHTEdgeCase(self, incomingNode):
        logging.info("Edge case start of DHT")

        if (incomingNode['nodeid'] < self.dht.fingerTable.nodeid['nodeid'] and incomingNode['nodeid'] < self.dht.fingerTable.predecessor['nodeid']):
            self.newPredecessor(incomingNode)

        elif (incomingNode['nodeid'] > self.dht.fingerTable.nodeid['nodeid'] and incomingNode['nodeid'] < self.dht.fingerTable.successor['nodeid']):
            self.newSuccessor(incomingNode)

        elif (incomingNode['nodeid'] > self.dht.fingerTable.successor['nodeid']):
            self.fowardRequestToSuccessor(incomingNode)


        elif (incomingNode['nodeid'] < self.dht.fingerTable.predecessor['nodeid']):
            self.newSuccessor(incomingNode)

        else:
            logging.error("ERROR should never reach here")

    """
    endOfDHTEdgeCase()

    endOfDHTEdgeCase - Handles the edge case if the bootstrapping node is at the end of the ring, its successor is less than itself. This requires a special bootstrap procedure

    Arguments 
    incomingNode (user): Takes a user object

    """

    def endOfDHTEdgeCase(self, incomingNode):

        # Check that the incoming node is both greater than the local node id and greater than the local nodes successor
        if(incomingNode['nodeid'] > self.dht.fingerTable.nodeid['nodeid'] and incomingNode['nodeid'] > self.dht.fingerTable.successor['nodeid']):
            self.newSuccessor(incomingNode)

        # Check that the incoming node is both less than the local node id and greater than the local nodes successor
        elif (incomingNode['nodeid'] < self.dht.fingerTable.nodeid['nodeid'] and incomingNode['nodeid'] > self.dht.fingerTable.predecessor['nodeid']):
            self.newPredecessor(incomingNode)

        # Check that the incoming node is greater than the local nodes successor, if so the request cannot be satisied locally so is forwarded to the successor
        elif(incomingNode['nodeid'] > self.dht.fingerTable.successor['nodeid']):
            self.fowardRequestToSuccessor(incomingNode)

        # Check if the incoming node is less than the local nodes predecessor, if so this node now belongs at the start of the DHT and is the local nodes new successpr
        elif (incomingNode['nodeid'] < self.dht.fingerTable.predecessor['nodeid']):
            self.newSuccessor(incomingNode)

        else:
            logging.error("ERROR should never reach here")

    """
    newPredecessor(), newSuccessor(), fowardRequestTOPredecessor(), fowardRequestToSuccessor()

    The following methods represent standard DHT behaviour and will be the most commonly invoked. 
    
    newPredecessor - The incoming node is less than the local node ID and greater than the current predecessor
    newSuccessor -  The incoming node is greater than the local node ID and less than the current successor
    fowardRequestToPredecessor - The incoming node is less than the local predecessor
    fowardRequestToSuccessor - The incoming node is greater than the local successor
    
    Arguments 
    incomingNode (user): Takes a user object

    """

    def newPredecessor(self, incomingNode):

        """This handles the remote node"""
        dhtForNewNode = DHT()
        dhtForNewNode.fingerTable.successor = self.dht.fingerTable.nodeid
        dhtForNewNode.fingerTable.nodeid = incomingNode
        dhtForNewNode.fingerTable.predecessor = self.dht.fingerTable.predecessor

        # Stabilise the network by updating old predecessor with its new successor
        reactor.connectTCP(self.dht.fingerTable.predecessor['ip'],
                           int(self.dht.fingerTable.predecessor['port']), DHTSuccessorUpdate(incomingNode))

        # Provide the node being bootstrapped with network information allowing it to position itself in the network

        reactor.connectTCP(incomingNode['ip'], int(incomingNode['port']),
                           DHTReturn(dhtForNewNode.fingerTable.toDict()))

        # Update the local node with its new predecessor
        self.dht.fingerTable.predecessor = incomingNode
        # Write this new information to file
        self.dht.writeDHTInformation()

    def newSuccessor(self, incomingNode):

        # Create a finger table for the node being bootstrapped
        dhtForNewNode = DHT()
        dhtForNewNode.fingerTable.successor = self.dht.fingerTable.successor
        dhtForNewNode.fingerTable.nodeid = incomingNode
        dhtForNewNode.fingerTable.predecessor = self.dht.fingerTable.nodeid

        # Stabilise the network by updating old successor with its new predecessor

        reactor.connectTCP(self.dht.fingerTable.successor['ip'],
                           int(self.dht.fingerTable.successor['port']), DHTPredecessorUpdate(incomingNode))

        # Provide the node being bootstrapped with network information allowing it to position itself in the network

        reactor.connectTCP(incomingNode['ip'], int(incomingNode['port']),
                           DHTReturn(dhtForNewNode.fingerTable.toDict()))

        # Update the local node with its new successor
        self.dht.fingerTable.successor = incomingNode
        # Write this new information to file
        self.dht.writeDHTInformation()

    def fowardRequestToPredecessor(self, incomingNode):
        logging.info("DHT position request. Can't satisfy sending to the previous node")
        incomingNode = json.dumps(incomingNode)
        reactor.connectTCP(self.dht.fingerTable.predecessor['ip'],
                           int(self.dht.fingerTable.predecessor['port']), DHTRegistration(incomingNode))

    def fowardRequestToSuccessor(self, incomingNode):
        logging.info("DHT position request. Can't satisfy sending to the next node")
        incomingNode = json.dumps(incomingNode)
        reactor.connectTCP(self.dht.fingerTable.successor['ip'],
                           int(self.dht.fingerTable.successor['port']), DHTRegistration(incomingNode))

    """
    DHTInformationSearch
    
    This method encapsulates all the logic required to search the DHT for data
    Attempts to match a SHA256 value to those in its local stoage
    If the request can not be satisifed it will determine which direction the request
    should be fowarded in

    Arguments 
    userSearchRequest (dict): Takes a dict containing the return address of the search
    and a SHA256 encoding of a users pseudonym which is used in the search
    
    """

    def DHTInformationSearch(self, userSearchRequest):
        logging.info("DHT information search")

        # Remove the network command from the message
        userSearchRequest = userSearchRequest.replace("==DHTSEARCH==", "")
        self.dht.loadDHTInformation()
        # Convert the request into a dictionary object
        userSearchRequest = ast.literal_eval(userSearchRequest)

        logging.info("Incoming DHT Search Data" + str(userSearchRequest))

        # Check to see if the hash value is equivalent to our successors ID
        if (self.dht.fingerTable.successor['nodeid'] == userSearchRequest['username']):
            logging.info("Found user returning object successor")
            userfound = self.dht.fingerTable.successor
            reactor.connectTCP(userSearchRequest['ip'],
                               int(userSearchRequest['port']), DHTSearchReturn(userfound))

        # Check to see if the hash value is equivalent to our predecessors ID
        elif (self.dht.fingerTable.predecessor['nodeid'] == userSearchRequest['username']):
            logging.info("Found user returning object predecessor")
            userfound = self.dht.fingerTable.predecessor
            reactor.connectTCP(userSearchRequest['ip'],
                               int(userSearchRequest['port']), DHTSearchReturn(userfound))

        # Check to see if the hash value is equivalent to our own ID
        elif (self.dht.fingerTable.nodeid['nodeid'] == userSearchRequest['username']):
            logging.info("Found user is my NODEID!")
            userfound = self.dht.fingerTable.nodeid
            reactor.connectTCP(userSearchRequest['ip'],
                               int(userSearchRequest['port']), DHTSearchReturn(userfound))

        # Check if the hash value is greater than our ID, if so send to our successor
        elif (userSearchRequest['username'] > self.dht.fingerTable.nodeid['nodeid']):
            logging.info("Sending information request to our successor cannot satisfy request")
            reactor.connectTCP(self.dht.fingerTable.successor['ip'], int(self.dht.fingerTable.successor['port']),
                               DHTSearch(userSearchRequest))

        # Check if the hash value is greater than our ID, if so send to our successor
        elif (userSearchRequest['username'] < self.dht.fingerTable.nodeid['nodeid']):
            logging.info("Sending information request to our predecessor cannot satisfy request")
            reactor.connectTCP(self.dht.fingerTable.predecessor['ip'], int(self.dht.fingerTable.predecessor['port']),
                               DHTSearch(userSearchRequest))

        # Should not be possible to reach this position
        else:
            logging.error("ERROR! Should never reach this location")