Example #1
0
def encode_bits_as_words(chain: MarkovChain,
                         wt_dict: WordTypeDictionary,
                         bits: Bits,
                         pad_text=True) -> list:
    """
    Given a bit stream, a Markov chain, and a word-type dictionary, retrieve a corresponding list of words.
    Every state in the Markov chain, except the start state s0, must have a corresponding word-type in the given
    dictionary with at least one word.

    If the word-type dictionary does not have path bits to match the end of the input exactly, it will append 0s
    until the function can complete.

    :param chain: a Markov chain with states
    :param wt_dict: a corresponding dictionary of word-types
    :param bits: the input bits
    :param pad_text: if true, generate cover text from random bits until the Markov chain reaches state s0
    :return: an ordered list of words encoded by the system
    """
    if bits is None or bits.__eq__(Bits()):
        raise ValueError("Bits cannot be None or empty.")

    words = []
    prefix = bits
    while len(prefix) > 0:
        chain.transition()
        if chain.current_state.__eq__(START_STATE_LABEL):
            chain.transition()

        word, word_bits, encode_spaces = _encode_next_word(
            chain, wt_dict, prefix)
        words.append((word, encode_spaces))
        bit_length = len(word_bits)
        prefix = prefix[bit_length:]

    if pad_text:
        # add filler bits until s0 reached
        while not chain.current_state.__eq__(START_STATE_LABEL):
            chain.transition()
            if chain.current_state.__eq__(START_STATE_LABEL):
                break
            longest_word = get_longest_word_in_dictionary(wt_dict)
            pseudo_random_bits = Bits(bin="".join(
                random.choice(["0", "1"]) for _ in range(len(longest_word))))
            word, word_bits, encode_spaces = _encode_next_word(
                chain, wt_dict, pseudo_random_bits)
            words.append((word, encode_spaces))

    return words
Example #2
0
def encode_message(chain: MarkovChain,
                   wt_dict: WordTypeDictionary,
                   bits: Bits,
                   header_length=DEFAULT_HEADER_LENGTH) -> str:
    """
    Given a header length, a secret message as bits, a Markov chain, and a word-type dictionary, encode a cover text
    including a header which contains the length of the message.
    The message may be no more than (2^header_length) bits long.
    :param chain: a Markov chain with states
    :param wt_dict: a corresponding dictionary of word-types
    :param bits: the input bits
    :param header_length: the pre-shared length, in bits, of the header
    :return: the cover text as a string
    """
    if bits is None or bits.__eq__(Bits()):
        raise ValueError("Bits cannot be None or empty.")
    message_length = len(bits)
    header = get_fixed_length_header(message_length, header_length)
    full_message = header + bits
    words = encode_bits_as_words(chain, wt_dict, full_message)
    cover_text = words_to_cover_text(words, True)
    return cover_text
        input_filename = prefix_filename(args.subfolder, input_filename)
    if output_filename is None:
        raise ValueError("Filename for output was not provided.")
    else:
        output_filename = prefix_filename(args.subfolder, output_filename)
    if header_length is None:
        header_length = DEFAULT_HEADER_LENGTH
    elif header_length < 1:
        raise ValueError("Header length must be greater than 0.")

    input_message = read_input_file(input_filename)
    try:
        message_bits = Bits(bin=input_message)
    except CreationError:
        raise ValueError("Provided input was not a valid bitstring.")
    if message_bits.__eq__(Bits()):
        raise ValueError("Provided input was empty.")

    markov_chain = markov.load_markov_chain(chain_filename)
    print("Markov chain loaded.")

    wt_dict = init_wt_dict(dict_filename)
    print("Word-type dictionary loaded.")

    print("Encoding cover text with header length {}.".format(header_length))
    cover_text = extendedcoder.encode_message(markov_chain, wt_dict,
                                              message_bits, header_length)

    write_output_file(output_filename, cover_text)
    print("Cover text written to {}.".format(output_filename))
Example #4
0
    key: bytes = None if args.key is None else bytes(args.key,
                                                     encoding=DEFAULT_ENCODING)

    if input_filename is None:
        raise ValueError("Filename for input was not provided.")
    if output_filename is None:
        raise ValueError("Filename for output was not provided.")
    if key is None:
        key = DEFAULT_KEY

    message_bits = read_input_file(input_filename)
    try:
        ciphertext_bits = Bits(bin=message_bits)
    except CreationError:
        raise ValueError("Provided input was not a valid bitstring.")
    if ciphertext_bits.__eq__(Bits()):
        raise ValueError("Provided input was empty.")

    bits = Bits(bytes=Encryptor(key).encrypt_bytes(ciphertext_bits.bytes))
    print("Input encrypted.")

    write_output_file(output_filename, bits.bin)
    print("Ciphertext written to {}".format(output_filename))

elif operation.__eq__("decrypt"):
    input_filename: str = prefix_filename(args.subfolder, args.input)
    output_filename: str = prefix_filename(args.subfolder, args.output)
    key: bytes = None if args.key is None else bytes(args.key,
                                                     encoding=DEFAULT_ENCODING)

    if input_filename is None:
Example #5
0
def encode_bits_as_strings(tree: HuffmanTree,
                           bits: Bits,
                           string_prefix: str = "") -> Tuple[Bits, str]:
    """
    Given a bit stream and a Huffman tree, return the appropriate
    string of
    symbols.

    The output will match the statistical distribution of the
    sample it was made
    with as much as possible, although limited by the necessity of an
    unambiguous HuffmanTree structure.

    If the Huffman tree does not have path bits to match the input
    exactly, it
    will append 0s until the function can complete.

    :param tree: a Huffman tree with path bits allocated
    :param bits: the input bits
    :param string_prefix: the so-far accumulated string. Leave
    empty when
    calling manually
    :return: a Tuple of the remaining bits and the accumulated
    string made up
    of symbols in the Huffman tree
    """
    if bits is None or bits.__eq__(Bits()):
        return Bits(), string_prefix

    if tree.left is not None and tree.right is not None:
        # This tree has subtrees
        left_tree = tree.left[1]
        right_tree = tree.right[1]

        if left_tree.path_code is None or right_tree.path_code is \
                None:
            raise HuffmanError(
                "When encoding bits as strings, a node was missing "
                "a path code")
        else:
            if bits.startswith(left_tree.path_code):
                remaining_bits, accumulated_string = \
                    encode_bits_as_strings(
                    left_tree, bits, string_prefix)
            elif bits.startswith(right_tree.path_code):
                remaining_bits, accumulated_string = \
                    encode_bits_as_strings(
                    right_tree, bits, string_prefix)
            else:
                # Binary sequence does not match a leaf value. Must
                # pad with 0s
                padded_bits = bits.__add__(zero_bit)
                return padded_bits, string_prefix

            if tree.path_code is None:
                # This tree is a root node
                if bits is None:
                    # We are out of bits, so we can return the
                    # final string
                    return remaining_bits, accumulated_string
                else:  # Continue recursively processing the
                    # remaining bits
                    return encode_bits_as_strings(tree, remaining_bits,
                                                  accumulated_string)
            else:
                return remaining_bits, accumulated_string
    elif tree.left is None and tree.right is None:  # This tree is
        # a leaf node
        if tree.path_code is None:
            raise HuffmanError("When encoding bits as strings, a leaf node was"
                               " missing a path code")
        else:
            if bits.startswith(tree.path_code):
                accumulated_string = string_prefix + tree.value[0]
                if bits.__eq__(tree.path_code):
                    remaining_bits = None
                else:
                    remaining_bits = bits[tree.path_code.length:]
                return remaining_bits, accumulated_string
            else:
                warnings.warn("When encoding bits as strings, some unencodable"
                              " bits were left over")
                return bits, string_prefix
    else:
        raise HuffmanError(
            "The given Huffman tree contained a node with exactly 1 "
            "child tree")