I'm trying to implement RC4 and DH key exchange in python. Problem is that I have no idea about how to convert the python long/int from the key exchange to the byte array I need for the RC4 implementation. Is there a simple way to convert a long to the required length byte array?
我正在尝试用python实现RC4和DH密钥交换。问题是,我不知道如何将python long/int从密钥交换转换为我需要的RC4实现的字节数组。是否有一种简单的方法来转换长到所需的长度字节数组?
Update: forgot to mention that the numbers I'm dealing with are 768 bit unsigned integers.
更新:忘记说我处理的数字是768位无符号整数。
10 个解决方案
#1
16
I haven't done any benchmarks, but this recipe "works for me".
我没有做过任何基准测试,但是这个食谱“对我有用”。
The short version: use '%x' % val
, then unhexlify
the result. The devil is in the details, though, as unhexlify
requires an even number of hex digits, which %x
doesn't guarantee. See the docstring, and the liberal inline comments for details.
短版本:使用'%x' % val,然后将结果不化。但在细节上是魔鬼,因为unhexlify需要一个偶数的十六进制数字,%x不能保证。请参阅docstring,以及关于细节的*内联注释。
from binascii import unhexlify
def long_to_bytes (val, endianness='big'):
"""
Use :ref:`string formatting` and :func:`~binascii.unhexlify` to
convert ``val``, a :func:`long`, to a byte :func:`str`.
:param long val: The value to pack
:param str endianness: The endianness of the result. ``'big'`` for
big-endian, ``'little'`` for little-endian.
If you want byte- and word-ordering to differ, you're on your own.
Using :ref:`string formatting` lets us use Python's C innards.
"""
# one (1) hex digit per four (4) bits
width = val.bit_length()
# unhexlify wants an even multiple of eight (8) bits, but we don't
# want more digits than we need (hence the ternary-ish 'or')
width += 8 - ((width % 8) or 8)
# format width specifier: four (4) bits per hex digit
fmt = '%%0%dx' % (width // 4)
# prepend zero (0) to the width, to zero-pad the output
s = unhexlify(fmt % val)
if endianness == 'little':
# see http://*.com/a/931095/309233
s = s[::-1]
return s
...and my nosetest unit tests ;-)
…以及我的nosetest单元测试;-)
class TestHelpers (object):
def test_long_to_bytes_big_endian_small_even (self):
s = long_to_bytes(0x42)
assert s == '\x42'
s = long_to_bytes(0xFF)
assert s == '\xff'
def test_long_to_bytes_big_endian_small_odd (self):
s = long_to_bytes(0x1FF)
assert s == '\x01\xff'
s = long_to_bytes(0x201FF)
assert s == '\x02\x01\xff'
def test_long_to_bytes_big_endian_large_even (self):
s = long_to_bytes(0xab23456c8901234567)
assert s == '\xab\x23\x45\x6c\x89\x01\x23\x45\x67'
def test_long_to_bytes_big_endian_large_odd (self):
s = long_to_bytes(0x12345678901234567)
assert s == '\x01\x23\x45\x67\x89\x01\x23\x45\x67'
def test_long_to_bytes_little_endian_small_even (self):
s = long_to_bytes(0x42, 'little')
assert s == '\x42'
s = long_to_bytes(0xFF, 'little')
assert s == '\xff'
def test_long_to_bytes_little_endian_small_odd (self):
s = long_to_bytes(0x1FF, 'little')
assert s == '\xff\x01'
s = long_to_bytes(0x201FF, 'little')
assert s == '\xff\x01\x02'
def test_long_to_bytes_little_endian_large_even (self):
s = long_to_bytes(0xab23456c8901234567, 'little')
assert s == '\x67\x45\x23\x01\x89\x6c\x45\x23\xab'
def test_long_to_bytes_little_endian_large_odd (self):
s = long_to_bytes(0x12345678901234567, 'little')
assert s == '\x67\x45\x23\x01\x89\x67\x45\x23\x01'
#2
27
With Python 3.2 and later, you can use int.to_bytes
and int.from_bytes
: https://docs.python.org/3/library/stdtypes.html#int.to_bytes
使用Python 3.2和之后,您可以使用int.to_bytes和int.from_bytes: https://docs.python.org/3/library/stdtypes.html#int.to_bytes。
#3
10
One-liner:
一行程序:
bytearray.fromhex('{:0192x}'.format(big_int))
The 192 is 768 / 4, because OP wanted 768-bit numbers and there are 4 bits in a hex digit. If you need a bigger bytearray
use a format string with a higher number. Example:
192是768 / 4,因为OP想要768位数字,在十六位数字中有4位。如果您需要一个较大的bytearray,使用一个更高数字的格式字符串。例子:
>>> big_int = 911085911092802609795174074963333909087482261102921406113936886764014693975052768158290106460018649707059449553895568111944093294751504971131180816868149233377773327312327573120920667381269572962606994373889233844814776702037586419
>>> bytearray.fromhex('{:0192x}'.format(big_int))
bytearray(b'\x96;h^\xdbJ\x8f3obL\x9c\xc2\xb0-\x9e\xa4Sj-\xf6i\xc1\x9e\x97\x94\x85M\x1d\x93\x10\\\x81\xc2\x89\xcd\xe0a\xc0D\x81v\xdf\xed\xa9\xc1\x83p\xdbU\xf1\xd0\xfeR)\xce\x07\xdepM\x88\xcc\x7fv\\\x1c\x8di\x87N\x00\x8d\xa8\xbd[<\xdf\xaf\x13z:H\xed\xc2)\xa4\x1e\x0f\xa7\x92\xa7\xc6\x16\x86\xf1\xf3')
>>> lepi_int = 0x963b685edb4a8f336f624c9cc2b02d9ea4536a2df669c19e9794854d1d93105c81c289cde061c0448176dfeda9c18370db55f1d0fe5229ce07de704d88cc7f765c1c8d69874e008da8bd5b3cdfaf137a3a48edc229a41e0fa792a7c61686f1f
>>> bytearray.fromhex('{:0192x}'.format(lepi_int))
bytearray(b'\tc\xb6\x85\xed\xb4\xa8\xf36\xf6$\xc9\xcc+\x02\xd9\xeaE6\xa2\xdff\x9c\x19\xe9yHT\xd1\xd91\x05\xc8\x1c(\x9c\xde\x06\x1c\x04H\x17m\xfe\xda\x9c\x187\r\xb5_\x1d\x0f\xe5"\x9c\xe0}\xe7\x04\xd8\x8c\xc7\xf7e\xc1\xc8\xd6\x98t\xe0\x08\xda\x8b\xd5\xb3\xcd\xfa\xf17\xa3\xa4\x8e\xdc"\x9aA\xe0\xfay*|aho\x1f')
[My answer had used hex()
before. I corrected it with format()
in order to handle ints with odd-sized byte expressions. This fixes previous complaints about ValueError
.]
我的回答以前用过十六进制。我用format()来纠正它,以处理奇数大小的字节表达式的ints。这修正了之前对ValueError的抱怨。
#4
7
long/int to the byte array looks like exact purpose of struct.pack
. For long integers that exceed 4(8) bytes, you can come up with something like the next:
长/int到字节数组看起来像结构的确切目的。对于超过4(8)字节的长整数,您可以提出如下的内容:
>>> limit = 256*256*256*256 - 1
>>> i = 1234567890987654321
>>> parts = []
>>> while i:
parts.append(i & limit)
i >>= 32
>>> struct.pack('>' + 'L'*len(parts), *parts )
'\xb1l\x1c\xb1\x11"\x10\xf4'
>>> struct.unpack('>LL', '\xb1l\x1c\xb1\x11"\x10\xf4')
(2976652465L, 287445236)
>>> (287445236L << 32) + 2976652465L
1234567890987654321L
#5
6
Everyone has overcomplicated this answer:
每个人的回答都过于复杂:
some_int = <256 bit integer>
some_bytes = some_int.to_bytes(32, sys.byteorder)
my_bytearray = bytearray(some_bytes)
You just need to know the number of bytes that you are trying to convert. In my use cases, normally I only use this large of numbers for crypto, and at that point I have to worry about modulus and what-not, so I don't think this is a big problem to be required to know the max number of bytes to return.
您只需要知道要转换的字节数。在我的用例中,通常我只使用这个大的数字作为密码,在那个时候,我需要担心模量和什么-不,所以我不认为这是一个大问题,需要知道返回的最大字节数。
Since you are doing it as 768-bit math, then instead of 32 as the argument it would be 96.
因为你用的是768位数学,而不是32,而是96。
#7
3
Little-endian, reverse the result or the range if you want Big-endian.
如果你想要Big-endian,你可以把结果或范围颠倒过来。
def int_to_bytes(val, num_bytes):
return [(val & (0xff << pos*8)) >> pos*8 for pos in range(num_bytes)]
#8
3
Basically what you need to do is convert the int/long into its base 256 representation -- i.e. a number whose "digits" range from 0-255. Here's a fairly efficient way to do something like that:
基本上,您需要做的是将int/long转换为其基础256表示——即一个“数字”范围为0-255的数字。这是一种很有效的方法:
def base256_encode(n, minwidth=0): # int/long to byte array
if n > 0:
arr = []
while n:
n, rem = divmod(n, 256)
arr.append(rem)
b = bytearray(reversed(arr))
elif n == 0:
b = bytearray(b'\x00')
else:
raise ValueError
if minwidth > 0 and len(b) < minwidth: # zero padding needed?
b = (minwidth-len(b)) * '\x00' + b
return b
You many not need thereversed()
call depending on the endian-ness desired (doing so would require the padding to be done differently as well). Also note that as written it doesn't handle negative numbers.
您不需要使用thereversed()调用,这取决于您所期望的“endian-ness”(这样做会要求对填充进行不同的处理)。同样要注意的是,它不能处理负数。
You might also want to take a look at the similar but highly optimized long_to_bytes()
function in thenumber.py
module which is part of the open source Python Cryptography Toolkit. It actually converts the number into a string, not a byte array, but that's a minor issue.
您可能还想看一下在thenumber中类似但高度优化的long_to_bytes()函数。pymodule是开源Python加密工具箱的一部分。它实际上将数字转换成字符串,而不是字节数组,但这是个小问题。
#9
2
Python 2.7 does not implement the int.to- very slow_bytes() method.
Python 2.7没有实现int.to- very slow_bytes()方法。
I tried 3 methods:
我尝试了3个方法:
- hex unpack/pack : very slow
- 海克斯打开/包:非常慢。
- byte shifting 8 bits at a time: significantly faster.
- 字节一次移动8位:明显快得多。
- using a "C" module and packing into the lower (7 ia64 or 3 i32) bytes. This was about twice as fast as 2/ . It is the fastest option, but still too slow.
- 使用“C”模块,并将其打包到更低的(7 ia64或3 i32)字节中。这个速度是2的两倍。这是最快的选择,但仍然太慢。
All these methods are very inefficient for two reasons:
所有这些方法都非常低效,原因有二:
- Python 2.7 does not support this useful operation.
- Python 2.7不支持这个有用的操作。
- c does not support extended precision arithmetic using the carry/borrow/overflow flags available on most platforms.
- c不支持在大多数平台上使用carry/borrow/溢出标志的扩展精度算法。
#10
0
i = 0x12345678
s = struct.pack('<I',i)
b = struct.unpack('BBBB',s)
#1
16
I haven't done any benchmarks, but this recipe "works for me".
我没有做过任何基准测试,但是这个食谱“对我有用”。
The short version: use '%x' % val
, then unhexlify
the result. The devil is in the details, though, as unhexlify
requires an even number of hex digits, which %x
doesn't guarantee. See the docstring, and the liberal inline comments for details.
短版本:使用'%x' % val,然后将结果不化。但在细节上是魔鬼,因为unhexlify需要一个偶数的十六进制数字,%x不能保证。请参阅docstring,以及关于细节的*内联注释。
from binascii import unhexlify
def long_to_bytes (val, endianness='big'):
"""
Use :ref:`string formatting` and :func:`~binascii.unhexlify` to
convert ``val``, a :func:`long`, to a byte :func:`str`.
:param long val: The value to pack
:param str endianness: The endianness of the result. ``'big'`` for
big-endian, ``'little'`` for little-endian.
If you want byte- and word-ordering to differ, you're on your own.
Using :ref:`string formatting` lets us use Python's C innards.
"""
# one (1) hex digit per four (4) bits
width = val.bit_length()
# unhexlify wants an even multiple of eight (8) bits, but we don't
# want more digits than we need (hence the ternary-ish 'or')
width += 8 - ((width % 8) or 8)
# format width specifier: four (4) bits per hex digit
fmt = '%%0%dx' % (width // 4)
# prepend zero (0) to the width, to zero-pad the output
s = unhexlify(fmt % val)
if endianness == 'little':
# see http://*.com/a/931095/309233
s = s[::-1]
return s
...and my nosetest unit tests ;-)
…以及我的nosetest单元测试;-)
class TestHelpers (object):
def test_long_to_bytes_big_endian_small_even (self):
s = long_to_bytes(0x42)
assert s == '\x42'
s = long_to_bytes(0xFF)
assert s == '\xff'
def test_long_to_bytes_big_endian_small_odd (self):
s = long_to_bytes(0x1FF)
assert s == '\x01\xff'
s = long_to_bytes(0x201FF)
assert s == '\x02\x01\xff'
def test_long_to_bytes_big_endian_large_even (self):
s = long_to_bytes(0xab23456c8901234567)
assert s == '\xab\x23\x45\x6c\x89\x01\x23\x45\x67'
def test_long_to_bytes_big_endian_large_odd (self):
s = long_to_bytes(0x12345678901234567)
assert s == '\x01\x23\x45\x67\x89\x01\x23\x45\x67'
def test_long_to_bytes_little_endian_small_even (self):
s = long_to_bytes(0x42, 'little')
assert s == '\x42'
s = long_to_bytes(0xFF, 'little')
assert s == '\xff'
def test_long_to_bytes_little_endian_small_odd (self):
s = long_to_bytes(0x1FF, 'little')
assert s == '\xff\x01'
s = long_to_bytes(0x201FF, 'little')
assert s == '\xff\x01\x02'
def test_long_to_bytes_little_endian_large_even (self):
s = long_to_bytes(0xab23456c8901234567, 'little')
assert s == '\x67\x45\x23\x01\x89\x6c\x45\x23\xab'
def test_long_to_bytes_little_endian_large_odd (self):
s = long_to_bytes(0x12345678901234567, 'little')
assert s == '\x67\x45\x23\x01\x89\x67\x45\x23\x01'
#2
27
With Python 3.2 and later, you can use int.to_bytes
and int.from_bytes
: https://docs.python.org/3/library/stdtypes.html#int.to_bytes
使用Python 3.2和之后,您可以使用int.to_bytes和int.from_bytes: https://docs.python.org/3/library/stdtypes.html#int.to_bytes。
#3
10
One-liner:
一行程序:
bytearray.fromhex('{:0192x}'.format(big_int))
The 192 is 768 / 4, because OP wanted 768-bit numbers and there are 4 bits in a hex digit. If you need a bigger bytearray
use a format string with a higher number. Example:
192是768 / 4,因为OP想要768位数字,在十六位数字中有4位。如果您需要一个较大的bytearray,使用一个更高数字的格式字符串。例子:
>>> big_int = 911085911092802609795174074963333909087482261102921406113936886764014693975052768158290106460018649707059449553895568111944093294751504971131180816868149233377773327312327573120920667381269572962606994373889233844814776702037586419
>>> bytearray.fromhex('{:0192x}'.format(big_int))
bytearray(b'\x96;h^\xdbJ\x8f3obL\x9c\xc2\xb0-\x9e\xa4Sj-\xf6i\xc1\x9e\x97\x94\x85M\x1d\x93\x10\\\x81\xc2\x89\xcd\xe0a\xc0D\x81v\xdf\xed\xa9\xc1\x83p\xdbU\xf1\xd0\xfeR)\xce\x07\xdepM\x88\xcc\x7fv\\\x1c\x8di\x87N\x00\x8d\xa8\xbd[<\xdf\xaf\x13z:H\xed\xc2)\xa4\x1e\x0f\xa7\x92\xa7\xc6\x16\x86\xf1\xf3')
>>> lepi_int = 0x963b685edb4a8f336f624c9cc2b02d9ea4536a2df669c19e9794854d1d93105c81c289cde061c0448176dfeda9c18370db55f1d0fe5229ce07de704d88cc7f765c1c8d69874e008da8bd5b3cdfaf137a3a48edc229a41e0fa792a7c61686f1f
>>> bytearray.fromhex('{:0192x}'.format(lepi_int))
bytearray(b'\tc\xb6\x85\xed\xb4\xa8\xf36\xf6$\xc9\xcc+\x02\xd9\xeaE6\xa2\xdff\x9c\x19\xe9yHT\xd1\xd91\x05\xc8\x1c(\x9c\xde\x06\x1c\x04H\x17m\xfe\xda\x9c\x187\r\xb5_\x1d\x0f\xe5"\x9c\xe0}\xe7\x04\xd8\x8c\xc7\xf7e\xc1\xc8\xd6\x98t\xe0\x08\xda\x8b\xd5\xb3\xcd\xfa\xf17\xa3\xa4\x8e\xdc"\x9aA\xe0\xfay*|aho\x1f')
[My answer had used hex()
before. I corrected it with format()
in order to handle ints with odd-sized byte expressions. This fixes previous complaints about ValueError
.]
我的回答以前用过十六进制。我用format()来纠正它,以处理奇数大小的字节表达式的ints。这修正了之前对ValueError的抱怨。
#4
7
long/int to the byte array looks like exact purpose of struct.pack
. For long integers that exceed 4(8) bytes, you can come up with something like the next:
长/int到字节数组看起来像结构的确切目的。对于超过4(8)字节的长整数,您可以提出如下的内容:
>>> limit = 256*256*256*256 - 1
>>> i = 1234567890987654321
>>> parts = []
>>> while i:
parts.append(i & limit)
i >>= 32
>>> struct.pack('>' + 'L'*len(parts), *parts )
'\xb1l\x1c\xb1\x11"\x10\xf4'
>>> struct.unpack('>LL', '\xb1l\x1c\xb1\x11"\x10\xf4')
(2976652465L, 287445236)
>>> (287445236L << 32) + 2976652465L
1234567890987654321L
#5
6
Everyone has overcomplicated this answer:
每个人的回答都过于复杂:
some_int = <256 bit integer>
some_bytes = some_int.to_bytes(32, sys.byteorder)
my_bytearray = bytearray(some_bytes)
You just need to know the number of bytes that you are trying to convert. In my use cases, normally I only use this large of numbers for crypto, and at that point I have to worry about modulus and what-not, so I don't think this is a big problem to be required to know the max number of bytes to return.
您只需要知道要转换的字节数。在我的用例中,通常我只使用这个大的数字作为密码,在那个时候,我需要担心模量和什么-不,所以我不认为这是一个大问题,需要知道返回的最大字节数。
Since you are doing it as 768-bit math, then instead of 32 as the argument it would be 96.
因为你用的是768位数学,而不是32,而是96。
#6
#7
3
Little-endian, reverse the result or the range if you want Big-endian.
如果你想要Big-endian,你可以把结果或范围颠倒过来。
def int_to_bytes(val, num_bytes):
return [(val & (0xff << pos*8)) >> pos*8 for pos in range(num_bytes)]
#8
3
Basically what you need to do is convert the int/long into its base 256 representation -- i.e. a number whose "digits" range from 0-255. Here's a fairly efficient way to do something like that:
基本上,您需要做的是将int/long转换为其基础256表示——即一个“数字”范围为0-255的数字。这是一种很有效的方法:
def base256_encode(n, minwidth=0): # int/long to byte array
if n > 0:
arr = []
while n:
n, rem = divmod(n, 256)
arr.append(rem)
b = bytearray(reversed(arr))
elif n == 0:
b = bytearray(b'\x00')
else:
raise ValueError
if minwidth > 0 and len(b) < minwidth: # zero padding needed?
b = (minwidth-len(b)) * '\x00' + b
return b
You many not need thereversed()
call depending on the endian-ness desired (doing so would require the padding to be done differently as well). Also note that as written it doesn't handle negative numbers.
您不需要使用thereversed()调用,这取决于您所期望的“endian-ness”(这样做会要求对填充进行不同的处理)。同样要注意的是,它不能处理负数。
You might also want to take a look at the similar but highly optimized long_to_bytes()
function in thenumber.py
module which is part of the open source Python Cryptography Toolkit. It actually converts the number into a string, not a byte array, but that's a minor issue.
您可能还想看一下在thenumber中类似但高度优化的long_to_bytes()函数。pymodule是开源Python加密工具箱的一部分。它实际上将数字转换成字符串,而不是字节数组,但这是个小问题。
#9
2
Python 2.7 does not implement the int.to- very slow_bytes() method.
Python 2.7没有实现int.to- very slow_bytes()方法。
I tried 3 methods:
我尝试了3个方法:
- hex unpack/pack : very slow
- 海克斯打开/包:非常慢。
- byte shifting 8 bits at a time: significantly faster.
- 字节一次移动8位:明显快得多。
- using a "C" module and packing into the lower (7 ia64 or 3 i32) bytes. This was about twice as fast as 2/ . It is the fastest option, but still too slow.
- 使用“C”模块,并将其打包到更低的(7 ia64或3 i32)字节中。这个速度是2的两倍。这是最快的选择,但仍然太慢。
All these methods are very inefficient for two reasons:
所有这些方法都非常低效,原因有二:
- Python 2.7 does not support this useful operation.
- Python 2.7不支持这个有用的操作。
- c does not support extended precision arithmetic using the carry/borrow/overflow flags available on most platforms.
- c不支持在大多数平台上使用carry/borrow/溢出标志的扩展精度算法。
#10
0
i = 0x12345678
s = struct.pack('<I',i)
b = struct.unpack('BBBB',s)