web-dev-qa-db-ja.com

pythonのバイト演算(XOR)

    #!/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))

私がこれをどのように達成できるかについて誰かが私に啓蒙できれば、私はとても幸せです。データ型変換全体が非常に新しいので、そうです... python wikiを読むのは、私が望むほど明確ではありません:(。

11
Jcov

あなたがする必要があるように見えますXORメッセージの各文字とキーの対応する文字です。しかし、それを行うには、ordchrは、xorの数値のみが可能で、文字列は使用できないためです。

_>>> 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'
_

binascii.a2b_qp("hello")は、文字列を別の文字列に変換するだけであることに注意してください(ただし、エンコードが異なる場合があります)。

あなたのアプローチと上記の私のコードは、キーが少なくともメッセージと同じ長さである場合にのみ機能します。ただし、必要に応じて_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'
_

ユニコード/マルチバイト文字(以下のコメントで発生)の問題に対処するには、文字列(およびキー)をバイトに変換し、これらを一緒に圧縮してから、次のような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❤'
_
17
DNA

2つのpython3ソリューションの比較

最初のものは Zip に基づいています:

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

2つ目は int.from_bytes および int.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)

簡単なテスト:

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

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

整数アプローチは大幅に高速であるようです。

24
Vincent