Exemple #1
0
def generateQubits():
    # Creating registers with n qubits
    qr = QuantumRegister(chunk, name='qr')
    cr = ClassicalRegister(chunk, name='cr')

    # Quantum circuit for alice state
    alice = QuantumCircuit(qr, cr, name='Alice')

    # Generate a random number in the range of available qubits [0,65536))
    temp_alice_key = randomStringGen(chunk)
    #app.logger.info("key: ", temp_alice_key)

    # Switch randomly about half qubits to diagonal basis
    alice_table = np.array([])
    for index in range(len(qr)):
        if 0.5 < int(randomStringGen(1)):
            # change to diagonal basis
            alice.h(qr[index])
            alice_table = np.append(alice_table, 'X')
        else:
            # stay in computational basis
            alice_table = np.append(alice_table, 'Z')

    # Reverse basis table
    alice_table = alice_table[::-1]

    # Generate a statevector initialised with the random generated string
    sve = Statevector.from_label(temp_alice_key)
    # Evolve stetavector in generated circuit
    qubits = sve.evolve(alice)

    # return quantum circuit, basis table and temporary key
    return qubits, alice_table, temp_alice_key
Exemple #2
0
def compareBasis():
    global alice_key
    global alice_table

    res = eval(request.data)

    if authenticationMethod == 'aesgcm':
        cypher_table = res[0]
        keyID = res[1]
        nonce = res[2]
        # get key and TTL
        key, keyID, TTL = getPresharedKey(keyID)
        if key is None:
            return "Error: unable to get required preshared key.", 400
        aesgcm = AESGCM(bytes(key, 'utf-8'))
        bob_table = aesgcm.decrypt(bytes(nonce, 'utf-8'), cypher_table, None)
        # convert bytes back to numpy array
        bob_table = np.frombuffer(bob_table, dtype=alice_table.dtype)

    else:
        bob_table = pickle.loads(res[0])
        tableSign = res[1]

        # check that table was actually sent from Bob
        if not sphincs.verify(bob_table.tobytes(), tableSign,
                              eval(prefs['auth_key']['peerPublicKey'])):
            app.logger.error("Table comparison failed due to wrong signature!")
            return "Unauthorized", 401

    keep = []
    discard = []
    for qubit, basis in enumerate(zip(bob_table, alice_table)):
        if basis[0] == basis[1]:
            #print("Same choice for qubit: {}, basis: {}" .format(qubit, basis[0]))
            keep.append(qubit)
        else:
            #print("Different choice for qubit: {}, Alice has {}, Bob has {}" .format(qubit, basis[0], basis[1]))
            discard.append(qubit)

    #print('Percentage of qubits to be discarded according to table comparison: ', len(keep)/chunk)

    # get new key
    alice_key += [temp_alice_key[qubit] for qubit in keep]

    if authenticationMethod == 'aesgcm':
        nonce = randomStringGen(12)
        cypherTable = aesgcm.encrypt(bytes(nonce, 'utf-8'),
                                     alice_table.tobytes(), None)
        return repr([cypherTable, nonce])
    else:
        # prepare reply
        reply = alice_table
        # sign reply to let Bob trust us
        repSign = sphincs.sign(alice_table.tobytes(),
                               eval(prefs['auth_key']['privateKey']))
        # reset alice_table for next comparisons
        alice_table = np.array([])

        return repr([pickle.dumps(reply), repSign])
Exemple #3
0
def getBasis():
    length = request.args.get('length')
    if length is None:
        # error - this should not happen
        return "Bad request", 400
    length = int(length)
    i = 0
    basisTable = []
    while i < length:
        randNo = randomStringGen(2)
        # convert string into bytes
        randNo = int(randNo, 2)
        # we need a number in range 1 to 3 - skip 0
        if randNo > 0:
            basisTable.append(randNo)
            i = i + 1
    return repr(basisTable)
Exemple #4
0
def forwardQubits():
	qubits = pickle.loads(request.data)
	destination = request.args.get('destination')
	if destination is None:
		# error - this should not happen
		return "Bad request", 400
	req = destination + '/sendRegister'
	keyLen = request.args.get('keyLen')
	if keyLen is not None:
		req = req + '?keyLen=' + keyLen

	# Do attacks on quantum channel here
	if interceptAndResend:
		chunk = qubits[0].num_qubits
		eveTable = np.array([])
		eveMeasurements = []
		for i in range(len(qubits)):
			# generate a quantum circuit random basis for measurements
			qr = QuantumRegister(chunk, name='qr')
			cr = ClassicalRegister(chunk, name='cr')
			circuit = QuantumCircuit(qr, cr, name='qcircuit')

			basisChoice = randomStringGen(chunk)
			# randomly chose basis for measurement
			table = np.array([])
			for index, bit in enumerate(basisChoice): 
				if 0.5 < int(bit):
					circuit.h(qr[index])
					table = np.append(table, 'X')
				else:
					table = np.append(table, 'Z')

			# Reverse table
			table = table[::-1]
			eveTable = np.append(eveTable, table)
			eveMeasurements.append(qubits[i].evolve(circuit))

		# replace data to be forwarded
		qubits = eveMeasurements

	# forward request and related reply
	x = requests.post(req, data = pickle.dumps(qubits))
	return x.content, x.status_code
Exemple #5
0
def getQuantumKey():
    global temp_alice_key
    global alice_key
    global alice_table
    global key_length

    bob = pickle.loads(request.data)
    #print(alice)

    # check if this is a new key
    new = request.args.get('newKey')
    if (new == 'true'):
        requestIP = request.remote_addr
        # retrieve information about this destination if any
        db = mysql.connector.connect(
            host=str(prefs['internal_db']['host']),
            user=str(prefs['internal_db']['user']),
            passwd=str(prefs['internal_db']['passwd']),
            database=str(prefs['internal_db']['database']),
            autocommit=True)
        cursor = db.cursor()
        # use a lock to access database to avoid concurrency access
        cursor.execute("LOCK TABLES " + str(prefs['simulator']['table']) +
                       " WRITE")
        try:
            if DEBUG:
                print("SELECT * FROM " + str(prefs['simulator']['table']) +
                      " WHERE requestIP = '%s'" % requestIP)
            cursor.execute("SELECT * FROM " +
                           str(prefs['simulator']['table']) +
                           " WHERE requestIP = '%s'" % requestIP)
            result = cursor.fetchone()
            if result is not None:
                # an exchange for this key is already in progress, return an error
                # release db lock
                cursor.execute("UNLOCK TABLES")
                return "Error", 400
            else:
                # a new key exchange can be started
                # insert information
                if DEBUG:
                    print(
                        "INSERT INTO " + str(prefs['simulator']['table']) +
                        " (requestIP, complete, exchangedKey, verified) VALUES ('%s', True, %s, True)"
                        % (requestIP, str(bob)))
                cursor.execute(
                    "INSERT INTO " + str(prefs['simulator']['table']) +
                    " (requestIP, complete, exchangedKey, verified) VALUES ('%s', True, %s, True)"
                    % (requestIP, str(bob)))
                # save key in vault
                client = hvac.Client(url='http://' + prefs['vault']['host'] +
                                     ':8200')
                client.token = prefs['vault']['token']
                client.secrets.kv.v2.create_or_update_secret(
                    path='currentKey',
                    secret=dict(key=bob),
                )
                # release db lock
                cursor.execute("UNLOCK TABLES")
                return "OK", 200
        except Exception as e:
            # always release lock before quit
            cursor.execute("UNLOCK TABLES")
            raise (e)
        # new key requested - reset all variables
        alice_table = []
        alice_key = []
        # get key length
        key_length = int(request.args.get('keyLen'))
        app.logger.info(
            'New key exchange requested from client. Desired key length %s' %
            str(key_length))

    # try to generate a quantum circuit with the given quantum register
    qr = QuantumRegister(chunk, name='qr')
    cr = ClassicalRegister(chunk, name='cr')
    # Quantum circuit for bob state
    alice = QuantumCircuit(qr, cr, name='Alice')
    SendState(bob, alice, 'Bob', qr)

    # randomly chose basis for measurement
    for index in range(len(qr)):
        if 0.5 < int(randomStringGen(1)):  # With 50% chance...
            alice.h(qr[index])  # ...change to diagonal basis
            alice_table.append('X')
        else:
            alice_table.append('Z')

    # Measure all qubits
    for index in range(len(qr)):
        alice.measure(qr[index], cr[index])

    # Execute the quantum circuit
    backend = BasicAer.get_backend('qasm_simulator')
    result = execute(alice, backend=backend, shots=1).result()

    # Result of the measure is Alice's key candidate
    temp_alice_key = list(result.get_counts(alice))[0]
    temp_alice_key = temp_alice_key[::
                                    -1]  # key is reversed so that first qubit is the first element of the list
    #print(temp_alice_key)

    return "OK"
Exemple #6
0
    def exchangeKey(self,
                    key_length,
                    destination='http://localhost:4000',
                    timeout=0,
                    eve=False):
        # delay the start of the exchange of a random number of seconds (between 0 and 8)
        randNo = randomStringGen(3)
        # convert bit string into bytes
        randNo = int(randNo, 2)
        time.sleep(randNo)

        app.logger.info('Starting key exchange. Desired key length: %s' %
                        str(key_length))
        # 1/3 of the key needs to be exchanged in order to verify key
        # that part of the key cannot be used anymore after key verification
        # generate 1/3 more than key_length that will then be exchanged
        # in this way final key length will be as equals as key_length
        key_length = int(key_length)
        generateLength = round(key_length + (key_length / 3))

        # check if a key has already been exchanged with desired destination
        destAddr = str(destination.split(':')[1][2:])
        db = mysql.connector.connect(
            host=str(prefs['internal_db']['host']),
            user=str(prefs['internal_db']['user']),
            passwd=str(prefs['internal_db']['passwd']),
            database=str(prefs['internal_db']['database']),
            autocommit=True)
        cursor = db.cursor()
        # use a lock to access database to avoid concurrency access
        cursor.execute("LOCK TABLES " + str(prefs['simulator']['table']) +
                       " WRITE")
        try:
            if DEBUG:
                print("SELECT * FROM " + str(prefs['simulator']['table']) +
                      " WHERE requestIP = '%s'" % (destAddr))
            cursor.execute("SELECT * FROM " +
                           str(prefs['simulator']['table']) +
                           " WHERE requestIP = '%s'" % (destAddr))
            result = cursor.fetchone()
            if result is not None:
                if DEBUG:
                    print("match found")
                # a key with this id has already been requested from server side
                # wait until the whole key is received
                # release lock during wait
                cursor.execute("UNLOCK TABLES")
                start_time = current_time()
                while bool(result[1]) is not True:
                    if DEBUG:
                        print("key exchange not completed yet")
                        print("SELECT * FROM " +
                              str(prefs['simulator']['table']) +
                              " WHERE requestIP = '%s'" % (destAddr))
                    cursor.execute("SELECT * FROM " +
                                   str(prefs['simulator']['table']) +
                                   " WHERE requestIP = '%s'" % (destAddr))
                    result = cursor.fetchone()
                    if current_time() > start_time + timeout:
                        # timeout elapsed - clean requests list
                        if DEBUG:
                            print("DELETE FROM " +
                                  str(prefs['simulator']['table']) +
                                  " WHERE `requestIP` = '%s'" % (destAddr))
                        cursor.execute("DELETE FROM " +
                                       str(prefs['simulator']['table']) +
                                       " WHERE `requestIP` = '%s'" %
                                       (destAddr))
                        return None, 4
                if DEBUG:
                    print("result found - returning it")
                # now key exchange is complete
                verified = result[3]
                # key is saved in vault
                client = hvac.Client(url='http://' + prefs['vault']['host'] +
                                     ':8200')
                client.token = prefs['vault']['token']
                response = client.secrets.kv.read_secret_version(
                    path='currentKey')
                key = response['data']['data']['key']
                # delete key once returned
                client.secrets.kv.delete_metadata_and_all_versions(
                    'currentKey')
                # once key has been exchange, delete its data from this module
                cursor.execute("LOCK TABLES " +
                               str(prefs['simulator']['table']) + " WRITE")
                if DEBUG:
                    print("DELETE FROM " + str(prefs['simulator']['table']) +
                          " WHERE `requestIP` = '%s'" % (destAddr))
                cursor.execute("DELETE FROM " +
                               str(prefs['simulator']['table']) +
                               " WHERE `requestIP` = '%s'" % (destAddr))
                cursor.execute("UNLOCK TABLES")
                return key, verified
            else:
                if DEBUG:
                    print("match not found - start key exchange")
                    print(
                        "INSERT INTO " + str(prefs['simulator']['table']) +
                        " (requestIP, complete, exchangedKey, verified) VALUES ('%s', False, NULL, False)"
                        % (destAddr))
                # start key exchange - save information
                cursor.execute(
                    "INSERT INTO " + str(prefs['simulator']['table']) +
                    " (requestIP, complete, exchangedKey, verified) VALUES ('%s', False, NULL, False)"
                    % (destAddr))
            # release lock
            cursor.execute("UNLOCK TABLES")
        except Exception as e:
            # error occurred - clean requests list
            if DEBUG:
                print("DELETE FROM " + str(prefs['simulator']['table']) +
                      " WHERE `requestIP` = '%s'" % (destAddr))
            cursor.execute("DELETE FROM " + str(prefs['simulator']['table']) +
                           " WHERE `requestIP` = '%s'" % (destAddr))
            # always release lock before quit
            cursor.execute("UNLOCK TABLES")
            raise (e)

        # [cr] here
        # send key
        alice_key = randomStringGen(key_length)
        x = requests.post(destination + '/sendRegister?newKey=true&keyLen=' +
                          str(key_length),
                          data=pickle.dumps(alice_key))
        if x.status_code != 200:
            # get key chunk failed
            # since exchange failed, delete record from db
            if DEBUG:
                print("DELETE FROM " + str(prefs['simulator']['table']) +
                      " WHERE requestIP = '%s'" % (destAddr))
            cursor.execute("DELETE FROM " + str(prefs['simulator']['table']) +
                           " WHERE requestIP = '%s'" % (destAddr))
            return None, False
        current_len = len(alice_key)

        app.logger.info("Key exchange completed - new key: %s" % alice_key)
        app.logger.info("key len: %s" % str(len(alice_key)))

        # delete info once key exchange is complete
        try:
            cursor.execute("LOCK TABLES " + str(prefs['simulator']['table']) +
                           " WRITE")
            if DEBUG:
                print("DELETE FROM " + str(prefs['simulator']['table']) +
                      " WHERE `requestIP` = '%s'" % (destAddr))
            cursor.execute("DELETE FROM " + str(prefs['simulator']['table']) +
                           " WHERE `requestIP` = '%s'" % (destAddr))
            cursor.execute("UNLOCK TABLES")
        except Exception as e:
            # always release lock before quit
            cursor.execute("UNLOCK TABLES")
            raise (e)

        return alice_key, True
        app.logger.info("\nKey exchange has been successfull")
Exemple #7
0
def generateQubits(eve=False):
    # Creating registers with n qubits
    qr = QuantumRegister(chunk, name='qr')
    cr = ClassicalRegister(chunk, name='cr')

    # Quantum circuit for alice state
    alice = QuantumCircuit(qr, cr, name='Alice')

    # Generate a random number in the range of available qubits [0,65536))
    temp_alice_key = randomStringGen(chunk)
    #app.logger.info("key: ", temp_alice_key)

    # Encode key as alice qubits
    # IBM's qubits are all set to |0> initially
    for index, digit in enumerate(temp_alice_key):
        if digit == '1':
            alice.x(qr[index])  # if key has a '1', change state to |1>

    # Switch randomly about half qubits to diagonal basis
    alice_table = []  # Create empty basis table
    for index in range(
            len(qr)):  # BUG: enumerate(q) raises an out of range error
        if 0.5 < int(randomStringGen(1)):  # With 50% chance...
            alice.h(qr[index])  # ...change to diagonal basis
            alice_table.append('X')  # character for diagonal basis
        else:
            alice_table.append('Z')  # character for computational basis

    # Measure all qubits
    for index in range(len(qr)):
        alice.measure(qr[index], cr[index])

    # Execute the quantum circuit
    backend = BasicAer.get_backend('qasm_simulator')
    result = execute(alice, backend=backend, shots=1).result()

    if eve:
        # EVE is listening on the quantum channel - now she makes her measurements
        eve = QuantumCircuit(qr, cr, name='Eve')
        SendState(alice, eve, 'Alice', qr)
        eve_table = []
        for index in range(len(qr)):
            if 0.5 < int(randomStringGen(1)):
                eve.h(qr[index])
                eve_table.append('X')
            else:
                eve_table.append('Z')

        for index in range(len(qr)):
            eve.measure(qr[index], cr[index])

        # Execute (build and run) the quantum circuit
        backend = BasicAer.get_backend('qasm_simulator')
        result = execute(eve, backend=backend, shots=1).result()

        # Result of the measure is Eve's key
        eve_key = list(result.get_counts(eve))[0]
        eve_key = eve_key[::-1]
        # Eve's measurements changed qubits polarization - here we return Eve's circuit to remark this behavior
        return eve, alice_table, temp_alice_key

    # return quantum circuit, basis table and temporary key
    return alice, alice_table, temp_alice_key
Exemple #8
0
def verifyKey():
    global alice_key

    # key exchange completed
    req = eval(request.data)

    # verify key
    if authenticationMethod == 'aesgcm':
        keyID = req[0]
        chypherVK = req[1]
        nonceVK = req[2]
        chypherP = req[3]
        nonceP = req[4]
        # get key and TTL
        key, keyID, TTL = getPresharedKey(keyID)
        if key is None:
            return "Error: unable to get required preshared key.", 400
        aesgcm = AESGCM(bytes(key, 'utf-8'))
        bobKey = aesgcm.decrypt(bytes(nonceVK, 'utf-8'), chypherVK, None)
        picked = aesgcm.decrypt(bytes(nonceP, 'utf-8'), chypherP, None)
        bobKey = eval(bobKey)
        picked = eval(picked)
    # default to sphincs
    else:
        bobKey = req[0]
        keySign = req[1]
        picked = pickle.loads(req[2])
        pickSign = req[3]

        # check that message actually comes from Bob
        if not sphincs.verify(bytes(bobKey), keySign,
                              eval(prefs['auth_key']['peerPublicKey'])):
            app.logger.error("Key verification failed due to wrong signature!")
            return "Unauthorized", 401
        if not sphincs.verify(picked.tobytes(), pickSign,
                              eval(prefs['auth_key']['peerPublicKey'])):
            app.logger.error("Key verification failed due to wrong signature!")
            return "Unauthorized", 401

    # get part of the key to be used during key verification
    verifyingKey = []
    # add picked bit to verifyingKey and remove them from the key
    for i in sorted(picked, reverse=True):
        verifyingKey.append(int(alice_key[i]))
        del alice_key[i]

    # make sure key length is exactly equals to key_length
    alice_key = alice_key[:key_length]
    app.logger.info("New Alice's key: %s" % alice_key)
    app.logger.info("key len: %s" % str(len(alice_key)))

    # check that Alice and Bob have the same key
    acc = 0
    for bit in zip(verifyingKey, bobKey):
        if bit[0] == bit[1]:
            acc += 1

    app.logger.info('\nPercentage of similarity between the keys: %s' %
                    str(acc / len(verifyingKey)))

    if (acc // len(verifyingKey) == 1):
        verified = True
        app.logger.info("\nKey exchange has been successfull")
    else:
        verified = False
        app.logger.error(
            "\nKey exchange has been tampered! Check for eavesdropper or try again"
        )

    # save key
    db = mysql.connector.connect(host=str(prefs['internal_db']['host']),
                                 user=str(prefs['internal_db']['user']),
                                 passwd=str(prefs['internal_db']['passwd']),
                                 database=str(
                                     prefs['internal_db']['database']),
                                 autocommit=True)
    cursor = db.cursor()
    # use a lock to access database to avoid concurrency access
    cursor.execute("LOCK TABLES " + str(prefs['simulator']['table']) +
                   " WRITE")
    try:
        # save key in vault
        client = hvac.Client(url='http://' + prefs['vault']['host'])
        client.token = prefs['vault']['token']
        client.secrets.kv.v2.create_or_update_secret(
            path='currentKey',
            secret=dict(key=alice_key),
        )
        cursor.execute(
            'UPDATE ' + str(prefs["simulator"]["table"]) +
            ' SET complete = True, verified = %d WHERE requestIP = "%s"' %
            (verified, request.remote_addr))

        cursor.execute("UNLOCK TABLES")
    except Exception as e:
        # error occurred - clean requests list
        cursor.execute("DELETE FROM " + str(prefs['simulator']['table']) +
                       " WHERE `requestIP` = '%s'" % (request.remote_addr))

        # always release lock before quit
        cursor.execute("UNLOCK TABLES")
        raise (e)

    if authenticationMethod == 'aesgcm':
        nonce = randomStringGen(12)
        cypherVKey = aesgcm.encrypt(bytes(nonce, 'utf-8'),
                                    bytes(repr(verifyingKey), 'utf-8'), None)
        return repr([cypherVKey, nonce])
    # default to sphincs
    else:
        # prepare our reply - sign this key part
        keySignature = sphincs.sign(bytes(verifyingKey),
                                    eval(prefs['auth_key']['privateKey']))

    return repr([verifyingKey, keySignature])
Exemple #9
0
def getQuantumKey():
    global temp_alice_key
    global alice_key
    global alice_table
    global key_length

    requestIP = request.remote_addr
    # retrieve information about this destination if any
    db = mysql.connector.connect(host=str(prefs['internal_db']['host']),
                                 user=str(prefs['internal_db']['user']),
                                 passwd=str(prefs['internal_db']['passwd']),
                                 database=str(
                                     prefs['internal_db']['database']),
                                 autocommit=True)
    cursor = db.cursor()
    # use a lock to access database to avoid concurrency access
    cursor.execute("LOCK TABLES " + str(prefs['simulator']['table']) +
                   " WRITE")
    try:
        cursor.execute("SELECT * FROM " + str(prefs['simulator']['table']) +
                       " WHERE requestIP = '%s'" % requestIP)
        result = cursor.fetchone()
        if result is not None:
            # an exchange for this key is already in progress, return an error
            # release db lock
            cursor.execute("UNLOCK TABLES")
            return "Error", 400
        else:
            # a new key exchange can be started
            # insert information
            cursor.execute(
                "INSERT INTO " + str(prefs['simulator']['table']) +
                " (requestIP, complete, exchangedKey, verified) VALUES ('%s', False, NULL, False)"
                % (requestIP))
        # release db lock
        cursor.execute("UNLOCK TABLES")
    except Exception as e:
        # always release lock before quit
        cursor.execute("UNLOCK TABLES")
        raise (e)
    # new key requested - reset all variables
    alice_table = np.array([])
    alice_key = []
    temp_alice_key = ''
    # get key length
    key_length = int(request.args.get('keyLen'))
    app.logger.info(
        'New key exchange requested from client. Desired key length %s' %
        str(key_length))

    qubits_vectors = pickle.loads(request.data)

    chunk = qubits_vectors[0].num_qubits

    for i in range(len(qubits_vectors)):
        # generate a quantum circuit random basis for measurements
        qr = QuantumRegister(chunk, name='qr')
        cr = ClassicalRegister(chunk, name='cr')
        circuit = QuantumCircuit(qr, cr, name='qcircuit')

        basisChoice = randomStringGen(chunk)
        # randomly chose basis for measurement
        table = np.array([])
        for index, bit in enumerate(basisChoice):
            if 0.5 < int(bit):
                circuit.h(qr[index])
                table = np.append(table, 'X')
            else:
                table = np.append(table, 'Z')

        # Reverse table
        table = table[::-1]
        alice_table = np.append(alice_table, table)
        qubits = qubits_vectors[i].evolve(circuit)
        # Measure statevector
        meas = qubits.measure()
        temp_alice_key += meas[0]

    return "OK"
Exemple #10
0
    def exchangeKey(self,
                    key_length,
                    destination='http://localhost:4000',
                    protocol='bb84',
                    timeout=0):
        if protocol == 'e91':
            # check if a key is already present
            client = hvac.Client(url='http://' + prefs['vault']['host'])
            client.token = prefs['vault']['token']
            db = mysql.connector.connect(
                host=str(prefs['internal_db']['host']),
                user=str(prefs['internal_db']['user']),
                passwd=str(prefs['internal_db']['passwd']),
                database=str(prefs['internal_db']['database']),
                autocommit=True)
            cursor = db.cursor(buffered=True)
            cursor.execute("SELECT * FROM " +
                           str(prefs['simulator']['table']) +
                           " WHERE requestIP = 'E91'")
            result = cursor.fetchone()
            if result is not None:
                # key already exchanged
                response = client.secrets.kv.read_secret_version(path='e91Key')
                key = response['data']['data']['key']
                # delete key once returned
                client.secrets.kv.delete_metadata_and_all_versions('e91Key')
                cursor.execute("DELETE FROM " +
                               str(prefs['simulator']['table']) +
                               " WHERE `requestIP` = 'E91'")
                return key, True, 0

            x = requests.post('http://' + prefs['eve']['ip_addr'] +
                              '/startE91exchange?destination=' + destination,
                              data=repr(key_length))
            if x.status_code == 200:
                response = client.secrets.kv.read_secret_version(path='e91Key')
                key = response['data']['data']['key']
                # delete key once returned
                client.secrets.kv.delete_metadata_and_all_versions('e91Key')
                cursor.execute("DELETE FROM " +
                               str(prefs['simulator']['table']) +
                               " WHERE `requestIP` = 'E91'")
                return key, True, 0
            return None, False, 0
        # bb84
        else:
            # delay the start of the exchange of a random number of seconds (between 0 and 8)
            randNo = randomStringGen(3)
            # convert bit string into bytes
            randNo = int(randNo, 2)
            time.sleep(randNo)

            app.logger.info('Starting key exchange. Desired key length: %s' %
                            str(key_length))
            # 1/3 of the key needs to be exchanged in order to verify key
            # that part of the key cannot be used anymore after key verification
            # generate 1/3 more than key_length that will then be exchanged
            # in this way final key length will be as equals as key_length
            key_length = int(key_length)
            includingVerifyLen = round(key_length + (key_length / 3))
            # add a 15% of the total length
            generateLength = includingVerifyLen + round(
                includingVerifyLen * 15 / 100)
            # multiply generatedLength by two since qubits will be discarded with a probability of 50%
            generateLength = generateLength * 2

            # check if a key has already been exchanged with desired destination
            destAddr = str(prefs['eve']['ip_addr']).split(':')[0]
            db = mysql.connector.connect(
                host=str(prefs['internal_db']['host']),
                user=str(prefs['internal_db']['user']),
                passwd=str(prefs['internal_db']['passwd']),
                database=str(prefs['internal_db']['database']),
                autocommit=True)
            cursor = db.cursor()
            # use a lock to access database to avoid concurrency access
            cursor.execute("LOCK TABLES " + str(prefs['simulator']['table']) +
                           " WRITE")
            try:
                cursor.execute("SELECT * FROM " +
                               str(prefs['simulator']['table']) +
                               " WHERE requestIP = '%s'" % (destAddr))
                result = cursor.fetchone()
                if result is not None:
                    # a key with this id has already been requested from server side
                    # wait until the whole key is received
                    # release lock during wait
                    cursor.execute("UNLOCK TABLES")
                    start_time = current_time()
                    while bool(result[1]) is not True:
                        cursor.execute("SELECT * FROM " +
                                       str(prefs['simulator']['table']) +
                                       " WHERE requestIP = '%s'" % (destAddr))
                        result = cursor.fetchone()
                        if current_time() > start_time + timeout:
                            # timeout elapsed - clean requests list
                            cursor.execute("DELETE FROM " +
                                           str(prefs['simulator']['table']) +
                                           " WHERE `requestIP` = '%s'" %
                                           (destAddr))

                            return None, 4, 0
                    # now key exchange is complete
                    verified = result[3]
                    if verified == 0:
                        verified = False
                    else:
                        verified = True
                    # key is saved in vault
                    client = hvac.Client(url='http://' +
                                         prefs['vault']['host'])
                    client.token = prefs['vault']['token']
                    response = client.secrets.kv.read_secret_version(
                        path='currentKey')
                    key = response['data']['data']['key']
                    # delete key once returned
                    client.secrets.kv.delete_metadata_and_all_versions(
                        'currentKey')
                    # once key has been exchange, delete its data from this module
                    cursor.execute("LOCK TABLES " +
                                   str(prefs['simulator']['table']) + " WRITE")
                    cursor.execute("DELETE FROM " +
                                   str(prefs['simulator']['table']) +
                                   " WHERE `requestIP` = '%s'" % (destAddr))
                    cursor.execute("UNLOCK TABLES")
                    return key, verified, 1
                else:
                    # start key exchange - save information
                    cursor.execute(
                        "INSERT INTO " + str(prefs['simulator']['table']) +
                        " (requestIP, complete, exchangedKey, verified) VALUES ('%s', False, NULL, False)"
                        % (destAddr))
                # release lock
                cursor.execute("UNLOCK TABLES")
            except Exception as e:
                # error occurred - clean requests list
                cursor.execute("DELETE FROM " +
                               str(prefs['simulator']['table']) +
                               " WHERE `requestIP` = '%s'" % (destAddr))
                # always release lock before quit
                cursor.execute("UNLOCK TABLES")
                raise (e)

            # start a new key exchange
            # generate state vectors
            qubit_vectors = []
            alice_table = np.array([])
            alice_key = ''
            start = time.time()
            for i in range(math.ceil(generateLength / chunk)):
                qubits, alice_table_part, temp_alice_key = generateQubits()
                qubit_vectors.append(qubits)
                alice_table = np.append(alice_table, alice_table_part)
                alice_key = alice_key + temp_alice_key
            end = time.time()
            app.logger.info("Qubits generation time: " + str(end - start))

            # send quantum bits
            start = time.time()
            x = requests.post('http://' + prefs['eve']['ip_addr'] +
                              '/sendRegister?keyLen=' + str(key_length) +
                              '&destination=' + destination,
                              data=pickle.dumps(qubit_vectors))
            if x.status_code != 200:
                # error - return
                #cursor.execute("DELETE FROM " + str(prefs['simulator']['table']) + " WHERE `requestIP` = '%s'" % (destAddr))
                return None, False, 0
            end = time.time()
            app.logger.info("/sendRegister time: " + str(end - start))

            if authenticationMethod == 'aesgcm':
                start = time.time()
                # select key
                key, keyID, TTL = getPresharedKey()
                if key is None:
                    # no key available, return an error
                    cursor.execute("DELETE FROM " +
                                   str(prefs['simulator']['table']) +
                                   " WHERE `requestIP` = '%s'" % (destAddr))
                    return None, False, 0
                aesgcm = AESGCM(bytes(key, 'utf-8'))
                # select nonce
                nonce = randomStringGen(12)
                cypherTable = aesgcm.encrypt(bytes(nonce, 'utf-8'),
                                             alice_table.tobytes(), None)
                end = time.time()
                app.logger.info("aes-gcm signature time: " + str(end - start))
                # compare basis table
                reqData = [cypherTable, keyID, nonce]
                start = time.time()
                y = requests.post('http://' + prefs['eve']['ip_addr'] +
                                  '/compareBasis?destination=' + destination,
                                  data=repr(reqData))
                if y.status_code != 200:
                    # error - return
                    cursor.execute("DELETE FROM " +
                                   str(prefs['simulator']['table']) +
                                   " WHERE `requestIP` = '%s'" % (destAddr))
                    return None, False, 0
                end = time.time()
                app.logger.info("/compareBasis time: " + str(end - start))
                rep = eval(y.content)
                cypherRep = rep[0]
                nonceRep = rep[1]
                start = time.time()
                bob_table = aesgcm.decrypt(bytes(nonceRep, 'utf-8'), cypherRep,
                                           None)
                end = time.time()
                app.logger.info("aes-gcm decrypt time: " + str(end - start))
                # convert bytes back to numpy array
                bob_table = np.frombuffer(bob_table, dtype=alice_table.dtype)
            # default to sphincs
            else:
                # sign alice_table before sending it
                start = time.time()
                aliceSign = sphincs.sign(alice_table.tobytes(),
                                         eval(prefs['auth_key']['privateKey']))
                end = time.time()
                app.logger.info("sphincs+ signature time: " + str(end - start))
                # compare basis table
                start = time.time()
                y = requests.post('http://' + prefs['eve']['ip_addr'] +
                                  '/compareBasis?destination=' + destination,
                                  data=repr(
                                      [pickle.dumps(alice_table), aliceSign]))
                if y.status_code != 200:
                    # error - return
                    cursor.execute("DELETE FROM " +
                                   str(prefs['simulator']['table']) +
                                   " WHERE `requestIP` = '%s'" % (destAddr))
                    return None, False, 0
                end = time.time()
                app.logger.info("/compareBasis time: " + str(end - start))

                rep = eval(y.content)
                bob_table = pickle.loads(rep[0])
                tableSign = rep[1]
                # check that table was actually sent from Bob
                start = time.time()
                if not sphincs.verify(bob_table.tobytes(), tableSign,
                                      eval(
                                          prefs['auth_key']['peerPublicKey'])):
                    app.logger.error(
                        "Table comparison failed due to wrong signature!")
                    cursor.execute("DELETE FROM " +
                                   str(prefs['simulator']['table']) +
                                   " WHERE `requestIP` = '%s'" % (destAddr))
                    return None, False, 0
                end = time.time()
                app.logger.info("sphincs+ verify time: " + str(end - start))

            keep = []
            discard = []
            for qubit, basis in enumerate(zip(alice_table, bob_table)):
                if basis[0] == basis[1]:
                    #print("Same choice for qubit: {}, basis: {}" .format(qubit, basis[0]))
                    keep.append(qubit)
                else:
                    #print("Different choice for qubit: {}, Alice has {}, Bob has {}" .format(qubit, basis[0], basis[1]))
                    discard.append(qubit)

            #print('Percentage of qubits to be discarded according to table comparison: ', len(keep)/chunk)
            alice_key = [alice_key[qubit] for qubit in keep]

            # randomly select bit to be used for key verification
            picked, verifyingKey = [], []
            i = 0
            # we need to generate a random number between 0 and includingVerifyLen
            # randomStringGen generates a string of bit - calculate how many bit we need to get a consistent top value
            bits = 0
            temp = includingVerifyLen
            while temp > 0:
                temp = temp >> 1
                bits += 1
            while i < includingVerifyLen - key_length:
                # generate a valid random number (in range 0 - key_length + includingVerifyLen and not already used)
                while True:
                    randNo = randomStringGen(bits)
                    # convert bit string into bytes
                    randNo = int(randNo, 2)
                    if randNo >= includingVerifyLen:
                        # not a valid number
                        continue
                    if randNo in picked:
                        # number already used
                        continue
                    # number is valid - exit from this inner loop
                    break
                # add randNo to list of picked
                picked.append(randNo)
                i += 1

            # remove used bits from the key
            for i in sorted(picked, reverse=True):
                verifyingKey.append(int(alice_key[i]))
                del alice_key[i]

            # make sure key length is exactly equals to key_length
            alice_key = alice_key[:key_length]

            app.logger.info("Key exchange completed - new key: %s" % alice_key)
            app.logger.info("key len: %s" % str(len(alice_key)))

            # delete info once key exchange is complete
            try:
                cursor.execute("LOCK TABLES " +
                               str(prefs['simulator']['table']) + " WRITE")
                cursor.execute("DELETE FROM " +
                               str(prefs['simulator']['table']) +
                               " WHERE `requestIP` = '%s'" % (destAddr))
                cursor.execute("UNLOCK TABLES")
            except Exception as e:
                # always release lock before quit
                cursor.execute("UNLOCK TABLES")
                raise (e)

            if authenticationMethod == 'aesgcm':
                if TTL == 0:
                    # we need to take another key
                    key, keyID, TTL = getPresharedKey()
                    if key is None:
                        # no key available, return an error
                        return None, False, 0
                aesgcm = AESGCM(bytes(key, 'utf-8'))
                # select nonce
                nonceVK = randomStringGen(12)
                nonceP = randomStringGen(12)
                cypherVK = aesgcm.encrypt(bytes(nonceVK, 'utf-8'),
                                          bytes(repr(verifyingKey), 'utf-8'),
                                          None)
                cypherP = aesgcm.encrypt(bytes(nonceP, 'utf-8'),
                                         bytes(repr(picked), 'utf-8'), None)
                x = requests.post(
                    'http://' + prefs['eve']['ip_addr'] +
                    '/verifyKey?destination=' + destination,
                    data=repr([keyID, cypherVK, nonceVK, cypherP, nonceP]))
                if x.status_code != 200:
                    app.logger.error("Server error occurred %s" %
                                     x.status_code)
                    return alice_key, False, 0
                # get Bob's reply
                rep = eval(x.content)
                cypherRep = rep[0]
                nonce = rep[1]
                bobKey = aesgcm.decrypt(bytes(nonce, 'utf-8'), cypherRep, None)
                bobKey = eval(bobKey)

            # default to sphincs
            else:
                # sign data with our private key
                keySign = sphincs.sign(bytes(verifyingKey),
                                       eval(prefs['auth_key']['privateKey']))
                picked = np.array(picked)
                pickSign = sphincs.sign(picked.tobytes(),
                                        eval(prefs['auth_key']['privateKey']))
                # send data and signature to verify key exchange
                x = requests.post('http://' + prefs['eve']['ip_addr'] +
                                  '/verifyKey?destination=' + destination,
                                  data=repr([
                                      verifyingKey, keySign,
                                      pickle.dumps(picked), pickSign
                                  ]))
                if x.status_code != 200:
                    app.logger.error("Server error occurred %s" %
                                     x.status_code)
                    return alice_key, False, 0

                # get Bob's reply
                rep = eval(x.content)
                bobKey = rep[0]
                bobKeySign = rep[1]

                # verify Bob's signature
                if not sphincs.verify(bytes(bobKey), bobKeySign,
                                      eval(
                                          prefs['auth_key']['peerPublicKey'])):
                    app.logger.error(
                        "Key verification failed due to wrong signature!")
                    return alice_key, false, 0

            # check that Alice and Bob have the same key
            acc = 0
            for bit in zip(verifyingKey, bobKey):
                if bit[0] == bit[1]:
                    acc += 1

            app.logger.info('\nPercentage of similarity between the keys: %s' %
                            str(acc / len(verifyingKey)))
            qber = 1 - (acc / len(verifyingKey))

            if (acc // len(verifyingKey) == 1):
                return alice_key, True, qber
                app.logger.info("\nKey exchange has been successfull")
            else:
                app.logger.error(
                    "\nKey exchange has been tampered! Check for eavesdropper or try again"
                )
                return alice_key, False, qber