根据索引将字节对象分成 n 个大小相等的块

Break bytes object into n equally sized blocks based on index

我正在编写一个脚本来破解重复密钥 XOR (Vigenère) 密码。

这涉及确定一些数字(0 < n < 可能是 50),然后将字节对象拆分为 n 个更小的块,其中第一个块包含(来自原始对象)索引 n、2n、3n,下一个包含n+1、2n+1、3n+1... 然后是 n+y、2n+y、3n+y,其中 y < n。

如果 n = 3,字节 [0、2、5、8 等] 应该在一个块中,字节 [1,3,6,9] 在下一个块中,字节 [2,4,7 ,10] 在最后一个块中。

我可以用字符串很容易地实现它,但我不知道如何让它与字节对象一起工作。我搜索并找到并改编了这段代码:

blocks = [ciphertext[i:i+most_likely_keylength] for i in range(0, len(ciphertext)+1, most_likely_keylength)]

transposedBlocks = list(zip_longest(*blocks, fillvalue=0))

##ciphertext is a bytes object resulting from the following line:
##ciphertext = base64.b64decode(open('Q6.txt', 'r').read())

然而,这是一个 returns 一个充满整数的元组列表,我不知道如何再次 'join' 这些整数,所以它们将像以前一样是长二进制对象。 (这样我就可以在每个元组上 运行 像 Crypto.Util.strxor_c 这样的好东西。

字节对象的 'string manipulation' 有任何帮助吗?

注意:我正在 cryptopals.com 上进行 Break repeating-key XOR 挑战 – 我看过其他人在 Github 上的解决方案,但他们大多使用专门的加密模块,我想要看看我在做什么的胆量。

从概念上讲,bytes对象一个整数序列:

>>> tuple(b'ciphertext')
(99, 105, 112, 104, 101, 114, 116, 101, 120, 116)

...所以它的构造函数会很乐意接受一个:

>>> bytes((99, 105, 112, 104, 101, 114, 116, 101, 120, 116))
b'ciphertext'

知道这一点后,您可以将第二行更改为:

transposed = [bytes(t) for t in zip_longest(*blocks, fillvalue=0))]

... 你会得到 bytes 个对象:

from itertools import zip_longest

ciphertext = b'ciphertext'
keylength = 3

blocks = [ciphertext[i:i+keylength] for i in range(0, len(ciphertext)+1, keylength)]
# [b'cip', b'her', b'tex', b't']

transposed = [bytes(t) for t in zip_longest(*blocks, fillvalue=0)]
# [b'chtt', b'iee\x00', b'prx\x00']

但是,您的代码中存在一个错误 – 因为您在调用 range() 时使用的是 len(ciphertext)+1 而不仅仅是 len(ciphertext),您在 blocks 如果密文是 keylength 的整数倍:

ciphertext = b'SplitsEvenly'

blocks = [ciphertext[i:i+keylength] for i in range(0, len(ciphertext)+1, keylength)]
# [b'Spl', b'its', b'Eve', b'nly', b'']

... 这会导致 transposed:

中所有元素末尾出现额外的空字节
transposed = [bytes(t) for t in zip_longest(*blocks, fillvalue=0)]
# [b'SiEn\x00', b'ptvl\x00', b'lsey\x00']

如果删除 +1,它在两种情况下都能正常工作:

ciphertext = b'ciphertext'

blocks = [ciphertext[i:i+keylength] for i in range(0, len(ciphertext), keylength)]
# [b'cip', b'her', b'tex', b't']

transposed = [bytes(t) for t in zip_longest(*blocks, fillvalue=0)]
# [b'chtt', b'iee\x00', b'prx\x00']
ciphertext = b'SplitsEvenly'

blocks = [ciphertext[i:i+keylength] for i in range(0, len(ciphertext), keylength)]
# [b'Spl', b'its', b'Eve', b'nly']

transposed = [bytes(t) for t in zip_longest(*blocks, fillvalue=0)]
# [b'SiEn', b'ptvl', b'lsey']