给定一个密钥作为种子,如何洗牌然后取消洗牌字节数组

How shuffle and then unshuffle a bytearray, given a key as seed

我正在尝试创建一个加密系统,为此我将采用字节数组,我打算使用

random.Random(seed).shuffle(bytearray)

加密信息的函数。

我在逆转解密过程时遇到了问题,我尝试了类似的方法(没有成功):

random.Random(1/seed).shuffle(encryptedbytearray)

有办法吗?

打乱排序的范围,以便我们可以将打乱的索引与未打乱的索引相匹配。

x = list(range(len(s)))
random.Random(seed).shuffle(x)

对于 12345 的种子,这会产生 [14, 15, 12, 3, 24, 16, 7, 22, 10, 2, 19, 4, 20, 17, 1, 21, 5, 25, 18, 8, 6, 11, 9, 0, 23, 13]。这表明打乱后列表中第 0 个索引中的值实际上在未打乱列表中的第 14 个索引中,第 1 个索引实际上是第 15 个未打乱等

然后将每个打乱后的索引与打乱后的值进行匹配,然后(根据索引)排序回到它们未打乱的位置。

unshuffled = bytearray(c for i, c in sorted(zip(x, s)))
print(unshuffled)

完整示例:

import random
# setup
s = bytearray(b"abcdefghijklmnopqrstuvxwyz")
seed = 12345
random.Random(seed).shuffle(s)

# shuffle a sorted range, so that we can match the shuffled indicies to the unshuffled indicies
x = list(range(len(s)))
random.Random(seed).shuffle(x)

# match each shuffled index to the shuffled value, and then sort (based on the index) back into their unshuffled positions
unshuffled = bytearray(c for i, c in sorted(zip(x, s)))
print(unshuffled)

上面详述的过程应该适用于任何打乱顺序的序列(例如列表),而不仅仅是字节数组。

这里 a more detailed explanation of this process on crypto.se 涉及更多数学。

您需要使用相同的种子来打乱索引,以便您可以回溯原始位置。 (枚举将允许您避免对该映射进行排序)

import random

def encrypt(decrypted,seed=4):
    encrypted = decrypted.copy()
    random.Random(seed).shuffle(encrypted)
    return encrypted

def decrypt(encrypted,seed=4):
    decrypted = encrypted.copy()
    indexes   = list(range(len(encrypted)))
    random.Random(seed).shuffle(indexes)
    for e,d in enumerate(indexes):
        decrypted[d] = encrypted[e]
    return decrypted

示例运行(使用字符列表,但它适用于字节数组或任何其他类型的列表):

clearText = list('ABCDE')

encryptedText = encrypt(clearText)
print(encryptedText)
['D', 'E', 'A', 'C', 'B']

decryptedText = decrypt(encryptedText)
print(decryptedText)
['A', 'B', 'C', 'D', 'E']

如果您希望函数直接在数组上“就地”工作(而不是返回值),您可以这样写:

def encrypt(decrypted,seed=4):
    random.Random(seed).shuffle(encrypted)

def decrypt(encrypted,seed=4):
    before   = encrypted.copy()
    indexes  = list(range(len(encrypted)))
    random.Random(seed).shuffle(indexes)
    for e,d in enumerate(indexes):
        encrypted[d] = before[e]