web-dev-qa-db-fra.com

Conversion de python long/int en tableau d'octets de taille fixe

J'essaie d'implémenter l'échange de clés RC4 et DH en python. Le problème est que je ne sais pas comment convertir le python long/int de l'échange de clés en tableau d'octets dont j'ai besoin pour l'implémentation RC4. Existe-t-il un moyen simple de convertir un tableau d'octets de longueur en longueur requise?

Mise à jour : J'ai oublié de mentionner que les nombres que je traite sont des entiers non signés de 768 bits.

37
cdecker

Je n'ai pas fait de repères, mais cette recette "fonctionne pour moi".

La version courte: utilisez '%x' % val, puis unhexlify le résultat. Le diable est dans les détails, car unhexlify nécessite un nombre pair de chiffres hexadécimaux, ce que %x ne garantit pas. Voir la docstring et les commentaires en ligne libéraux pour plus de détails.

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://stackoverflow.com/a/931095/309233
        s = s[::-1]

    return s

... et mon nosetest tests unitaires ;-)

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'
17
Tripp Lilley

Avec Python 3.2 et ultérieur, vous pouvez utiliser int.to_bytes et int.from_bytes: https://docs.python.org/3/library/stdtypes.html#int.to_bytes

37
Jack O'Connor

Tout le monde a trop compliqué cette réponse:

some_int = <256 bit integer>
some_bytes = some_int.to_bytes(32, sys.byteorder)
my_bytearray = bytearray(some_bytes)

Vous avez juste besoin de connaître le nombre d'octets que vous essayez de convertir. Dans mes cas d'utilisation, normalement, je n'utilise que ce grand nombre pour la cryptographie, et à ce moment-là, je dois m'inquiéter du module et de ce qui ne l'est pas. d'octets à retourner.

Puisque vous le faites en maths 768 bits, au lieu de 32 comme argument, ce serait 96.

13
sparticvs

Bon mot:

bytearray.fromhex('{:0192x}'.format(big_int))

Le 192 est 768/4, parce que l’OP voulait des nombres de 768 bits et il y a 4 bits dans un chiffre hexadécimal. Si vous avez besoin d'une plus grande bytearray, utilisez une chaîne de format avec un nombre plus élevé. Exemple:

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

[Ma réponse avait utilisé hex() auparavant. Je l'ai corrigé avec format() afin de gérer les ints avec des expressions d'octets de taille impaire. Cela corrige les plaintes précédentes à propos de ValueError.]

12
Jess Austin

long/int dans le tableau d'octets ressemble au but exact de struct.pack. Pour les entiers longs qui dépassent 4(8) octets, vous pouvez créer quelque chose comme le suivant:

>>> 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
7
Roman Bodnarchuk

Vous pouvez essayer d’utiliser struct :

import struct
struct.pack('L',longvalue)
6
Eduardo Ivanec

Little-endian, inversez le résultat ou la plage si vous voulez le Big-endian.

def int_to_bytes(val, num_bytes):
    return [(val & (0xff << pos*8)) >> pos*8 for pos in range(num_bytes)]
4
scornwell

Fondamentalement, vous devez convertir int/long en sa représentation de base 256 - c’est-à-dire un nombre dont les "chiffres" vont de 0 à 255. Voici un moyen assez efficace de faire quelque chose comme ça:

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

Nombreux sont ceux qui n’ont pas besoin de l’appelreversed() en fonction de l’endurance souhaitée (pour ce faire, le remplissage doit également être effectué différemment). Notez également que tel qu'il est écrit, il ne gère pas les nombres négatifs.

Vous pouvez également vous intéresser à la fonction long_to_bytes(), similaire mais hautement optimisée, du module number.py qui fait partie de l’open source Python Cryptography Toolkit . En fait, il convertit le nombre en chaîne, pas en tableau d'octets, mais c'est un problème mineur.

3
martineau

Python 2.7 n'implémente pas la méthode int.to- very slow_bytes ().

J'ai essayé 3 méthodes:

  1. hex déballer/pack: très lent
  2. décalage d'octet 8 bits à la fois: beaucoup plus rapide.
  3. en utilisant un module "C" et en compressant dans les octets inférieurs (7 ia64 ou 3 i32) . C'était environ deux fois plus vite que 2 /. C'est l'option la plus rapide, mais toujours trop lente.

Toutes ces méthodes sont très inefficaces pour deux raisons: 

  • Python 2.7 ne supporte pas cette opération utile.
  • c ne prend pas en charge l’arithmétique de précision étendue utilisant les indicateurs de report/emprunt/dépassement disponibles sur la plupart des plates-formes.
2
GP Eckersley
i = 0x12345678
s = struct.pack('<I',i)
b = struct.unpack('BBBB',s)
0
user1025874