Beispiel #1
0
class MixNode(NetworkPart):
    """
    The class mainly consists of:
    - the key manager, whose keys are used to blind and un-blind messages/responses
    - 3 cyclic group dual arrays (array + inverse) used to blind and un-blind messages
    - the permutation array (self.perm) and its inverse (self.permInverse)
    - the tuple (key share in cyclic group, exponent of key share): self.e
    - the message components that the last node stores (forward and return): self.mixMessageComponents
    - the decryption shares that every node computes (forward and return): self.decryptionShare
    - the commitments (forward and return) of the last node stored by the other nodes: self.realMixCommitment
    """
    def __init__(self, b):
        NetworkPart.__init__(self)
        self.sharedKey = None
        self.b = b

        self.r = CyclicGroupDualArray(self.b)
        s = CyclicGroupDualArray(self.b)
        s1 = CyclicGroupDualArray(self.b)
        self.S = {'FOR': s, 'RET': s1}

        perm = range(0, b)
        shuffle(perm)
        permInverse = inversePermute(perm)
        self.permutation = {'FOR': perm, 'RET': permInverse}

        self.e = CyclicGroup.randomPair()

        self.mixMessageComponents = {'FOR': None, 'RET': None}
        self.preMixCallback = {
            'FOR': Callback.PRE_FOR_MIX,
            'RET': Callback.PRE_RET_MIX
        }
        self.realMixCallback = {
            'FOR': Callback.REAL_FOR_MIX,
            'RET': Callback.REAL_RET_MIX
        }
        self.mixCommitCallback = {
            'FOR': Callback.REAL_FOR_MIX_COMMIT,
            'RET': Callback.REAL_RET_MIX_COMMIT
        }
        self.prePostProcessCallback = {
            'FOR': Callback.PRE_FOR_POSTPROCESS,
            'RET': Callback.PRE_RET_POSTPROCESS
        }
        self.realPostProcessCallback = {
            'FOR': Callback.REAL_FOR_POSTPROCESS,
            'RET': Callback.REAL_RET_POSTPROCESS
        }

        self.decryptionShare = {'FOR': None, 'RET': None}
        self.realMixCommitment = {'FOR': None, 'RET': None}
        self.senders = None

        self.keyManager = KeyManager()

        self.associateCallback(Callback.KEY_SHARE, self.storeSharedKey, 1)
        self.associateCallback(Callback.PRE_FOR_MIX, self.preForwardMix, 1)
        self.associateCallback(Callback.PRE_FOR_POSTPROCESS,
                               self.preForwardPostProcess, 1)
        self.associateCallback(Callback.PRE_RET_MIX, self.preReturnMix, 1)
        self.associateCallback(Callback.PRE_RET_POSTPROCESS,
                               self.preReturnPostProcess, 1)
        self.associateCallback(Callback.KEY_USER, self.sendUserKey)
        self.associateCallback(Callback.REAL_FOR_PREPROCESS,
                               self.realForPreProcess)
        self.associateCallback(Callback.REAL_FOR_MIX, self.realForMix)
        self.associateCallback(Callback.REAL_FOR_MIX_COMMIT,
                               self.realForMixCommit)
        self.associateCallback(Callback.REAL_RET_MIX, self.realRetMix)
        self.associateCallback(Callback.REAL_RET_MIX_COMMIT,
                               self.realRetMixCommit)

    # called in network.init() before the precomputation phase, when the public key is collectively constructed
    def computeSecretShare(self):
        self.network.sendToNH(Message(Callback.KEY_SHARE, self.e[1]))

    # store the above shared key (that the NH broadcasts)
    def storeSharedKey(self, message):
        self.sharedKey = message.payload
        return Status.OK

    # callback that performs dummy key exchange with a user
    def sendUserKey(self, message):
        userId = message.payload
        self.keyManager.addSeeds(userId)
        payload = [
            self.id,
            self.keyManager.getSeed(userId, KeyManager.MESSAGE),
            self.keyManager.getSeed(userId, KeyManager.RESPONSE)
        ]
        self.network.sendToUser(userId, Message(Callback.KEY_USER, payload))
        return Status.OK

    # called in network.init(), initiates the precomputation process
    def precompute(self):
        self.network.sendToNH(
            Message(Callback.PRE_FOR_PREPROCESS,
                    self.r.inverse.encrypt(self.sharedKey).vector))

    # callback that performs the mixing process in the the forward path of precomputation phase
    def preForwardMix(self, message):
        status = self.__preMix(message=ElGamalVector(vector=message.payload),
                               path='FOR')

        # the last node initiates the matching return path
        if self.__finalNode('FOR'):
            self.network.sendToPreviousNode(
                self.id,
                Message(Callback.PRE_RET_MIX,
                        self.S['RET'].inverse.encrypt(self.sharedKey).vector))
        return status

    # callback that computes the decryption share of the forward path
    def preForwardPostProcess(self, message):
        return self.__prePostProcess(
            randomComponents=CyclicGroupVector(vector=message.payload),
            path='FOR')

    # callback that performs mixing process in the the return path of precomputation phase
    def preReturnMix(self, message):
        return self.__preMix(message=ElGamalVector(vector=message.payload),
                             path='RET')

    # callback that computes the decryption share of the return path
    def preReturnPostProcess(self, message):
        return self.__prePostProcess(
            randomComponents=CyclicGroupVector(vector=message.payload),
            path='RET')

    # callback that computes the value that gradually replaces "keys" with "r" values in blind "messages"
    def realForPreProcess(self, message):
        self.senders = message.payload
        cyclicVector = self.keyManager.getNextKeys(ids=self.senders,
                                                   type=KeyManager.MESSAGE,
                                                   inverse=False)
        product = CyclicGroupVector.multiply(cyclicVector, self.r.array)
        self.network.sendToNH(
            Message(Callback.REAL_FOR_PREPROCESS, product.vector))
        return Status.OK

    # callback that performs the mixing process in the the forward path of real-time phase
    def realForMix(self, message):
        return self.__realMix(message=Vector(
            vector=[CyclicGroupVector(vector=v) for v in message.payload]),
                              path='FOR')

    # callback that performs the mixing process in the the return path of real-time phase
    def realRetMix(self, message):
        def tempProcess(temp):
            return self.__returnPathKeyBlinding(temp)

        return self.__realMix(message=Vector(
            vector=[CyclicGroupVector(vector=v) for v in message.payload]),
                              path='RET',
                              tempProcess=tempProcess)

    # callback that stores the commitment of the last node regarding
    # the mixing process in the the forward path of real-time phase
    def realForMixCommit(self, message):
        def resultProcess(decrShare):
            return decrShare

        return self.__realMixCommit(message=Vector(
            vector=[CyclicGroupVector(vector=v) for v in message.payload]),
                                    path='FOR',
                                    resultProcess=resultProcess)

    # callback that stores the commitment of the last node regarding
    # the mixing process in the the return path of real-time phase
    def realRetMixCommit(self, message):
        def resultProcess(decrShare):
            return self.__returnPathKeyBlinding(decrShare)

        return self.__realMixCommit(message=Vector(
            vector=[CyclicGroupVector(vector=v) for v in message.payload]),
                                    path='RET',
                                    resultProcess=resultProcess)

    # performs the mixing process of the precomputation phase
    def __preMix(self, message, path):
        result = ElGamalVector.multiply(
            message.permute(self.permutation[path]),
            self.S[path].inverse.encrypt(self.sharedKey))
        if not self.__finalNode(path):
            self.__toNextNode(
                path, Message(self.preMixCallback[path], result.vector))
        else:
            self.mixMessageComponents[path] = result.messageComponents()
            message = Message(self.prePostProcessCallback[path],
                              result.randomComponents().vector)
            self.network.broadcastToNodes(self.id, message)
            self.receive(message.toJSON())
        return Status.OK

    # performs the mixing process of the real-time phase
    def __realMix(self, message, path, tempProcess=None):
        temp = message.permute(self.permutation[path])
        result = Vector([
            CyclicGroupVector.scalarMultiply(temp.at(i),
                                             self.S[path].array.at(i))
            for i in range(0, self.b)
        ])
        if not self.__finalNode(path):
            self.__toNextNode(
                path,
                Message(self.realMixCallback[path],
                        [v.vector for v in result.vector]))
        else:
            self.network.broadcastToNodes(
                self.id,
                Message(self.mixCommitCallback[path],
                        [v.vector for v in result.vector]))
            temp = CyclicGroupVector.multiply(self.decryptionShare[path],
                                              self.mixMessageComponents[path])
            if tempProcess is not None:
                temp = tempProcess(temp)

            result = Vector([
                CyclicGroupVector.scalarMultiply(result.at(i), temp.at(i))
                for i in range(0, self.b)
            ])
            self.network.sendToNH(
                Message(self.realPostProcessCallback[path],
                        [True, [v.vector for v in result.vector]]))
        return Status.OK

    # computes the decryption share
    def __prePostProcess(self, randomComponents, path):
        self.decryptionShare[path] = randomComponents.exp(self.e[0]).inverse()
        return Status.OK

    # the blinding proceess that the mix node performs in the return path (that involves user keys)
    def __returnPathKeyBlinding(self, temp):
        return CyclicGroupVector.multiply(
            temp,
            self.keyManager.getNextKeys(ids=self.senders,
                                        type=KeyManager.RESPONSE,
                                        inverse=False))

    # stores the mixing commitment
    def __realMixCommit(self, message, path, resultProcess):
        self.realMixCommitment[path] = message
        result = resultProcess(self.decryptionShare[path])
        self.network.sendToNH(
            Message(self.realPostProcessCallback[path],
                    [False, result.vector]))
        return Status.OK

    def __toNextNode(self, path, message):
        if path == 'FOR':
            return self.network.sendToNextNode(self.id, message)
        elif path == 'RET':
            return self.network.sendToPreviousNode(self.id, message)
        else:
            raise Exception("Wrong path!")

    def __finalNode(self, path):
        if path == 'FOR':
            return self.network.isLastNode(self.id)
        elif path == 'RET':
            return self.network.isFirstNode(self.id)
        else:
            raise Exception("Wrong path!")
Beispiel #2
0
class User(NetworkPart):
    """
    The class consists of:
    - the key manager, whose keys are used to blind and un-blind messages/responses
    - the response handler, that reads a message and produces a response
    - the LAST message and response the user sent and received (or received and sent)
    """
    def __init__(self, name, handler=None):
        NetworkPart.__init__(self)

        self.name = name
        self.messageSent = None
        self.messageGot = None

        self.responseSent = None
        self.responseGot = None

        self.keyManager = KeyManager()

        if handler is None:
            self.setCallbackHandler(BasicHandler())
        else:
            self.setCallbackHandler(handler)

        self.associateCallback(Callback.KEY_USER, self.storeKeyUser)
        self.associateCallback(Callback.USER_MESSAGE, self.readMessage)
        self.associateCallback(Callback.USER_MESSAGE_STATUS,
                               self.messageStatus)
        self.associateCallback(Callback.USER_RESPONSE, self.readResponse)

    # the key establishment takes place here
    def setUp(self):
        self.network.broadcastToNodes(self.id,
                                      Message(Callback.KEY_USER, self.id))

    def setCallbackHandler(self, callbackHandler):
        self.callbackHandler = callbackHandler

    # store the keys that a node sent
    def storeKeyUser(self, message):
        nodeId = message.payload[0]
        messageKey = message.payload[1]
        responseKey = message.payload[2]
        self.keyManager.addSeeds(nodeId, (messageKey, responseKey))
        self.callbackHandler.setUp(self)
        return Status.OK

    # send a message to a user through the mixnet
    def sendMessage(self, userId, messageId, messageVector):
        # store the message for future reference
        self.messageSent = messageVector.copyVector()

        # append the userId (the receiver)
        # the NH will read this id (after the message is un-blinded) and route the message accordingly
        messageVector.append(userId)

        # blind the message with the combined key and send it to the NH
        combinedKey = self.keyManager.getCombinedKey(type=KeyManager.MESSAGE,
                                                     inverse=True)
        blindMessage = CyclicGroupVector.scalarMultiply(
            messageVector, combinedKey)
        self.network.sendToNH(
            Message(Callback.USER_MESSAGE,
                    [self.id, messageId, blindMessage.vector]))

    # read a message from the mixnet and send a response
    def readMessage(self, message):
        index = message.payload[0]

        # the payload is the message (mapped to cyclic group members)
        messageVector = CyclicGroupVector(vector=message.payload[1])

        # compute a response and send it to the mixnet
        responseVector = self.callbackHandler.messageHandler(
            self, messageVector)
        # store message and response for future reference
        self.messageGot = messageVector.copyVector()
        self.responseSent = responseVector.copyVector()

        self.network.sendToNH(
            Message(Callback.USER_RESPONSE, [index, responseVector.vector]))
        return Status.OK

    # read a response (after I sent a message)
    def readResponse(self, message):
        # un-blind the response and store it for future reference
        responseVector = CyclicGroupVector.scalarMultiply(
            CyclicGroupVector(vector=message.payload),
            self.keyManager.getCombinedKey(type=KeyManager.RESPONSE,
                                           inverse=True))
        self.callbackHandler.responseHandler(self, responseVector)
        self.responseGot = responseVector.copyVector()
        return Status.OK

    def messageStatus(self, message):
        messageId = message.payload[0]
        status = message.payload[1]
        self.callbackHandler.messageStatusHandler(self, messageId, status)
        return Status.OK