Шифр "Кузнечика"

Всем доброго дня! Пишу алгоритмы шифрования "Кузнечик" для Клиент-Сервера, но получается не оч. Я Бы сказал, никак.

До этого делал ключ Хеллмана, шифр Цезаря и Виженера ( +- рабоатет)

Пишу всё на Python, кто-нибудь делал и с чего вообще можно начать. Или какие-нибудь библиотеки там.

Всем большое спасибо за ответы и предложения!


Ответы (1 шт):

Автор решения: jitco co

Программа сыра. Вот результат ее исполнения:

Введите ключ: 23456789087654FFFF
Введите сообщение: РАК
Шифр текст (зашифрованное сообщение с помощью ключа):
1b026ae958130e363bf60c8ed1cc3f53
Расшифрованное сообщение в виде байт:
b'#%\xc7\x02\x7f\xc9k\x90\x8cx\x80UTN\xd6\xac'
Расшифрованное сообщение:
Не удается декодировать сообщение.
>>>
BLOCK_SIZE = 16  # длина блока 

КОД:

# таблица прямого нелинейного преобразования
Pi = [
    0xFC, 0xEE, 0xDD, 0x11, 0xCF, 0x6E, 0x31, 0x16,
    0xFB, 0xC4, 0xFA, 0xDA, 0x23, 0xC5, 0x04, 0x4D,
    0xE9, 0x77, 0xF0, 0xDB, 0x93, 0x2E, 0x99, 0xBA,
    0x17, 0x36, 0xF1, 0xBB, 0x14, 0xCD, 0x5F, 0xC1,
    0xF9, 0x18, 0x65, 0x5A, 0xE2, 0x5C, 0xEF, 0x21,
    0x81, 0x1C, 0x3C, 0x42, 0x8B, 0x01, 0x8E, 0x4F,
    0x05, 0x84, 0x02, 0xAE, 0xE3, 0x6A, 0x8F, 0xA0,
    0x06, 0x0B, 0xED, 0x98, 0x7F, 0xD4, 0xD3, 0x1F,
    0xEB, 0x34, 0x2C, 0x51, 0xEA, 0xC8, 0x48, 0xAB,
    0xF2, 0x2A, 0x68, 0xA2, 0xFD, 0x3A, 0xCE, 0xCC,
    0xB5, 0x70, 0x0E, 0x56, 0x08, 0x0C, 0x76, 0x12,
    0xBF, 0x72, 0x13, 0x47, 0x9C, 0xB7, 0x5D, 0x87,
    0x15, 0xA1, 0x96, 0x29, 0x10, 0x7B, 0x9A, 0xC7,
    0xF3, 0x91, 0x78, 0x6F, 0x9D, 0x9E, 0xB2, 0xB1,
    0x32, 0x75, 0x19, 0x3D, 0xFF, 0x35, 0x8A, 0x7E,
    0x6D, 0x54, 0xC6, 0x80, 0xC3, 0xBD, 0x0D, 0x57,
    0xDF, 0xF5, 0x24, 0xA9, 0x3E, 0xA8, 0x43, 0xC9,
    0xD7, 0x79, 0xD6, 0xF6, 0x7C, 0x22, 0xB9, 0x03,
    0xE0, 0x0F, 0xEC, 0xDE, 0x7A, 0x94, 0xB0, 0xBC,
    0xDC, 0xE8, 0x28, 0x50, 0x4E, 0x33, 0x0A, 0x4A,
    0xA7, 0x97, 0x60, 0x73, 0x1E, 0x00, 0x62, 0x44,
    0x1A, 0xB8, 0x38, 0x82, 0x64, 0x9F, 0x26, 0x41,
    0xAD, 0x45, 0x46, 0x92, 0x27, 0x5E, 0x55, 0x2F,
    0x8C, 0xA3, 0xA5, 0x7D, 0x69, 0xD5, 0x95, 0x3B,
    0x07, 0x58, 0xB3, 0x40, 0x86, 0xAC, 0x1D, 0xF7,
    0x30, 0x37, 0x6B, 0xE4, 0x88, 0xD9, 0xE7, 0x89,
    0xE1, 0x1B, 0x83, 0x49, 0x4C, 0x3F, 0xF8, 0xFE,
    0x8D, 0x53, 0xAA, 0x90, 0xCA, 0xD8, 0x85, 0x61,
    0x20, 0x71, 0x67, 0xA4, 0x2D, 0x2B, 0x09, 0x5B,
    0xCB, 0x9B, 0x25, 0xD0, 0xBE, 0xE5, 0x6C, 0x52,
    0x59, 0xA6, 0x74, 0xD2, 0xE6, 0xF4, 0xB4, 0xC0,
    0xD1, 0x66, 0xAF, 0xC2, 0x39, 0x4B, 0x63, 0xB6
]

# таблица обратного нелинейного преобразования
reverse_Pi = [
    0xA5, 0x2D, 0x32, 0x8F, 0x0E, 0x30, 0x38, 0xC0,
    0x54, 0xE6, 0x9E, 0x39, 0x55, 0x7E, 0x52, 0x91,
    0x64, 0x03, 0x57, 0x5A, 0x1C, 0x60, 0x07, 0xE0,
    0x98, 0x06, 0x4F, 0xB2, 0xD3, 0x1D, 0xAA, 0xE5,
    0x3E, 0xFD, 0xC3, 0x80, 0x0B, 0x0D, 0x87, 0xA7,
    0x6D, 0x82, 0x7A, 0x7C, 0x95, 0xCA, 0xA0, 0x35,
    0xE8, 0x55, 0x21, 0xE2, 0xFF, 0x99, 0x49, 0xAE,
    0x65, 0x80, 0xF4, 0x00, 0x03, 0xA1, 0x98, 0x6D,
    0x0B, 0xBF, 0xD6, 0x6A, 0x97, 0x71, 0x63, 0xDF,
    0xC6, 0x57, 0x3D, 0xC7, 0xC1, 0x3F, 0x1A, 0x12,
    0xEB, 0x2A, 0x8A, 0x96, 0xA4, 0x92, 0x1E, 0x7B,
    0x9C, 0x80, 0x83, 0x62, 0x8B, 0x78, 0xA9, 0xF3,
    0x99, 0x2A, 0xB0, 0x1C, 0x9F, 0xE3, 0x14, 0x19,
    0x21, 0xA8, 0x3A, 0x61, 0x7F, 0x26, 0x40, 0xFB,
    0xEE, 0x3E, 0xD9, 0x24, 0xC5, 0xF0, 0xE7, 0x05,
    0x5F, 0x49, 0x56, 0x6F, 0x3B, 0x1D, 0x77, 0x6C,
    0x0C, 0xD5, 0x45, 0x11, 0xA3, 0x48, 0xFC, 0x47,
    0x2C, 0xC2, 0x37, 0xB3, 0x84, 0xAA, 0x76, 0x27,
    0x79, 0x36, 0xC9, 0x4D, 0x58, 0x50, 0x1B, 0x1F,
    0x28, 0x02, 0x41, 0xAD, 0x6E, 0x51, 0xC4, 0x1E,
    0xAB, 0x93, 0xD4, 0x88, 0xF2, 0x3C, 0x7D, 0xB8,
    0xD7, 0xCB, 0xDE, 0xB5, 0x33, 0xCE, 0xD8, 0x0D,
    0x5B, 0xCD, 0x6B, 0x90, 0xF6, 0x53, 0xBE, 0x4A,
    0x3A, 0xFA, 0x7A, 0xDA, 0x85, 0x8D, 0x75, 0x43,
    0x8C, 0x66, 0x25, 0xC8, 0xA6, 0x4E, 0xDD, 0xEF,
    0x8E, 0x67, 0x13, 0x0F, 0x23, 0xF9, 0x81, 0x74,
    0x42, 0x04, 0x0A, 0xB1, 0xDC, 0x5E, 0x22, 0x86,
    0x09, 0x68, 0xF8, 0x70, 0xEC, 0x94, 0x8C, 0xBC,
    0xF7, 0xF5, 0xCF, 0x29, 0x4B, 0x2E, 0xED, 0xAC,
    0x46, 0x72, 0x44, 0x69, 0x17, 0xF1, 0xD1, 0x18,
    0xB4, 0xE4, 0x73, 0x5D, 0xD0, 0x15, 0x20, 0x59,
    0x2F, 0x89, 0x10, 0x31, 0xE1, 0xBA, 0xEA, 0x1A,
    0xFF, 0xE9, 0x9B, 0x16, 0x34, 0xBB, 0xB7, 0x9A,
    0xBD, 0x6C, 0xA2, 0x2B, 0x05, 0xA9, 0xFE, 0xC2,
    0x58, 0x33, 0x4C, 0x08, 0xB9, 0xEB, 0x50, 0x4A
]
# Вектор линейного преобразования
l_vec = [1, 148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148]

# Массив для хранения констант
iter_C = [[0] * BLOCK_SIZE for _ in range(32)]

# Массив для хранения ключей
iter_key = [[0] * 64 for _ in range(10)]


def kuz_x(a, b):
    if len(a) != len(b):
        # Выравнивание массивов до одинаковой длины
        max_len = max(len(a), len(b))
        a += bytearray(max_len - len(a))
        b += bytearray(max_len - len(b))
    c = bytearray(len(a))
    for i in range(len(a)):
        c[i] = a[i] ^ b[i]
    return c

# Функция S
def kuz_s(in_data):
    out_data = bytearray(BLOCK_SIZE)
    for i in range(BLOCK_SIZE):
        out_data[i] = Pi[in_data[i]]
    return bytes(out_data)

# Функция L
def kuz_l(in_data):
    out_data = bytearray(in_data)
    for _ in range(16):
        a_15 = out_data[-1]
        for i in range(BLOCK_SIZE - 1, 0, -1):
            out_data[i] = out_data[i - 1]
            a_15 ^= gf_mul(out_data[i], l_vec[i])
        # Ограничение значения a_15 до диапазона от 0 до 255
        a_15 %= 256
        out_data[0] = a_15
    return bytes(out_data)

# Функция обратного преобразования S
def reverse_kuz_s(in_data):
    out_data = bytearray(BLOCK_SIZE)
    for i in range(BLOCK_SIZE):
        out_data[i] = reverse_Pi[in_data[i]]
    return bytes(out_data)

# Функция обратного преобразования L
def reverse_kuz_l(in_data):
    out_data = bytearray(in_data)
    for _ in range(16):
        a_0 = out_data[0]
        for i in range(1, BLOCK_SIZE):
            out_data[i - 1] = out_data[i]
            a_0 ^= gf_mul(out_data[i], l_vec[i])
        # Ограничение значения a_0 до диапазона от 0 до 255
        a_0 %= 256
        out_data[-1] = a_0
    return bytes(out_data)


# Функция умножения в поле Галуа
def gf_mul(a, b):
    c = 0
    for _ in range(8):
        if b & 1:
            c ^= a
        hi_bit = a & 0x80
        a <<= 1
        if hi_bit:
            a ^= 0xc3  # Полином x^8 + x^7 + x^6 + x + 1
        b >>= 1
    return c

# Функция расчета констант
def get_iter_C():
    iter_num = [[0] * BLOCK_SIZE for _ in range(32)]
    for i in range(32):
        iter_num[i][0] = i + 1
    for i in range(32):
        iter_C[i] = kuz_l(iter_num[i])

# Функция, выполняющая преобразования ячейки Фейстеля
def kuz_f(in_key_1, in_key_2, iter_const):
    internal = kuz_x(in_key_1, iter_const)
    internal = kuz_s(internal)
    internal = kuz_l(internal)
    out_key_1 = kuz_x(internal, in_key_2)
    out_key_2 = in_key_1
    return out_key_1, out_key_2

# Функция расчета раундовых ключей
def expand_key(key_1, key_2):
    get_iter_C()
    iter_key[0] = key_1
    iter_key[1] = key_2
    iter12 = [key_1, key_2]
    for i in range(4):
        iter34 = kuz_f(iter12[0], iter12[1], iter_C[0 + 8 * i])
        iter12 = kuz_f(iter34[0], iter34[1], iter_C[1 + 8 * i])
        iter34 = kuz_f(iter12[0], iter12[1], iter_C[2 + 8 * i])
        iter12 = kuz_f(iter34[0], iter34[1], iter_C[3 + 8 * i])
        iter34 = kuz_f(iter12[0], iter12[1], iter_C[4 + 8 * i])
        iter12 = kuz_f(iter34[0], iter34[1], iter_C[5 + 8 * i])
        iter34 = kuz_f(iter12[0], iter12[1], iter_C[6 + 8 * i])
        iter12 = kuz_f(iter34[0], iter34[1], iter_C[7 + 8 * i])
        iter_key[2 * i + 2] = iter12[0]
        iter_key[2 * i + 3] = iter12[1]

def kuz_encrypt_block(blk):
    out_blk = blk
    for i in range(9):
        out_blk = kuz_x(iter_key[i], out_blk)
        out_blk = kuz_s(out_blk)
        out_blk = kuz_l(out_blk)
    out_blk = kuz_x(out_blk, iter_key[9])
    return out_blk


def kuz_decrypt_block(blk):
    out_blk = blk
    out_blk = kuz_x(out_blk, iter_key[9])
    for i in range(8, -1, -1):
        out_blk = reverse_kuz_l(out_blk)
        out_blk = reverse_kuz_s(out_blk)
        out_blk = kuz_x(iter_key[i], out_blk)
    return out_blk
def main():
    key = input("Введите ключ: ")
    message = input("Введите сообщение: ")
    encrypted_message = encrypt_message(key, message)
    print("Шифр текст (зашифрованное сообщение с помощью ключа):")
    print(encrypted_message)
    decrypted_message = decrypt_message(key, encrypted_message)
    print("Расшифрованное сообщение в виде байт:")
    print(decrypted_message)
    try:
        print("Расшифрованное сообщение:")
        print(decrypted_message.decode('ascii'))  # Попытка декодировать как ASCII
    except UnicodeDecodeError:
        print("Не удается декодировать сообщение.")

def encrypt_message(key, message):
    key_bytes = bytes.fromhex(key)
    message_bytes = message.encode('windows-1251')  # Используем кодировку windows-1251
    if len(message_bytes) % BLOCK_SIZE != 0:
        message_bytes += bytes(BLOCK_SIZE - len(message_bytes) % BLOCK_SIZE)
    expand_key(key_bytes[:32], key_bytes[32:])
    encrypted_message = b''
    for i in range(0, len(message_bytes), BLOCK_SIZE):
        block = message_bytes[i:i+BLOCK_SIZE]
        encrypted_block = kuz_encrypt_block(block)
        encrypted_message += encrypted_block
    return encrypted_message.hex()


def decrypt_message(key, encrypted_message):
    key_bytes = bytes.fromhex(key)
    encrypted_message_bytes = bytes.fromhex(encrypted_message)
    decrypted_message = b''
    for i in range(0, len(encrypted_message_bytes), BLOCK_SIZE):
        block = encrypted_message_bytes[i:i+BLOCK_SIZE]
        decrypted_block = kuz_decrypt_block(block)
        decrypted_message += decrypted_block
    decrypted_message = decrypted_message.rstrip(b'\x00')  # Удаление нулевых байтов после декодирования
    return decrypted_message



if __name__ == "__main__":
    main()

Вот еще один вариант:

import binascii

# Функция XOR для двух строк
def xor_func (input1AsString, input2AsString, in_code = 16):
    input1AsInteger = int(input1AsString, in_code)
    input2AsInteger = int(input2AsString, in_code)
    result = input1AsInteger ^ input2AsInteger
    resultAsHex = hex(result)
    resultAsHex = resultAsHex.upper()
    resultAsHex = resultAsHex[2:]
    if len(resultAsHex) != len(input1AsString):
        for i in range(len(input1AsString) - len(resultAsHex)):
            resultAsHex = '0' + resultAsHex
    return resultAsHex
# Пример:
# xor_func('101', '10', in_code = 2)

# Функция перевода между системами счисления
def convert_base(num, to_base = 10, from_base = 10):
    # Преобразование в десятичное число
    if isinstance(num, str):
        n = int(num, from_base)
    else:
        n = int(num)
    # Преобразование десятичного числа в необходимую систему счисления
    alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    if n < to_base:
        return alphabet[n]
    else:
        return convert_base(n // to_base, to_base) + alphabet[n % to_base]
# Пример:
# convert_base('AC', from_base=16)

# Ряд Галуа
galua_coef = [148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148, 1]
galua_coef_reverse = [1, 148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148]
# Таблица степеней двойки
galua_fields = [
    1, 2, 4, 8, 16, 32, 64, 128, 195, 69, 138, 215, 109, 218, 119, 238,
    31, 62, 124, 248, 51, 102, 204, 91, 182, 175, 157, 249, 49, 98, 196,
    75, 150, 239, 29, 58, 116, 232, 19, 38, 76, 152, 243, 37, 74, 148,
    235, 21, 42, 84, 168, 147, 229, 9, 18, 36, 72, 144, 227, 5, 10, 20, 
    40, 80, 160, 131, 197, 73, 146, 231, 13, 26, 52, 104, 208, 99, 198,
    79, 158, 255, 61, 122, 244, 43, 86, 172, 155, 245, 41, 82, 164, 139,
    213, 105, 210, 103, 206, 95, 190, 191, 189, 185, 177, 161, 129, 193,
    65, 130, 199, 77, 154, 247, 45, 90, 180, 171, 149, 233, 17, 34, 68,
    136, 211, 101, 202, 87, 174, 159, 253, 57, 114, 228, 11, 22, 44, 88,
    176, 163, 133, 201, 81, 162, 135, 205, 89, 178, 167, 141, 217, 113,
    226, 7, 14, 28, 56, 112, 224, 3, 6, 12, 24, 48, 96, 192, 67, 134, 207,
    93, 186, 183, 173, 153, 241, 33, 66, 132, 203, 85, 170, 151, 237, 25,
    50, 100, 200, 83, 166, 143, 221, 121, 242, 39, 78, 156, 251, 53, 106,
    212, 107, 214, 111, 222, 127, 254, 63, 126, 252, 59, 118, 236, 27, 54,
    108, 216, 115, 230, 15, 30, 60, 120, 240, 35, 70, 140, 219, 117, 234,
    23, 46, 92, 184, 179, 165, 137, 209, 97, 194, 71, 142, 223, 125, 250, 
    55, 110, 220, 123, 246, 47, 94, 188, 187, 181, 169, 145, 225, 1]

# Линейное преобразование для блока длины 32
def linear_transformation(num, move = 'straight'):
    # Подставляемое число, если число, образованное 2-мя подряд 
    # идущими цифрами в 16-ой системе счисления, равно 0
    numIfNull = 257

    # Линейная функция выполняется 16 раз
    for i in range(16):
        # Массив индексов таблицы степеней двойки `galua_fields` коэффициентов ряда Галуа
        coefs = []
        # Массив индексов таблицы степеней двойки `galua_fields`,
        # полученных от чисел, образованных 2-мя подряд идущими 
        # цифрами в 16-ой системе счисления
        nums = []

        # Заполнение массивов
        for j in range(len(galua_coef)):
            if move == 'reverse':
                coefs.append(galua_fields.index(galua_coef_reverse[ len(galua_coef_reverse) - j - 1 ]))
            else:
                coefs.append(galua_fields.index(galua_coef[ len(galua_coef) - j - 1 ]))
            if int(convert_base(num[j * 2 : j * 2 + 2], from_base=16)) == 0:
                nums.append(numIfNull)
            else:
                nums.append(galua_fields.index(int(convert_base(num[j * 2 : j * 2 + 2], from_base=16))))

        # Массив значений, полученных из таблицы степеней двойки `galua_fields`
        galua = []

        # Заполнение массива
        for j in range(len(galua_coef)):
            if nums[j] != numIfNull:
                # Проверка, что сумма индексов не более длины таблицы степеней двойки `galua_fields`
                if nums[j] + coefs[j] <= 255:
                    galua.append(galua_fields[nums[j] + coefs[j]])
                else:
                    galua.append(galua_fields[(nums[j] + coefs[j]) % 255])

        # Подсчитывание числа, которое необходимо прибавить к концу входного блока
        galua_num = galua[0]
        if len(galua) != 1:
            for j in range(len(galua) - 1):
                # XOR массива значений, полученных из таблицы степеней двойки `galua_fields`
                galua_num = int(xor_func(str(galua_num), str(galua[j + 1]), in_code = 10), 16) % 256
        galua_num = hex(galua_num)[2:]
        # Проверка, если длина полученного числа равна 1
        if len(str(galua_num)) == 1:
            galua_num = '0' + str(galua_num)

        # Сдвиг с добавлением полученного числа
        if move == 'reverse':
            num = galua_num + num[:len(num)-2]
        else:
            num = num[2:] + galua_num
    return num
# Пример:
# linear_transformation('02000000000000000000000000000000')

# Таблица для прямого хода (straight)
nonlinear_coef = [
    252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4,
    77, 233, 119, 240, 219, 147, 46, 153, 186, 23, 54, 241, 187, 20, 205,
    95, 193, 249, 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, 139, 1,
    142, 79, 5, 132, 2, 174, 227, 106, 143, 160, 6, 11, 237, 152, 127,
    212, 211, 31, 235, 52, 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 
    253, 58, 206, 204, 181, 112, 14, 86, 8, 12, 118, 18, 191, 114, 19, 71,
    156, 183, 93, 135, 21, 161, 150, 41, 16, 123, 154, 199, 243, 145, 120,
    111, 157, 158, 178, 177, 50, 117, 25, 61, 255, 53, 138, 126, 109, 84,
    198, 128, 195, 189, 13, 87, 223, 245, 36, 169, 62, 168, 67, 201, 215,
    121, 214, 246, 124, 34, 185, 3, 224, 15, 236, 222, 122, 148, 176, 188, 
    220, 232, 40, 80, 78, 51, 10, 74, 167, 151, 96, 115, 30, 0, 98, 68,
    26, 184, 56, 130, 100, 159, 38, 65, 173, 69, 70, 146, 39, 94, 85, 47,
    140, 163, 165, 125, 105, 213, 149, 59, 7, 88, 179, 64, 134, 172, 29,
    247, 48, 55, 107, 228, 136, 217, 231, 137, 225, 27, 131, 73, 76, 63,
    248, 254, 141, 83, 170, 144, 202, 216, 133, 97, 32, 113, 103, 164, 45,
    43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82, 89, 166, 116, 210,
    230, 244, 180, 192, 209, 102, 175, 194, 57, 75, 99, 182]

# Таблица для обратного хода (reverse)
nonlinear_coef_reverse = [
    165, 45, 50, 143, 14, 48, 56, 192, 84, 230, 158, 57, 85, 126, 82, 145,
    100, 3, 87, 90, 28, 96, 7, 24, 33, 114, 168, 209, 41, 198, 164, 63,
    224, 39, 141, 12, 130, 234, 174, 180, 154, 99, 73, 229, 66, 228, 21,
    183, 200, 6, 112, 157, 65, 117, 25, 201, 170, 252, 77, 191, 42, 115,
    132, 213, 195, 175, 43, 134, 167, 177, 178, 91, 70, 211, 159, 253,
    212, 15, 156, 47, 155, 67, 239, 217, 121, 182, 83, 127, 193, 240, 35,
    231, 37, 94, 181, 30, 162, 223, 166, 254, 172, 34, 249, 226, 74, 188,
    53, 202, 238, 120, 5, 107, 81, 225, 89, 163, 242, 113, 86, 17, 106,
    137, 148, 101, 140, 187, 119, 60, 123, 40, 171, 210, 49, 222, 196, 95,
    204, 207, 118, 44, 184, 216, 46, 54, 219, 105, 179, 20, 149, 190, 98,
    161, 59, 22, 102, 233, 92, 108, 109, 173, 55, 97, 75, 185, 227, 186,
    241, 160, 133, 131, 218, 71, 197, 176, 51, 250, 150, 111, 110, 194,
    246, 80, 255, 93, 169, 142, 23, 27, 151, 125, 236, 88, 247, 31, 251,
    124, 9, 13, 122, 103, 69, 135, 220, 232, 79, 29, 78, 4, 235, 248, 243,
    62, 61, 189, 138, 136, 221, 205, 11, 19, 152, 2, 147, 128, 144, 208,
    36, 52, 203, 237, 244, 206, 153, 16, 68, 64, 146, 58, 1, 38, 18, 26,
    72, 104, 245, 129, 139, 199, 214, 32, 10, 8, 0, 76, 215, 116]

# Нелинейное преобразование (шифр простой замены)
def nonlinear_transformation(num, move = 'straight'):
    for i in range(16):
        # Выбор таблицы для хода
        if move == 'reverse':
            nonlinear_table = nonlinear_coef_reverse
        else:
            nonlinear_table = nonlinear_coef

        # Выборка пары цифр, которые будут заменены в соответствии
        # с таблицей `nonlinear_coef` или `nonlinear_coef_reverse`
        num_for_replace = num[i * 2 : i * 2 + 2]
        # Преобразование числа через таблицы хода
        convert_num = convert_base(num_for_replace, to_base = 10, from_base = 16)
        num_for_replace = convert_base(nonlinear_table[int(convert_num)], to_base = 16, from_base = 10)
        if len(num_for_replace) == 1:
            num_for_replace = '0' + num_for_replace

        # Возврат числа
        num = num[: i * 2] + num_for_replace + num[i * 2 + 2:]
    return num


X = xor_func # операция XOR
S = nonlinear_transformation # нелинейное преобразование в режиме простой замены
L = linear_transformation # линейное преобразование

# Ключ шифрования должен быть задан в 16-ричной системе счисления
# key = '7766554433221100FFEEDDCCBBAA9988EFCDAB89674523011032547698BADCFE'

#
def utf8ToHex(text):
    text = binascii.hexlify(text.encode('utf8')).decode('utf8')
    return text

def transformKey(key):
    key = binascii.hexlify(key.encode('utf8')).decode('utf8')
    count = 64 - len(key) % 64
    while len(key) < 64:
        key += key
    return key[:64]

# turn text from hex to utf8
def hexToUtf8(text):
    try:
        text = binascii.unhexlify(text).decode('utf8')
    except:
        pass
    return text

def getKeys(key):


    C = [] # константы
    F = [] # ячейки Фейстеля
    K = [key[:int(len(key) / 2)], key[int(len(key) / 2) :]]

    for i in range(32):
        if len(hex(i + 1)[2:]) == 1:
            C.append(L('0' + hex(i + 1)[2:] + '000000000000000000000000000000').upper())
        else:
            C.append(L(hex(i + 1)[2:] + '000000000000000000000000000000').upper())

    # формирование ячеек Фейстеля
    F.append([ K[1], X(L(S(X( K[0], C[0]))),  K[1])])
    for i in range(32):
        K = [ F[i][1], X(L(S(X( F[i][0], C[i]))),  F[i][1])]
        F.append(K)

    # разбиение заданного ключа пополам
    K = [key[:int(len(key) / 2)], key[int(len(key) / 2) :]]

    # формирование новых ключей из ячеек Фейстеля
    for i in range(len(F)):
        if (i + 1) % 8 == 0:
            K.append(F[i][0])
            K.append(F[i][1])
    return (K)

# Расшифровка текста
def encrypt(text, K):
    text = utf8ToHex(text)

    count = 32 - len(text) % 32
    if count != 0 and count != 32:
        for i in range(count):
            text += '0'
    textArray = []
    for i in range(int(len(text) / 32)):
        textArray.append(text[i * 32 : i * 32 + 32])

    textEncrypt = []
    for j in textArray:
        # шифрование текста
        textEncrypted = j
        for i in range(9):
            textEncrypted = L(S(X(textEncrypted, K[i])))
        textEncrypted = X(textEncrypted, K[9])
        textEncrypt.append(textEncrypted)
    return(''.join(textEncrypt))

# Шифрование текста
def decrypt(text, K):

    textArray = []
    for i in range(int(len(text) / 32)):
        textArray.append(text[i * 32 : i * 32 + 32])

    textDecrypt = []
    for j in textArray:
        # расшифровка текста
        textDecrypted = j
        for i in range(9, 0, -1):
            textDecrypted = S(L(X(textDecrypted, K[i]), 'reverse'), 'reverse')
        textDecrypted = X(textDecrypted, K[0])
        textDecrypt.append(textDecrypted)
    return(hexToUtf8(''.join(textDecrypt)))
# Функция text_to_hex принимает текстовую строку и кодирует ее 
# в формат UTF-8, а затем преобразует в шестнадцатеричное представление.
def text_to_hex(text):
    hex_value = text.encode('utf-8').hex()
    return hex_value

# Функция add_padding добавляет нули к шестнадцатеричной строке до тех пор, 
# пока ее длина не станет равной 32 символам, что соответствует 128 битам.
def add_padding(hex_value):
    while len(hex_value) < 32:  
        hex_value += '0'
    return hex_value

# Функция hex_to_text принимает шестнадцатеричную строку и декодирует ее 
# обратно в текстовую строку.
def hex_to_text(hex_value):
    text = bytes.fromhex(hex_value).decode('utf-8')
    return text

# Функция main является точкой входа в программу. 
# Она запрашивает у пользователя ввод текста, затем преобразует его 
# в шестнадцатеричное представление,
# добавляет нули при необходимости, выводит исходную шестнадцатеричную строку, 
# количество добавленных нулей, а затем конвертирует обратно в текст и выводит его.
def main():
    text = input("Введите текст: ")
    hex_value = text_to_hex(text)
    original_length = len(hex_value)

    padded_hex_value = add_padding(hex_value)
    padded_length = len(padded_hex_value)

    if padded_length > 32:
        excess = padded_length - 32
        padded_hex_value = padded_hex_value[:-excess]
        padded_length = len(padded_hex_value)

    converted_text = hex_to_text(padded_hex_value)
    
    textInput = padded_hex_value # Ввод исходного текста
    print( "Сообщение в 16ичной системе: \n",textInput)
    #password = "strongPassword" # Ввод пароля

    # Получение ключей для шифрования и расшифрования
    K = getKeys('8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef')
    K= input("Введите ключ: ")
    print( "Ключ: \n",K)
    # Шифрование
    textEncrypt = encrypt(textInput, K)
    print(" \n Зашифрованный текст:  \n", textEncrypt)
    print(" \n Исходный текст в шестнадцатеричном формате: \n", padded_hex_value)
    # Расшифрование
    textDecrypt = decrypt(textEncrypt, K)
    converted_text = hex_to_text(textDecrypt)
    print("\n Расшифрованный текст:  \n", converted_text)
if __name__ == "__main__":
    main()

Ссылки:

Информация которая помогла в написании кода и пример на java

Пример на питоне

→ Ссылка