Example #1
0
def get_plaintexts(basename, nonce):
    if not utils.basename_and_nonce_are_valid(basename, nonce):
        return ""

    path_to_plaintexts_file = os.path.join('/tmp', basename + '.plaintext.bin')

    # If it doesn't already exist, create the file containting the plaintexts
    if not os.path.exists(path_to_plaintexts_file):
        with open(path_to_plaintexts_file, 'wb') as f:
            f.write(
                os.urandom(16 *
                           app.config['CHALLENGE_NUMBER_OF_TEST_VECTORS']))

    plaintexts = b''
    with open(path_to_plaintexts_file, 'rb') as f:
        plaintexts = f.read()

    return plaintexts
def get_messages(basename, nonce):
    if not utils.basename_and_nonce_are_valid(basename, nonce):
        return ""

    path_to_message_file = os.path.join('/tmp', basename + '.message.bin')

    # If it doesn't already exist, create the file containting the messages
    if not os.path.exists(path_to_message_file):
        with open(path_to_message_file, 'wb') as f:

            for case in CHALLENGE_TEST_EDGE_CASES:
                f.write(case.to_bytes(32, byteorder="big"))

            f.write(os.urandom(
                32 * app.config['CHALLENGE_NUMBER_OF_TEST_VECTORS']))

    with open(path_to_message_file, 'rb') as f:
        messages = f.read()

    return messages
Example #3
0
def compile_and_test_result(basename, nonce, ret):
    utils.console(
        "Entering compile_and_test_result(basename=%s, nonce=%s, ret=%d)" %
        (basename, nonce, ret))
    if not utils.basename_and_nonce_are_valid(basename, nonce) or ret is None:
        utils.console("Exception takes place ... (0)")
        return ""

    program = Program.get(basename)
    if program.status != Program.Status.submitted:
        utils.console(
            "The program %d status is %s. No need to proceed for this program."
            % (program._id, program.status))
        utils.console("Exception takes place ... (1)")
        return ""

    # We (try to) remove the compilation directory
    dir_for_compilation = basename
    path_for_compilations = os.path.join('/compilations', dir_for_compilation)
    utils.console('Trying to remove %s' % str(path_for_compilations))
    try:
        shutil.rmtree(path_for_compilations)
    except:
        utils.console('Could NOT remove the directory %s' %
                      str(path_for_compilations))

    # We process the ret code
    if ret == ERR_CODE_CONTAININT_FORBIDDEN_STRING:
        postdata = request.get_json()
        program.set_status_to_preprocess_failed(postdata['error_message'])
    elif ret == ERR_CODE_COMPILATION_FAILED:
        program.set_status_to_compilation_failed(
            'Compilation failed for unknown reason (may be due to an excessive memory usage).'
        )
        utils.console('Compilation failed for file with basename %s' %
                      str(basename))
    elif ret == ERR_CODE_BIN_TOO_LARGE:
        program.set_status_to_compilation_failed(
            'Compiled binary file size exceeds the limit of %dMB.' %
            app.config['CHALLENGE_MAX_BINARY_SIZE_IN_MB'])
        utils.console('Compilation failed for file with basename %s' %
                      str(basename))
    elif ret == ERR_CODE_LINK_FAILED:
        program.set_status_to_link_failed()
        utils.console('Link failed for file with basename %s' % str(basename))
    elif ret == ERR_CODE_EXECUTION_EXCEED_RAM_LIMIT:
        postdata = request.get_json()
        program.set_status_to_execution_failed(
            "Execution reach memory limitation of %dMB. Memory consumption was %.2fMB."
            %
            (app.config['CHALLENGE_MAX_MEM_EXECUTION_IN_MB'], postdata['ram']))
        utils.console(
            'Code execution reach memory limit for file with basename %s' %
            str(basename))
    elif ret == ERR_CODE_EXECUTION_EXCEED_TIME_LIMIT:
        postdata = request.get_json()
        program.set_status_to_execution_failed(
            "Execution reach time limitation of %ds. Time used %.2fs" %
            (app.config['CHALLENGE_MAX_TIME_EXECUTION_IN_SECS'],
             postdata['cpu_time']))
        utils.console(
            'Code execution reach time limit for file with basename %s' %
            str(basename))
    elif ret == ERR_CODE_EXECUTION_FAILED:
        program.set_status_to_execution_failed()
        utils.console('Code execution failed for file with basename %s' %
                      str(basename))
    elif ret == CODE_SUCCESS:
        utils.console('Success for file with basename %s' % str(basename))
    else:
        utils.console(
            "We received an unexpected return code (%s) for file with basename %s"
            % (str(ret), str(basename)))
    db.session.commit()

    if ret != ERR_CODE_EXECUTION_FAILED:
        client = docker.from_env()
        utils.remove_compiler_service_for_basename(client, basename, app)

    if ret != CODE_SUCCESS:
        utils.console("Failed to compiling ... ")
        return ""

    # If we reach this point, the program was successfuly compiled,
    # we can test the ciphertexts
    response = request.get_json()
    size_factor = response['size_factor']
    ram_factor = response['ram_factor']
    time_factor = response['time_factor']
    program.set_performance_factor(size_factor, ram_factor, time_factor)

    ciphertexts = bytes.fromhex(response['ciphertexts'])
    number_of_test_vectors = app.config['CHALLENGE_NUMBER_OF_TEST_VECTORS']
    if len(ciphertexts) != 16 * number_of_test_vectors:
        utils.console(
            "The length of the ciphertexts is %d, we were expecting %d." %
            (len(ciphertexts), 16 * number_of_test_vectors))
        error_message = "The stream of ciphertexts does not have the appropriate length."
        utils.console(error_message)
        program.error_message = error_message
        program.set_status_to_test_failed()
        db.session.commit()

        utils.console("Exception take place... (3)")
        return ""

    # If we reach this point, the ciphertexts stream has the appropriate length
    utils.console("We received the appropriate number of ciphertexts.")
    utils.console(
        "Testing the plaintexts against the ciphertexts using the announced key..."
    )

    # Retrieve the plaintexts from the saved file
    path_to_plaintexts_file = os.path.join('/tmp', basename + '.plaintext.bin')
    plaintexts = b''
    with open(path_to_plaintexts_file, 'rb') as f:
        plaintexts = f.read()

    # Check the ciphertexts against the plaintext and key.
    # TODO the db should always return the key as 16 bytes
    key = bytes.fromhex(program.key)
    try:
        expected_ciphertexts = utils.compute_ciphertexts(
            plaintexts, key, number_of_test_vectors)
        if len(expected_ciphertexts) != 16 * number_of_test_vectors:
            raise
    except:
        error_message = "Could not compute the test vectors for the given key."
        program.set_status_to_test_failed(error_message)
        db.session.commit()
        return ""
    for i in range(number_of_test_vectors):
        ciphertext = ciphertexts[16 * i:16 * (i + 1)]
        expected_ciphertext = expected_ciphertexts[16 * i:16 * (i + 1)]
        if ciphertext != expected_ciphertext:
            plaintext = plaintexts[16 * i:16 * (i + 1)]
            utils.console(
                "One of the ciphertext failed the test (plaintext=%s, key=%s, ciphertext=%s)."
                % (plaintext, key, ciphertext))
            error_message = '''One of the tests failed:
- plaintext   %s
- key         %s
- ciphertext  %s
- expected    %s''' % (binascii.hexlify(plaintext).decode(),
                       binascii.hexlify(key).decode(),
                       binascii.hexlify(ciphertext).decode(),
                       binascii.hexlify(expected_ciphertext).decode())
            program.set_status_to_test_failed(error_message)
            db.session.commit()
            return ""

    # If we reach this point, all the tests were successful.
    # We save 10 test vectors for key validation in the database:
    plaintexts_for_breaking = plaintexts[0:10 * 16]
    ciphertexts_for_breaking = ciphertexts[0:10 * 16]
    program.plaintexts = plaintexts_for_breaking
    program.ciphertexts = ciphertexts_for_breaking
    # we save one pair for validating inversion
    plaintext_for_inverting = plaintexts[10 * 16:11 * 16]
    ciphertext_for_inverting = ciphertexts[10 * 16:11 * 16]
    program.plaintext_sha256_for_inverting = hashlib.sha256(
        plaintext_for_inverting).hexdigest()
    program.ciphertext_for_inverting = ciphertext_for_inverting

    program.set_status_to_unbroken()
    db.session.commit()
    utils.console("The program is unbroken!")

    # Cleanup
    try:
        os.remove(path_to_plaintexts_file)
        utils.console("We removed the file %s" % path_to_plaintexts_file)
    except:
        utils.console("Could NOT remove the file %s" % path_to_plaintexts_file)

    return ""
def compile_and_test_result(basename, nonce, ret):
    utils.console(f"Entering compile_and_test_result(basename={basename}, "
                  f"nonce={nonce}, ret={ret})")
    if not utils.basename_and_nonce_are_valid(basename, nonce) or ret is None:
        utils.console("Exception takes place ... (0)")
        return ""

    program = Program.get(basename)
    if program.status != Program.Status.submitted:
        utils.console(f"The program {program._id} status is {program.status}. "
                      "No need to proceed for this program.")
        utils.console("Exception takes place ... (1)")
        return ""

    # We (try to) remove the compilation directory
    dir_for_compilation = basename
    path_for_compilations = os.path.join('/compilations', dir_for_compilation)
    utils.console(f'Trying to remove {path_for_compilations}')
    try:
        shutil.rmtree(path_for_compilations)
    except:
        utils.console(f'Could NOT remove the dir {path_for_compilations}')

    # We process the ret code
    process_compile_and_test_ret(program, request, basename, ret)
    db.session.commit()

    if ret != ERR_CODE_EXECUTION_FAILED:
        client = docker.from_env()
        utils.remove_compiler_service_for_basename(client, basename, app)

    if ret != CODE_SUCCESS:
        utils.console("Failed to compiling ... ")
        return ""

    # If we reach this point, the program was successfully compiled,
    # we get performance factors
    postdata = request.get_json()
    size_factor = postdata['size_factor']
    ram_factor = postdata['ram_factor']
    time_factor = postdata['time_factor']
    program.set_performance_factor(size_factor, ram_factor, time_factor)

    # we can test the signatures
    signatures = bytes.fromhex(postdata['signatures'])
    number_of_test_vectors = app.config['CHALLENGE_NUMBER_OF_TEST_VECTORS']
    number_of_test_vectors += len(CHALLENGE_TEST_EDGE_CASES)
    if len(signatures) != 64 * number_of_test_vectors:
        utils.console(f"The length of the signatures is {len(signatures)}, "
                      f"we were expecting {32*number_of_test_vectors}.")
        error_message = "The stream of ciphertexts does not have the appropriate length."
        utils.console(error_message)
        program.error_message = error_message
        program.set_status_to_test_failed()
        db.session.commit()

        utils.console("Exception take place... (3)")
        return ""

    # If we reach this point, the ciphertexts stream has the appropriate length
    utils.console("We received the appropriate number of signatures.")
    utils.console("Verify signature for messages using the announced key...")

    # Retrieve the messages from the saved file
    path_to_messages_file = os.path.join('/tmp', basename + '.message.bin')
    with open(path_to_messages_file, 'rb') as f:
        messages = f.read()

    # Check the signature against the public key and messages.
    # TODO the db should always return the key as 128 hexdecimal digits
    pubkey = program.pubkey
    for i in range(number_of_test_vectors):
        message = messages[32*i:32*(i+1)].hex()
        signature = signatures[64*i:64*(i+1)].hex()
        if not ecdsa_verify_str(pubkey, message, signature):
            utils.console(f"The {i}-th signature cannot be verified "
                          f"(hash={message}, pubkey={pubkey}, "
                          f"signature={signature}).")

            error_message = f'''One of the tests failed:

- hash      {message}
- pubkey    {pubkey}
- signature {signature}'''
            program.set_status_to_test_failed(error_message)
            db.session.commit()
            return ""
    utils.console(f"All {number_of_test_vectors} signatures verified")

    # If we reach this point, all the tests were successful.
    # We save 10 test vectors for in the database
    messages_for_checking = messages[0:10*32]
    signatures_for_checking = signatures[0:10*64]
    program.hashes = messages_for_checking
    program.signatures = signatures_for_checking

    program.set_status_to_unbroken()
    db.session.commit()
    utils.console("The program is unbroken!")

    # Cleanup
    try:
        os.remove(path_to_messages_file)
        utils.console("We removed the file %s" % path_to_messages_file)
    except:
        utils.console("Could NOT remove the file %s" % path_to_messages_file)

    return ""
Example #5
0
def compile_and_test_result(basename, nonce, ret):
    utils.console(
        "Entering compile_and_test_result(basename=%s, nonce=%s, ret=%d)" %
        (basename, nonce, ret))
    if not utils.basename_and_nonce_are_valid(basename, nonce) or ret is None:
        return ""
    program = Program.get(basename)

    # We (try to) remove the compilation directory
    dir_for_compilation = basename
    path_for_compilations = os.path.join('/compilations', dir_for_compilation)
    utils.console('Trying to remove %s' % str(path_for_compilations))
    try:
        shutil.rmtree(path_for_compilations)
    except:
        utils.console('Could NOT remove the directory %s' %
                      str(path_for_compilations))

    # We process the ret code
    if ret == ERR_CODE_COMPILATION_FAILED:
        program.set_status_to_compilation_failed(
            'Compilation failed for unknown reason (may be due to an excessive memory usage).'
        )
        utils.console('Compilation failed for file with basename %s' %
                      str(basename))
    elif ret == ERR_CODE_BIN_TOO_LARGE:
        program.set_status_to_compilation_failed(
            'Compiled binary file size exceeds the limit of %dMB.' %
            app.config['CHALLENGE_MAX_BINARY_SIZE_IN_MB'])
        utils.console('Compilation failed for file with basename %s' %
                      str(basename))
    elif ret == ERR_CODE_LINK_FAILED:
        program.set_status_to_link_failed()
        utils.console('Link failed for file with basename %s' % str(basename))
    elif ret == ERR_CODE_EXECUTION_FAILED:
        program.set_status_to_execution_failed()
        utils.console('Code execution failed for file with basename %s' %
                      str(basename))
    elif ret == CODE_SUCCESS:
        utils.console('Success for file with basename %s' % str(basename))
    else:
        utils.console(
            'We received an unexpected return code (%s) for file with basename %s'
            % (str(ret), str(basename)))
    db.session.commit()
    client = docker.from_env()
    utils.remove_compiler_service_for_basename(client, basename, app)
    if ret != CODE_SUCCESS:
        return ""

    # If we reach this point, the program was successfuly compiled, we can test the ciphertexts

    ciphertexts = request.get_data()
    number_of_test_vectors = app.config['CHALLENGE_NUMBER_OF_TEST_VECTORS']
    if len(ciphertexts) != 16 * number_of_test_vectors:
        utils.console(
            "The length of the ciphertexts is %d, we were expecting %d." %
            (len(ciphertexts), 16 * number_of_test_vectors))
        error_message = "The stream of ciphertexts does not have the appropriate length."
        utils.console(error_message)
        program.error_message = error_message
        program.set_status_to_test_failed()
        db.session.commit()
        return ""

    # If we reach this point, the ciphertexts stream has the appropriate length

    utils.console("We received the appropriate number of ciphertexts.")
    utils.console(
        "Testing the plaintexts against the ciphertexts using the announced key..."
    )

    # Retrieve the plaintexts from the saved file
    path_to_plaintexts_file = os.path.join('/tmp', basename + '.plaintext.bin')
    plaintexts = b''
    with open(path_to_plaintexts_file, 'rb') as f:
        plaintexts = f.read()

    # Check the ciphertexts against the plaintext and key.
    key = bytes.fromhex(
        program.key)  # TODO the db should always return the key as 16 bytes
    try:
        expected_ciphertexts = utils.compute_ciphertexts(
            plaintexts, key, number_of_test_vectors)
        if len(expected_ciphertexts) != 16 * number_of_test_vectors:
            raise
    except:
        error_message = "Could not compute the test vectors for the given key."
        program.set_status_to_test_failed(error_message)
        db.session.commit()
        return ""
    for i in range(number_of_test_vectors):
        ciphertext = ciphertexts[16 * i:16 * (i + 1)]
        expected_ciphertext = expected_ciphertexts[16 * i:16 * (i + 1)]
        if ciphertext != expected_ciphertext:
            plaintext = plaintexts[16 * i:16 * (i + 1)]
            utils.console(
                "One of the ciphertext failed the test (plaintext=%s, key=%s, ciphertext=%s)."
                % (plaintext, key, ciphertext))
            error_message = '''One of the tests failed:
- plaintext   %s
- key         %s
- ciphertext  %s
- expected    %s''' % (binascii.hexlify(plaintext).decode(),
                       binascii.hexlify(key).decode(),
                       binascii.hexlify(ciphertext).decode(),
                       binascii.hexlify(expected_ciphertext).decode())
            program.set_status_to_test_failed(error_message)
            db.session.commit()
            return ""

    # If we reach this point, all the tests were successful. We save 10 test vectors in the database so that we can test
    # key candidates in the future.

    plaintexts = plaintexts[0:10 * 16]
    ciphertexts = ciphertexts[0:10 * 16]
    program.plaintexts = plaintexts
    program.ciphertexts = ciphertexts
    program.set_status_to_unbroken()
    db.session.commit()
    utils.console("The program is unbroken!")

    # Cleanup
    try:
        os.remove(path_to_plaintexts_file)
        utils.console("We removed the file %s" % path_to_plaintexts_file)
    except:
        utils.console("Could NOT remove the file %s" % path_to_plaintexts_file)

    # Look for another program to compile and test
    compile_and_test()

    return ""