library - Convierte python long/int en una matriz de bytes de tamaño fijo
struct python library (10)
Estoy intentando implementar el intercambio de claves RC4 y DH en python. El problema es que no tengo idea de cómo convertir el python long / int del intercambio de claves a la matriz de bytes que necesito para la implementación de RC4. ¿Existe una manera simple de convertir una matriz de bytes de longitud larga a la requerida?
Actualización : se olvidó de mencionar que los números con los que estoy tratando son enteros sin signo de 768 bits.
Básicamente, lo que necesita hacer es convertir el int / long en su representación base 256, es decir, un número cuyos "dígitos" van desde 0 hasta 255. Aquí hay una forma bastante eficiente de hacer algo como eso:
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
Muchos no necesitan la llamada reversed()
dependiendo de la endianancia deseada (si lo hace, también requerirá que el relleno se haga de forma diferente). También tenga en cuenta que, tal como está escrito, no maneja números negativos.
También es posible que desee echar un vistazo a la función long_to_bytes()
similar pero altamente optimizada en el módulo number.py
que forma parte del kit de herramientas de criptografía Python de código abierto. En realidad, convierte el número en una cadena, no en una matriz de bytes, pero eso es un problema menor.
Con Python 3.2 y posterior, puede usar int.to_bytes
e int.from_bytes
: https://docs.python.org/3/library/stdtypes.html#int.to_bytes
Little-endian, invierte el resultado o el rango si quieres Big-endian.
def int_to_bytes(val, num_bytes):
return [(val & (0xff << pos*8)) >> pos*8 for pos in range(num_bytes)]
No he hecho ningún punto de referencia, pero esta receta "funciona para mí".
La versión corta: use ''%x'' % val
, luego unhexlify
el resultado. El demonio está en los detalles, sin embargo, como unhexlify
requiere un número par de dígitos hexadecimales, que %x
no garantiza. Vea la docstring y los comentarios liberales en línea para más detalles.
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
... y mis pruebas de unidad 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''
Puedes intentar usar struct :
import struct
struct.pack(''L'',longvalue)
Python 2.7 no implementa el método int.to-very slow_bytes ().
Intenté 3 métodos:
- desempaquetar / paquete hexagonal: muy lento
- byte cambiando 8 bits a la vez: significativamente más rápido.
- usando un módulo "C" y empacando en los bytes inferiores (7 ia64 o 3 i32). Esto fue aproximadamente el doble de rápido que 2 /. Es la opción más rápida, pero aún demasiado lenta.
Todos estos métodos son muy ineficientes por dos razones:
- Python 2.7 no es compatible con esta operación útil.
- c no es compatible con la aritmética de precisión extendida utilizando los indicadores carry / borrow / overflow disponibles en la mayoría de las plataformas.
Todos han complicado demasiado esta respuesta:
some_int = <256 bit integer>
some_bytes = some_int.to_bytes(32, sys.byteorder)
my_bytearray = bytearray(some_bytes)
Solo necesita saber la cantidad de bytes que intenta convertir. En mis casos de uso, normalmente solo utilizo esta gran cantidad de números para cifrado, y en ese momento tengo que preocuparme por el módulo y lo que no, así que no creo que este sea un gran problema para conocer el número máximo. de bytes a devolver.
Como lo está haciendo como matemática de 768 bits, entonces en lugar de 32 como argumento, sería 96.
Un trazador de líneas:
bytearray.fromhex(''{:0192x}''.format(big_int))
El 192 es 768/4, porque OP quería números de 768 bits y hay 4 bits en un dígito hexadecimal. Si necesita un bytearray
más grande, use una cadena de formato con un número más alto. Ejemplo:
>>> 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'')
[Mi respuesta había usado hex()
antes. Lo corregí con format()
para manejar ints con expresiones de bytes de tamaño impar. Esto soluciona las quejas anteriores sobre ValueError
.]
long / int a la matriz de bytes parece el objetivo exacto de struct.pack
. Para enteros largos que superan los 4 (8) bytes, puede encontrar algo como el siguiente:
>>> 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
i = 0x12345678
s = struct.pack(''<I'',i)
b = struct.unpack(''BBBB'',s)