60 votes

Convertir un long/int python 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 n'ai aucune idée de la façon de convertir le long/int python 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 long en tableau d'octets de la longueur requise ?

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

0 votes

Je ne suis pas sûr que cela puisse vous aider, mais vérifiez la section struct module : docs.python.org/library/struct.html

63voto

Jack O'Connor Points 1165

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

8 votes

Il y a une erreur de débordement sur les grands nombres.

29voto

sparticvs Points 453

Tout le monde a surcompliqué cette réponse :

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

Il vous suffit de connaître le nombre d'octets que vous essayez de convertir. Dans mes cas d'utilisation, je n'utilise normalement des nombres aussi grands que pour la cryptographie, et à ce moment-là, je dois me préoccuper du module et d'autres choses, donc je ne pense pas que ce soit un gros problème de devoir connaître le nombre maximum d'octets à retourner.

Puisque vous le faites en tant que mathématique 768 bits, au lieu de 32 comme argument, ce serait 96.

1 votes

Dans Python 3, cette solution fonctionnait très bien pour les entiers de 2048 bits. Dans Python 2.7, elle ne fonctionne que pour les int (les entiers de 2048 bits sont des long dans Python 2.7).

5 votes

Dans Python 2.7 some_bytes = some_int.to_bytes(32, sys.byteorder) produit des erreurs AttributeError: 'int' object has no attribute 'to_bytes'

1 votes

Pas tout à fait tout le monde... voir la réponse de @JackOConnor

20voto

Tripp Lilley Points 1111

Je n'ai pas fait d'analyse comparative, mais cette recette "fonctionne pour moi".

La version courte : utilisez '%x' % val entonces unhexlify le résultat. Mais le diable se cache dans les détails. unhexlify nécessite un nombre pair de chiffres hexadécimaux, qui %x ne garantit pas. Voir la docstring, et les commentaires libéraux en ligne 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'

12voto

Jess Austin Points 332

Un seul mot :

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

Le 192 est 768 / 4, car l'OP voulait des nombres de 768 bits et il y a 4 bits dans un chiffre hexagonal. Si vous avez besoin d'un plus grand bytearray utiliser 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() avant. 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 concernant ValueError .]

0 votes

Ça ne marche pas si vous ne produisez pas un Long cependant. Je pense qu'une méthode comme bytearray.fromhex(hex(2**61-1).strip('0x').strip('L')) est plus sûre.

0 votes

@MarioAlemi le code dans votre commentaire est faux. strip('0x') enlèvera également les zéros de fin, ce qui donnera un mauvais résultat (et parfois ValueError ) !

2 votes

@Jess Austin : Votre solution est totalement fausse, car elle ne fonctionne que lorsque x est constitué d'un nombre pair d'hexadécimales. Exemple : x=0x963b685edb4a8f336f624c9cc2b02d9ea4536a2df669c19e9794854d‌​1d93105c81c289cde061‌​c0448176dfeda9c18370‌​db55f1d0fe5229ce07de‌​704d88cc7f765c1c8d69‌​874e008da8bd5b3cdfaf‌​137a3a48edc229a41e0f‌​a792a7c61686f1fL

8voto

Roman Bodnarchuk Points 12136

long/int vers le tableau d'octets on dirait que le but exact de struct.pack . Pour les entiers longs qui dépassent 4(8) octets, vous pouvez obtenir 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X