2012-01-04 39 views
30

Tôi đang cố gắng thực hiện trao đổi khóa RC4 và DH trong python. Vấn đề là tôi không có ý tưởng về làm thế nào để chuyển đổi python dài/int từ trao đổi khóa vào mảng byte tôi cần cho việc thực hiện RC4. Có cách nào đơn giản để chuyển đổi một mảng dài sang độ dài yêu cầu?Chuyển đổi python long/int thành mảng byte có kích thước cố định

Cập nhật: quên đề cập đến các số tôi đang xử lý là 768 bit số nguyên không dấu.

+0

không chắc chắn nếu nó sẽ giúp nhưng kiểm tra module 'struct': http://docs.python.org/library/struct.html –

+1

lớn như thế nào là những con số của bạn? – interjay

Trả lời

15

Tôi chưa thực hiện bất kỳ điểm chuẩn nào, nhưng công thức này "phù hợp với tôi".

Phiên bản ngắn: sử dụng '%x' % val, sau đó unhexlify kết quả. Tuy nhiên, ma quỷ có các chi tiết, vì unhexlify yêu cầu số chẵn chữ số thập phân, mà %x không đảm bảo. Xem docstring và các bình luận nội tuyến tự do để biết chi tiết.

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 

... và nosetest đơn vị thử nghiệm của tôi ;-)

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(0xab23456c89) 
     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(0x123456789) 
     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(0xab23456c89, '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(0x123456789, 'little') 
     assert s == '\x67\x45\x23\x01\x89\x67\x45\x23\x01' 
+0

Tôi gặp phải vấn đề khi giá trị là 0 (Python 3.5) '' 'binascii.Error: Odd-length string '' ', sửa lỗi nhanh cho điều này: thay thế' '' s = unhexlify (fmt% val) '' 'bằng' '' s = unhexlify ('00 ') nếu fmt% val ==' 0 'else unhexlify (fmt% val) '' ' – Kevin

5

Bạn có thể thử sử dụng struct:

import struct 
struct.pack('L',longvalue) 
+1

Đáng buồn là không, lỗi: số nguyên nằm ngoài phạm vi cho mã định dạng 'L'. Đó là một dài 768 bit, mà là khá lớn hơn một chút so với 4 byte unsigned int. – cdecker

+1

Bị bỏ qua vì Python dài int là số nguyên dài tùy ý. Hãy suy nghĩ về nó như một mảng 32 (hoặc bất cứ điều gì) bit số nguyên. C dài là kiểu dữ liệu được xác định kích thước. Với phản ứng này, bạn đang bối rối cả hai. – Havok

7

dài/int đến các mảng byte trông giống như mục đích chính xác của struct.pack. Đối với số nguyên dài quá 4 (8) byte, bạn có thể đưa ra một cái gì đó như sau:

>>> 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 
+3

Nhưng nó sẽ không giúp với số lượng lớn (> 8 byte), thường sẽ được sử dụng cho các ứng dụng mã hóa. – interjay

+0

nó được viết không phải là chung chung nhưng giống như giải pháp kích thước cố định cho vấn đề phổ biến của đại diện cho tất cả các ip có thể hoặc tương tự ... – bigkahunaburger

3

Về cơ bản những gì bạn cần làm là chuyển đổi int/dài thành đại diện cơ sở 256 của nó - tức là một số có "chữ số" nằm trong khoảng từ 0-255. Đây là một cách khá hiệu quả để làm một cái gì đó như thế:

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 

Bạn nhiều không cần reversed() gọi tùy thuộc vào endian-Ness mong muốn (làm như vậy sẽ đòi hỏi sự đệm phải được làm khác cũng). Cũng lưu ý rằng khi viết nó không xử lý số âm.

Bạn cũng có thể muốn xem xét chức năng long_to_bytes() tương tự nhưng được tối ưu hóa cao nhất trong mô-đun number.py là một phần của mã nguồn mở Python Cryptography Toolkit. Nó thực sự chuyển đổi số thành một chuỗi, không phải là một mảng byte, nhưng đó là một vấn đề nhỏ.

3

Little-endian, đảo ngược kết quả hoặc phạm vi nếu bạn muốn Big-endian.

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

One-liner:

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

192 là 768/4, vì OP muốn số 768-bit và có 4 bit trong một chữ số hex. Nếu bạn cần số bytearray lớn hơn, hãy sử dụng chuỗi định dạng có số cao hơn. Ví dụ:

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

[Câu trả lời của tôi đã từng sử dụng hex() trước đây. Tôi đã sửa nó với format() để xử lý int với các biểu thức byte có kích cỡ lẻ. Điều này khắc phục các khiếu nại trước đó về ValueError.]

+0

nó không hoạt động nếu bạn không tạo ra Long mặc dù. Tôi nghĩ rằng smt như bytearray.fromhex (hex (2 ** 61-1) .strip ('0x'). Strip ('L')) là an toàn hơn –

+0

@MarioAlemi mã trong nhận xét của bạn là sai. 'strip ('0x')' cũng sẽ loại bỏ các số 0, điều này sẽ dẫn đến kết quả xấu (và đôi khi 'ValueError')! – Lepi

+0

@Jess Austin: Giải pháp của bạn hoàn toàn sai, bởi vì nó chỉ hoạt động khi x bao gồm số chữ số thập phân. Ví dụ: 'x = 0x963b685edb4a8f336f624c9cc2b02d9ea4536a2df669c19e9794854d1d93105c81c289cde061c0448176dfeda9c18370db55f1d0fe5229ce07de704d88cc7f765c1c8d69874e008da8bd5b3cdfaf137a3a48edc229a41e0fa792a7c61686f1fL' – Lepi

6

Mọi người đều đã overcomplicated câu trả lời này:

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

Bạn chỉ cần biết số byte mà bạn đang cố gắng để chuyển đổi. Trong trường hợp sử dụng của tôi, thông thường tôi chỉ sử dụng số lượng lớn cho mật mã, và vào thời điểm đó tôi phải lo lắng về mô-đun và cái gì-không, vì vậy tôi không nghĩ rằng đây là một vấn đề lớn được yêu cầu để biết số lượng tối đa để trả về byte.

Vì bạn đang làm việc đó như 768-bit toán, sau đó thay vì 32 như là đối số nó sẽ là 96.

+0

Trong Python 3 giải pháp này đã làm việc thực sự tốt cho số nguyên 2048 bit. Nó Python 2,7 nó chỉ hoạt động với int (số nguyên 2048 bit dài trong Python 2.7). – desowin

+1

Trong Python 2,7 'some_bytes = some_int.to_bytes (32, sys.byteorder)' tạo lỗi 'AttributeError: đối tượng 'int' không có thuộc tính 'to_bytes'' – olibre

2

Python 2.7 không thực hiện() phương pháp int.to- slow_bytes rất.

tôi đã cố gắng 3 phương pháp:

  1. hex unpack/pack: rất chậm
  2. byte chuyển 8 bit tại một thời điểm: nhanh hơn đáng kể.
  3. sử dụng mô-đun "C" và đóng gói thành các byte thấp hơn (7 ia64 hoặc 3 i32). Tốc độ này nhanh gấp 2 lần. Đây là lựa chọn nhanh nhất, nhưng vẫn còn quá chậm.

Tất cả những phương pháp này rất không hiệu quả vì hai lý do:

  • Python 2.7 không hỗ trợ hoạt động này hữu ích.
  • c không hỗ trợ số học chính xác mở rộng bằng cờ mang/mượn/tràn sẵn có trên hầu hết các nền tảng.
0
i = 0x12345678 
s = struct.pack('<I',i) 
b = struct.unpack('BBBB',s) 
Các vấn đề liên quan