optimisticninja / liberprimus-tool

Python tool/libraray for evolving solutions toward the Liber Primus from Cicada 3301
1 stars 1 forks source link

Implement other classical cryptography schemes #4

Open optimisticninja opened 2 months ago

optimisticninja commented 2 months ago

Self explanatory, use the generic cradle of the spec to align their method signatures to the SolutionSpec run() function to be run in the genetic algorithm's evolve loop when offspring.rate() is called.

d4v1-sudo commented 2 months ago

After seeing many examples and explanations on the internet, here are some implementation ideas for some of these missing encryption types:

affine

def affine(text, a, b, decrypt=False):
    if not (a and b):
        raise ValueError("Values for 'a' and 'b' must be provided for affine cipher.")

    if decrypt:
        a_inv = 0
        for i in range(26):
            if (a * i) % 26 == 1:
                a_inv = i
                break

        if not a_inv:
            raise ValueError("Invalid 'a' value for decryption in affine cipher.")

        b = -a_inv * b

    result = []
    for char in text:
        if char.isalpha():
            if char.islower():
                result.append(chr(((a_inv * (ord(char) - ord('a') - b)) % 26) + ord('a')))
            else:
                result.append(chr(((a_inv * (ord(char) - ord('A') - b)) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

autokey

def autokey(text, key, decrypt=False):
    result = []
    key_index = 0
    for char in text:
        if char.isalpha():
            if decrypt:
                key_char = key[key_index]
            else:
                key_char = char

            key_index += 1

            if key_index == len(key):
                key_index = 0

            shift = ord(key_char.lower()) - ord('a')

            if char.islower():
                result.append(chr(((ord(char) - ord('a') - shift) % 26) + ord('a')))
            else:
                result.append(chr(((ord(char) - ord('A') - shift) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

beaufort

def beaufort(text, key, decrypt=False):
    if not key:
        raise ValueError("Key must be provided for Beaufort cipher.")

    result = []
    for char, key_char in zip(text, key):
        if char.isalpha():
            shift = ord(key_char.lower()) - ord('a')
            if decrypt:
                shift *= -1

            if char.islower():
                result.append(chr(((ord(char) - ord('a') - shift) % 26) + ord('a')))
            else:
                result.append(chr(((ord(char) - ord('A') - shift) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

caesar

def caesar(text, shift, decrypt=False):
    if decrypt:
        shift = -shift

    result = []
    for char in text:
        if char.isalpha():
            if char.islower():
                result.append(chr(((ord(char) - ord('a') + shift) % 26) + ord('a')))
            else:
                result.append(chr(((ord(char) - ord('A') + shift) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

chaocipher

def chaocipher(text, key1, key2):
    if not (key1 and key2):
        raise ValueError("Key1 and Key2 must be provided for Chaocipher.")

    result = []
    for char in text:
        if char.isalpha():
            if char.islower():
                index = ord(char) - ord('a')
                result.append(key2[index])
            else:
                index = ord(char) - ord('A')
                result.append(key1[index])
        else:
            result.append(char)

    return ''.join(result)

hill

def hill(text, key, decrypt=False):
    n = len(key)
    if not (n > 1 and n % 2 == 0):
        raise ValueError("Key must be a 2x2 matrix for Hill cipher.")

    det = key[0][0] * key[1][1] - key[0][1] * key[1][0]
    det_inv = None

    for i in range(26):
        if (det * i) % 26 == 1:
            det_inv = i
            break

    if not det_inv:
        raise ValueError("Invalid key for Hill cipher.")

    if decrypt:
        key = [[key[1][1], -key[0][1]], [-key[1][0], key[0][0]]]
        key = [[(det_inv * x) % 26 for x in row] for row in key]

    result = []
    for i in range(0, len(text), 2):
        a = ord(text[i]) - ord('A')
        b = ord(text[i + 1]) - ord('A')
        if decrypt:
            result.append(chr(((key[0][0] * a + key[0][1] * b) % 26) + ord('A')))
            result.append(chr(((key[1][0] * a + key[1][1] * b) % 26) + ord('A')))
        else:
            result.append(chr(((key[0][0] * a + key[0][1] * b) % 26) + ord('A')))
            result.append(chr(((key[1][0] * a + key[1][1] * b) % 26) + ord('A')))

    return ''.join(result)

trimethius

def trimethius(text, key, decrypt=False):
    def generate_key_stream(key, length):
        key_stream = ''
        while len(key_stream) < length:
            key_stream += key
        return key_stream[:length]

    def shift(char, shift_amount, decrypt=False):
        if decrypt:
            shift_amount = -shift_amount
        return chr(((ord(char) - ord('A') + shift_amount) % 26) + ord('A'))

    if not key:
        raise ValueError("Key must be provided for Trimethius cipher.")

    key_stream = generate_key_stream(key.upper(), len(text))
    result = ''
    for i in range(len(text)):
        if text[i].isalpha():
            shift_amount = ord(key_stream[i]) - ord('A')
            result += shift(text[i].upper(), shift_amount, decrypt)
        else:
            result += text[i]

    return result
optimisticninja commented 2 months ago

After seeing many examples and explanations on the internet, here are some implementation ideas for some of these missing encryption types:

affine

def affine(text, a, b, decrypt=False):
    if not (a and b):
        raise ValueError("Values for 'a' and 'b' must be provided for affine cipher.")

    if decrypt:
        a_inv = 0
        for i in range(26):
            if (a * i) % 26 == 1:
                a_inv = i
                break

        if not a_inv:
            raise ValueError("Invalid 'a' value for decryption in affine cipher.")

        b = -a_inv * b

    result = []
    for char in text:
        if char.isalpha():
            if char.islower():
                result.append(chr(((a_inv * (ord(char) - ord('a') - b)) % 26) + ord('a')))
            else:
                result.append(chr(((a_inv * (ord(char) - ord('A') - b)) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

autokey

def autokey(text, key, decrypt=False):
    result = []
    key_index = 0
    for char in text:
        if char.isalpha():
            if decrypt:
                key_char = key[key_index]
            else:
                key_char = char

            key_index += 1

            if key_index == len(key):
                key_index = 0

            shift = ord(key_char.lower()) - ord('a')

            if char.islower():
                result.append(chr(((ord(char) - ord('a') - shift) % 26) + ord('a')))
            else:
                result.append(chr(((ord(char) - ord('A') - shift) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

beaufort

def beaufort(text, key, decrypt=False):
    if not key:
        raise ValueError("Key must be provided for Beaufort cipher.")

    result = []
    for char, key_char in zip(text, key):
        if char.isalpha():
            shift = ord(key_char.lower()) - ord('a')
            if decrypt:
                shift *= -1

            if char.islower():
                result.append(chr(((ord(char) - ord('a') - shift) % 26) + ord('a')))
            else:
                result.append(chr(((ord(char) - ord('A') - shift) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

caesar

def caesar(text, shift, decrypt=False):
    if decrypt:
        shift = -shift

    result = []
    for char in text:
        if char.isalpha():
            if char.islower():
                result.append(chr(((ord(char) - ord('a') + shift) % 26) + ord('a')))
            else:
                result.append(chr(((ord(char) - ord('A') + shift) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

chaocipher

def chaocipher(text, key1, key2):
    if not (key1 and key2):
        raise ValueError("Key1 and Key2 must be provided for Chaocipher.")

    result = []
    for char in text:
        if char.isalpha():
            if char.islower():
                index = ord(char) - ord('a')
                result.append(key2[index])
            else:
                index = ord(char) - ord('A')
                result.append(key1[index])
        else:
            result.append(char)

    return ''.join(result)

hill

def hill(text, key, decrypt=False):
    n = len(key)
    if not (n > 1 and n % 2 == 0):
        raise ValueError("Key must be a 2x2 matrix for Hill cipher.")

    det = key[0][0] * key[1][1] - key[0][1] * key[1][0]
    det_inv = None

    for i in range(26):
        if (det * i) % 26 == 1:
            det_inv = i
            break

    if not det_inv:
        raise ValueError("Invalid key for Hill cipher.")

    if decrypt:
        key = [[key[1][1], -key[0][1]], [-key[1][0], key[0][0]]]
        key = [[(det_inv * x) % 26 for x in row] for row in key]

    result = []
    for i in range(0, len(text), 2):
        a = ord(text[i]) - ord('A')
        b = ord(text[i + 1]) - ord('A')
        if decrypt:
            result.append(chr(((key[0][0] * a + key[0][1] * b) % 26) + ord('A')))
            result.append(chr(((key[1][0] * a + key[1][1] * b) % 26) + ord('A')))
        else:
            result.append(chr(((key[0][0] * a + key[0][1] * b) % 26) + ord('A')))
            result.append(chr(((key[1][0] * a + key[1][1] * b) % 26) + ord('A')))

    return ''.join(result)

trimethius

def trimethius(text, key, decrypt=False):
    def generate_key_stream(key, length):
        key_stream = ''
        while len(key_stream) < length:
            key_stream += key
        return key_stream[:length]

    def shift(char, shift_amount, decrypt=False):
        if decrypt:
            shift_amount = -shift_amount
        return chr(((ord(char) - ord('A') + shift_amount) % 26) + ord('A'))

    if not key:
        raise ValueError("Key must be provided for Trimethius cipher.")

    key_stream = generate_key_stream(key.upper(), len(text))
    result = ''
    for i in range(len(text)):
        if text[i].isalpha():
            shift_amount = ord(key_stream[i]) - ord('A')
            result += shift(text[i].upper(), shift_amount, decrypt)
        else:
            result += text[i]

    return result

This is great work. As we only need decryptions, I'd recommend stripping out the encryption side and aligning method signatures to how we are calling other crypto functions. Thanks @d4v1-sudo

d4v1-sudo commented 2 months ago

Removing the encryption part - leaving only the decryption part, trying to aligning method signatures to how we are calling other crypto functions and adding more types of decryption :

affine

def affine(text, a, b):
    if not (a and b):
        raise ValueError("Values for 'a' and 'b' must be provided for affine cipher decryption.")

    a_inv = 0
    for i in range(26):
        if (a * i) % 26 == 1:
            a_inv = i
            break

    if not a_inv:
        raise ValueError("Invalid 'a' value for decryption in affine cipher.")

    b = -a_inv * b

    result = []
    for char in text:
        if char.isalpha():
            if char.islower():
                result.append(chr(((a_inv * (ord(char) - ord('a') - b)) % 26) + ord('a')))
            else:
                result.append(chr(((a_inv * (ord(char) - ord('A') - b)) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

autokey

def autokey(text, key):
    result = []
    key_index = 0
    for char in text:
        if char.isalpha():
            key_char = key[key_index]

            key_index += 1

            if key_index == len(key):
                key_index = 0

            shift = ord(key_char.lower()) - ord('a')

            if char.islower():
                result.append(chr(((ord(char) - ord('a') - shift) % 26) + ord('a')))
            else:
                result.append(chr(((ord(char) - ord('A') - shift) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

beaufort

def beaufort(text, key):
    if not key:
        raise ValueError("Key must be provided for Beaufort cipher decryption.")

    result = []
    for char, key_char in zip(text, key):
        if char.isalpha():
            shift = ord(key_char.lower()) - ord('a')

            if char.islower():
                result.append(chr(((ord(char) - ord('a') - shift) % 26) + ord('a')))
            else:
                result.append(chr(((ord(char) - ord('A') - shift) % 26) + ord('A')))
        else:
            result.append(char)

    return ''.join(result)

chaocipher

def chaocipher(text, key1, key2):
    if not (key1 and key2):
        raise ValueError("Key1 and Key2 must be provided for Chaocipher decryption.")

    result = []
    for char in text:
        if char.isalpha():
            if char.islower():
                index = ord(char) - ord('a')
                result.append(key2[index])
            else:
                index = ord(char) - ord('A')
                result.append(key1[index])
        else:
            result.append(char)

    return ''.join(result)

hill

def hill(text, key):
    n = len(key)
    if not (n > 1 and n % 2 == 0):
        raise ValueError("Key must be a 2x2 matrix for Hill cipher decryption.")

    det = key[0][0] * key[1][1] - key[0][1] * key[1][0]
    det_inv = None

    for i in range(26):
        if (det * i) % 26 == 1:
            det_inv = i
            break

    if not det_inv:
        raise ValueError("Invalid key for Hill cipher decryption.")

    key = [[key[1][1], -key[0][1]], [-key[1][0], key[0][0]]]
    key = [[(det_inv * x) % 26 for x in row] for row in key]

    result = []
    for i in range(0, len(text), 2):
        a = ord(text[i]) - ord('A')
        b = ord(text[i + 1]) - ord('A')
        result.append(chr(((key[0][0] * a + key[0][1] * b) % 26) + ord('A')))
        result.append(chr(((key[1][0] * a + key[1][1] * b) % 26) + ord('A')))

    return ''.join(result)

trimethius

def trimethius(text, key):
    def generate_key_stream(key, length):
        key_stream = ''
        while len(key_stream) < length:
            key_stream += key
        return key_stream[:length]

    def shift(char, shift_amount):
        return chr(((ord(char) - ord('A') + shift_amount) % 26) + ord('A'))

    if not key:
        raise ValueError("Key must be provided for Trimethius cipher decryption.")

    key_stream = generate_key_stream(key.upper(), len(text))
    result = ''
    for i in range(len(text)):
        if text[i].isalpha():
            shift_amount = ord(key_stream[i]) - ord('A')
            result += shift(text[i].upper(), shift_amount)
        else:
            result += text[i]

    return result

caesar_box

def caesar_box(text, key):
    rows = key
    cols = len(text) // key

    result = [''] * len(text)
    for r in range(rows):
        for c in range(cols):
            result[c * rows + r] = text[r * cols + c]

    return ''.join(result)

columnar_transposition

def columnar_transposition(text, key):
    key_order = sorted(range(len(key)), key=lambda k: key[k])

    num_cols = len(key)
    num_rows = -(-len(text) // num_cols)

    grid = [[''] * num_cols for _ in range(num_rows)]

    for i, char in enumerate(text):
        grid[i // num_cols][key_order[i % num_cols]] = char

    result = ''
    for col in range(num_cols):
        for row in range(num_rows):
            result += grid[row][col]

    return result

double

def double(text, key1, key2):
    return caesar_box(columnar_transposition(text, key2), key1)

myszkowski

def myszkowski(text, key):
    sorted_key = sorted(key)
    num_cols = len(key)

    num_rows = -(-len(text) // num_cols)
    sorted_key_index = {k: i for i, k in enumerate(sorted_key)}
    last_row_fill = len(text) % num_cols

    grid = [[''] * num_cols for _ in range(num_rows)]

    index = 0
    for col in range(num_cols):
        for row in range(num_rows):
            if row == num_rows - 1 and col >= last_row_fill:
                continue
            grid[row][sorted_key_index[key[col]]] = text[index]
            index += 1

    result = ''
    for row in range(num_rows):
        for col in range(num_cols):
            result += grid[row][col]

    return result

railfence

def railfence(text, key):
    fence = [['' for _ in range(len(text))] for _ in range(key)]
    rail = 0
    direction = 1

    for char in text:
        fence[rail][direction] = char
        rail += direction

        if rail == key - 1 or rail == 0:
            direction = -direction

    result = ''
    for i in range(key):
        for j in range(len(text)):
            if fence[i][j]:
                result += fence[i][j]

    return result

route

def route(text, key):
    num_rows = len(text) // key
    remainder = len(text) % key

    rows = [num_rows] * key
    for i in range(remainder):
        rows[i] += 1

    index = 0
    grid = [[''] * len(text) for _ in range(key)]
    for r in range(key):
        for c in range(rows[r]):
            grid[r][c] = text[index]
            index += 1

    result = ''
    for c in range(len(text)):
        for r in range(key):
            if grid[r][c]:
                result += grid[r][c]

    return result

concealment

def concealment(text, key):
    chunks = [text[i:i + key] for i in range(0, len(text), key)]
    max_len = max(map(len, chunks))

    padded_chunks = [chunk.ljust(max_len, ' ') for chunk in chunks]

    result = ''
    for i in range(max_len):
        for chunk in padded_chunks:
            if chunk[i] != ' ':
                result += chunk[i]

    return result