首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >加密/解密算法#3

加密/解密算法#3
EN

Code Review用户
提问于 2021-04-29 22:48:18
回答 1查看 235关注 0票数 0

这是加密/解密算法#2加解密算法的后续问题。

在我的最后一个问题中,在@Reinderien的回答中,他添加了这一行from typing import List,以及类似于这个m: List[List[int]]的东西。这只是为了输入提示吗?

使用numpy数组而不是python列表会更快吗?

除了这些后续的问题,如果你看到任何需要改变的地方,现在就让我来。

代码语言:javascript
复制
import base64
import numpy as np
from randomgen import ChaCha
from getpass import getpass
from typing import List


def add_padding(plain_text: str, block_size: int = 128) -> str:
    plain_text = plain_text.encode()
    padding = -(len(plain_text) + 1) % block_size  # Amount of padding needed to fill block
    
    padded_text = plain_text + b'=' * padding + bytes([padding + 1])
    
    return padded_text


def xor_string(key: bytes, secret: bytes) -> bytes:
    xored_secret = b''
    
    for i in range(len(secret) // len(key)):
        if i > 0:
            key = get_round_key(key)
        
        some_decimals = secret[i * len(key):len(key) + (i * len(key))]
        xored_secret = xored_secret + b''.join(bytes([key[i] ^ some_decimals[i]]) for i in range(len(key)))
    
    return xored_secret


def get_round_key(key: bytes) -> bytes:
    last_col = key[15::16]
    
    # interweave
    last_col = b''.join(x for i in range(len(last_col) // 2) for x in (bytes([last_col[-i - 1]]), bytes([last_col[i]])))
    
    new_key = b''
    for current_col in [key[i::16] for i in range(16)]:
        current_col = xor_string(last_col, current_col)
        new_key = new_key + current_col[len(current_col) // 2:] + current_col[:len(current_col) // 2]
    
    return new_key


def generate_key(key: str) -> bytes:
    if len(key) >= 128:
        key = key.encode()
        return key[:128]
    elif len(key) < 128:
        
        key = key.encode()
        
        for i in range(128 - len(key)):
            key = key + bytes([(sum(key) // len(key)) ^ sum(1 << (8 - 1 - j) for j in range(8) if key[i] >> j & 1)])
        
        decimal = ''.join(str(i) for i in key)
        
        binary = f'{bin(int(decimal[len(decimal) // 2:] + decimal[:len(decimal) // 2]))[2:]:<01024s}'
        
        key = bin_to_bytes(binary[:1024])
        half1 = key[:len(key) // 2]
        half2 = key[len(key) // 2:]
        key = half2 + half1
        return key[:128]


def bytes_to_base64(binary: bytes) -> str:
    # ints = [int(binary[i * 8:8 + i * 8], 2) for i in range(len(binary) // 8)]
    return base64.b64encode(binary).decode()


def bin_to_decimal(binary: str, length: int = 8) -> List[int]:
    b = [
        binary[i * length:length + (i * length)]
        for i in range(len(binary) // length)
    ]
    
    decimal = [int(i, 2) for i in b]
    
    return decimal


def decimal_to_binary(decimal: List[int], length: int = 8) -> str:
    return ''.join(str(bin(num)[2:].zfill(length)) for num in decimal)


def base64_to_bytes(base: str) -> bytes:
    return base64.b64decode(base)


def matrix_to_bytes(m: List[List[int]]) -> bytes:
    return b''.join(bytes([m[i][j]]) for i in range(16) for j in range(8))


def obfuscate(secret: bytes, key: bytes, encrypting: bool, loops: int) -> bytes:
    shuffled_data = b''
    round_key = key
    
    for i in range(len(secret) // 128):
        if i > 0:
            round_key = get_round_key(round_key)
        
        if encrypting:
            m = [
                list(secret[j * 8 + i * 128:j * 8 + i * 128 + 8])
                for j in range(16)
            ]
            m = shuffle(m, round_key, loops)
            m = matrix_to_bytes(m)
            m = shift_bits(round_key, m)
            shuffled_data += xor_string(round_key, m)
        else:
            xor = xor_string(round_key, secret[i * 128:i * 128 + 128])
            xor = unshift_bits(round_key, xor)
            m = [list(xor[j * 8:j * 8 + 8]) for j in range(16)]
            m = reverse_shuffle(m, round_key, loops)
            shuffled_data += matrix_to_bytes(m)
    
    return xor_string(key, shuffled_data)


def shuffle(m: List[List[int]], key: int, loops: int) -> List[List[int]]:
    for j in range(loops):
        # move columns to the right
        m = [row[-1:] + row[:-1] for row in m]
        
        # move rows down
        m = m[-1:] + m[:-1]
        
        shuffled_m = [[0] * 8 for _ in range(16)]
        
        for idx, sidx in enumerate(test(key)):
            shuffled_m[idx // 8][idx % 8] = m[sidx // 8][sidx % 8]
        
        m = shuffled_m
        
        # cut in half and flip halves
        m = m[len(m) // 2:] + m[:len(m) // 2]
        
        # test
        # m = list(map(list, zip(*m)))
    
    return m


def reverse_shuffle(m: List[List[str]], key: int, loops: int) -> List[List[str]]:
    for j in range(loops):
        # test
        # m = list(map(list, zip(*m)))
        
        # cut in half and flip halves
        m = m[len(m) // 2:] + m[:len(m) // 2]
        
        shuffled_m = [[0] * 8 for _ in range(16)]
        
        for idx, sidx in enumerate(test(key)):
            shuffled_m[sidx // 8][sidx % 8] = m[idx // 8][idx % 8]
        
        m = shuffled_m
        
        # move rows up
        m = m[1:] + m[:1]
        
        # move columns to the left
        m = [row[1:] + row[:1] for row in m]
    
    return m


def shift_bits(key: bytes, secret: bytes) -> bytes:
    """As you can see I have no idea what I'm doing :)"""
    shifted = b''
    for idx, byte in enumerate(secret):
        byte = byte ^ 255
        
        byte = sum(1 << (8 - 1 - j) for j in range(8) if byte >> j & 1)
        
        byte = byte ^ (key[idx] ^ 255)
        
        shifted = shifted + bytes([byte])
    
    return shifted


def unshift_bits(key: bytes, secret: bytes) -> bytes:
    shifted = b''
    for idx, byte in enumerate(secret):
        byte = byte ^ (key[idx] ^ 255)
        
        byte = sum(1 << (8 - 1 - j) for j in range(8) if byte >> j & 1)
        
        byte = byte ^ 255
        
        shifted = shifted + bytes([byte])
    
    return shifted


def test(seed: bytes) -> List[int]:
    rg = np.random.Generator(ChaCha(seed=int.from_bytes(seed, byteorder='big'), rounds=8))
    lst = np.arange(128)
    rg.shuffle(lst)
    
    return lst


def bin_to_bytes(binary: str) -> bytes:
    return int(binary, 2).to_bytes(len(binary) // 8, byteorder='big')


def encrypt(password: str, secret: str, loops: int = 1) -> str:
    key = generate_key(password)
    secret = add_padding(secret)
    secret = xor_string(key, secret)
    secret = obfuscate(secret, key, True, loops)
    secret = bytes_to_base64(secret)
    return secret


def decrypt(password: str, base: str, loops: int = 1) -> str:
    key = generate_key(password)
    binary = base64_to_bytes(base)
    binary = xor_string(key, binary)
    binary = obfuscate(binary, key, False, loops)
    pad = binary[-1]
    binary = binary[:-pad]
    return binary.decode()


def main():
    while True:
        com = input('1) Encrypt Text\n' '2) Decrypt Text\n' '3) Exit\n')
        
        input_text = input('Enter the text: ')
        # key = getpass('Enter your key: ')
        key = input('Enter your key: ')  # getpass doesn't work in pycharm, just for testing
        
        if com == '1':
            print(f'Encrypted text: {encrypt(key, input_text)}')
        
        elif com == '2':
            print(f'Decrypted text: {decrypt(key, input_text)}')
        
        elif com == '3':
            break
        
        print()


if __name__ == '__main__':
    # from datetime import datetime
    #
    # start = datetime.now()
    # encrypt('password', 'hello this is a test')
    # print(datetime.now() - start)
    
    main()
EN

回答 1

Code Review用户

回答已采纳

发布于 2021-05-06 05:18:59

这只是为了输入提示吗?

正确,键入模块中的所有内容都用于类型提示。在使用mypy之类的东西时,这些内容很有用,但是python解释器将忽略所有这些。

使用numpy数组而不是python列表会更快吗?

理论上,是的,但这取决于数据的大小。numpy实际上是用来处理大型数据集的,而且我认为您使用它的大多数东西都是非常小的,在这里开销会消除任何加速。

除了这些后续的问题,如果你看到任何需要改变的地方,现在就让我来。

代码语言:javascript
复制
def generate_key(key: str) -> bytes:
    if len(key) >= 128:
        key = key.encode()
        return key[:128]
    elif len(key) < 128:
        
        key = key.encode()
        
        for i in range(128 - len(key)):
            key = key + bytes([(sum(key) // len(key)) ^ sum(1 << (8 - 1 - j) for j in range(8) if key[i] >> j & 1)])
        
        decimal = ''.join(str(i) for i in key)
        
        binary = f'{bin(int(decimal[len(decimal) // 2:] + decimal[:len(decimal) // 2]))[2:]:<01024s}'
        
        key = bin_to_bytes(binary[:1024])
        half1 = key[:len(key) // 2]
        half2 = key[len(key) // 2:]
        key = half2 + half1
        return key[:128]

这里的elif是没有必要的。只需在这里省略elif语句,就可以删除部分缩进。

代码语言:javascript
复制
def generate_key(key: str) -> bytes:
    if len(key) >= 128:
        key = key.encode()
        return key[:128]
        
    key = key.encode()
    
    for i in range(128 - len(key)):
        key = key + bytes([(sum(key) // len(key)) ^ sum(1 << (8 - 1 - j) for j in range(8) if key[i] >> j & 1)])
    
    decimal = ''.join(str(i) for i in key)
    
    binary = f'{bin(int(decimal[len(decimal) // 2:] + decimal[:len(decimal) // 2]))[2:]:<01024s}'
    
    key = bin_to_bytes(binary[:1024])
    half1 = key[:len(key) // 2]
    half2 = key[len(key) // 2:]
    key = half2 + half1
    return key[:128]
  • 有些循环使用i,而在没有名称冲突的情况下使用j,这有点奇怪。很奇怪,但也不可怕。
  • 我可能会将128放入一个称为块大小的常量中,并将其放在这个文件的顶部。BLOCK_SIZE = 128。然后用这个常量替换128的所有实例,如果块大小发生变化,您只需要在一个地方更新它。
  • 您可能会将它包装在一个类中,如果您在另一个python文件中使用它,那么跨文件的名称空间处理就更容易了。按照这个路线,您只需公开加密/解密函数,并通过在所有其他函数的前缀加上_使其“私有”。
    • 如果这样做,可以在对象上设置块大小,而不需要是常量

  • 现在,您只对随机函数使用numpy。为此,我建议使用以随机函数构建的蟒蛇。
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/260171

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档