python中的字节操作(XOR)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29408173/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 04:31:55  来源:igfitidea点击:

byte operations (XOR) in python

pythonencryptionbytetype-conversionoperation

提问by Jcov

    #!/usr/bin/env python3

import binascii


var=binascii.a2b_qp("hello")
key=binascii.a2b_qp("supersecretkey")[:len(var)]

print(binascii.b2a_qp(var))
print(binascii.b2a_qp(key))


#here i want to do an XOR operation on the bytes in var and key and place them in 'encryption': encryption=var XOR key

print(binascii.b2a_qp(encrypted))

If someone could enlighten me on how I could accomplish this I would be very happy. Very new to the whole data-type conversions so yeah...reading through the python wiki is not as clear as I would like :(.

如果有人能启发我如何实现这一点,我会很高兴。整个数据类型转换非常新,所以是的......通过python wiki阅读并不像我想要的那样清晰:(。

采纳答案by DNA

It looks like what you need to do is XOR each of the characters in the message with the corresponding character in the key. However, to do that you need a bit of interconversion using ordand chr, because you can only xor numbers, not strings:

看起来您需要做的是将消息中的每个字符与键中的相应字符进行异或。但是,要做到这一点,您需要使用ordand进行一些相互转换chr,因为您只能对数字进行异或,而不能对字符串进行异或:

>>> encrypted = [ chr(ord(a) ^ ord(b)) for (a,b) in zip(var, key) ] 
>>> encrypted
['\x1b', '\x10', '\x1c', '\t', '\x1d']

>>> decrypted = [ chr(ord(a) ^ ord(b)) for (a,b) in zip(encrypted, key) ]
>>> decrypted
['h', 'e', 'l', 'l', 'o']

>>> "".join(decrypted)
'hello'

Note that binascii.a2b_qp("hello")just converts a string to another string (though possibly with different encoding).

请注意,binascii.a2b_qp("hello")只是将字符串转换为另一个字符串(尽管可能具有不同的编码)。

Your approach, and my code above, will only work if the key is at least as long as the message. However, you can easily repeat the key if required using itertools.cycle:

您的方法和我上面的代码仅在密钥至少与消息一样长时才有效。但是,如果需要,您可以使用itertools.cycle以下方法轻松重复该键:

>>> from itertools import cycle
>>> var="hello"
>>> key="xy"

>>> encrypted = [ chr(ord(a) ^ ord(b)) for (a,b) in zip(var, cycle(key)) ]
>>> encrypted
['\x10', '\x1c', '\x14', '\x15', '\x17']

>>> decrypted = [ chr(ord(a) ^ ord(b)) for (a,b) in zip(encrypted, cycle(key)) ]
>>> "".join(decrypted)
'hello'

To address the issue of unicode/multi-byte characters (raised in the comments below), one can convert the string (and key) to bytes, zip these together, then perform the XOR, something like:

为了解决 unicode/多字节字符的问题(在下面的评论中提出),可以将字符串(和密钥)转换为字节,将它们压缩在一起,然后执行 XOR,例如:

>>> var=u"hello\u2764"
>>> var
'hello?'

>>> encrypted = [ a ^ b for (a,b) in zip(bytes(var, 'utf-8'),cycle(bytes(key, 'utf-8'))) ]
>>> encrypted
[27, 16, 28, 9, 29, 145, 248, 199]

>>> decrypted = [ a ^ b for (a,b) in zip(bytes(encrypted), cycle(bytes(key, 'utf-8'))) ]
>>> decrypted
[104, 101, 108, 108, 111, 226, 157, 164]

>>> bytes(decrypted)
b'hello\xe2\x9d\xa4'

>>> bytes(decrypted).decode()
'hello?'

回答by Vincent

Comparison of two python3 solutions

两种python3解决方案的比较

The first one is based on zip:

第一个基于zip

def encrypt1(var, key):
    return bytes(a ^ b for a, b in zip(var, key))

The second one uses int.from_bytesand int.to_bytes:

第二个使用int.from_bytesint.to_bytes

def encrypt2(var, key):
    key = key[:len(var)]
    int_var = int.from_bytes(var, sys.byteorder)
    int_key = int.from_bytes(key, sys.byteorder)
    int_enc = int_var ^ int_key
    return int_enc.to_bytes(len(var), sys.byteorder)

Simple tests:

简单的测试:

assert encrypt1(b'hello', b'supersecretkey') == b'\x1b\x10\x1c\t\x1d'
assert encrypt2(b'hello', b'supersecretkey') == b'\x1b\x10\x1c\t\x1d'

Performance tests with varand keybeing 1000 bytes long:

性能测试使用var,并key为1000字节长:

$ python3 -m timeit \
  -s "import test_xor;a=b'abcdefghij'*100;b=b'0123456789'*100" \
  "test_xor.encrypt1(a, b)"
10000 loops, best of 3: 100 usec per loop

$ python3 -m timeit \
  -s "import test_xor;a=b'abcdefghij'*100;b=b'0123456789'*100" \
  "test_xor.encrypt2(a, b)"
100000 loops, best of 3: 5.1 usec per loop

The integer approach seems to be significantly faster.

整数方法似乎要快得多。