Convert String from Big-Endian to Little-Endian or Vice Versa in Python

Python) Convert Big Endian to Little Endian

Using a bytearray (Try it online!):

data = "F324658951425AF3EB0011"
bits = [16, 8, 8, 32, 8, 16]

b = bytearray.fromhex(data)
i = 0
for n in bits:
n //= 8
b[i:i+n] = reversed(b[i:i+n])
i += n
print(b.hex().upper())

Or with memoryview (Try it online!):

data = "F324658951425AF3EB0011"
bits = [16, 8, 8, 32, 8, 16]

b = bytearray.fromhex(data)
m = memoryview(b)
for n in bits:
n //= 8
m[:n] = m[n-1::-1]
m = m[n:]
print(b.hex().upper())

Convert little endian string to integer

The struct module converts packed data to Python values, and vice-versa.

>>> import struct
>>> struct.unpack("<h", "\x00\x05")
(1280,)
>>> struct.unpack("<h", "\x00\x06")
(1536,)
>>> struct.unpack("<h", "\x01\x06")
(1537,)

"h" means a short int, or 16-bit int. "<" means use little-endian.

Convert hex to uint16 little-endian

You need to process each element of the list separately, not the whole list at once. You can do this with a list comprehension.

If you want little-endian, you have to swap the halves of the string before parsing it as an integer:

hex = ['1A05','1A05','1A05'] 
endian = [int(h[2:4] + h[0:2], 16) for h in hex]

However, that will only work if the hex strings are always 4 characters. You can also swap the bytes of the integer using bit operations after parsing:

for h in hex:
i = int(h, 16)
i = i & 0xff << 16 | i & 0xff00 >> 16
endian.append(i)

changing the hash digest to big-endian in Python

As mentioned in my comment, I'm not sure if it is really required to invert the hash.

But if so, you need an implementation that does not process the raw data, but the already hashed data (and does not hash anymore).

PyCryptodome does not support this, but the Cryptography library with the Prehashed class does, s. here, 2nd snippet.

The following code performs a signing with PKCS#1 v1.5 and:

  • with PyCryptodome (implicit hashing)
  • with Cryptography (implicit hashing)
  • with Cryptography (using the Prehashed class and explicit hashing)
  • with Cryptography (using the Prehashed class and explicit hashing) and using the inverted hash
# Short key only for testing purposes!
pkcs8 = '''-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA2gdsVIRmg5IH0rG3
u3w+gHCZq5o4OMQIeomC1NTeHgxbkrfznv7TgWVzrHpr3HHK8IpLlG04/aBo6U5W
2umHQQIDAQABAkEAu7wulGvZFat1Xv+19BMcgl3yhCdsB70Mi+7CH98XTwjACk4T
+IYv4N53j16gce7U5fJxmGkdq83+xAyeyw8U0QIhAPIMhbtXlRS7XpkB66l5DvN1
XrKRWeB3RtvcUSf30RyFAiEA5ph7eWXbXWpIhdWMoe50yffF7pW+C5z07tzAIH6D
Ko0CIQCyveSTr917bdIxk2V/xNHxnx7LJuMEC5DcExorNanKMQIgUxHRQU1hNgjI
sXXZoKgfaHaa1jUZbmOPlNDvYYVRyS0CIB9ZZee2zubyRla4qN8PQxCJb7DiICmH
7nWP7CIvcQwB
-----END PRIVATE KEY-----'''
message = b'The quick brown fox jumps over the lazy dog'

# 1. PyCryptodome (implicit hashing)
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA

privateKey = RSA.import_key(pkcs8)
digest = SHA256.new(message)
signature = pkcs1_15.new(privateKey).sign(digest)
print(signature.hex())

# 2. Cryptography (implicit hashing)
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization

privateKey = serialization.load_pem_private_key(
pkcs8.encode('utf8'),
password=None,
)
digest = hashes.SHA256()
signature = privateKey.sign(
message,
padding.PKCS1v15(),
digest
)
print(signature.hex())

# 3. Cryptography (explicit hashing)
from cryptography.hazmat.primitives.asymmetric import utils

digest = hashes.SHA256()
hasher = hashes.Hash(digest)
hasher.update(message)
hash = hasher.finalize()
signature = privateKey.sign(
hash,
padding.PKCS1v15(),
utils.Prehashed(digest) # The digest must still be specified because the digest ID is included in the signature.
)
print(signature.hex())

# 4. Cryptography (explicit hashing), inverse hash
hashBA = bytearray(hash)
hashBA.reverse()
hashReversed = bytes(hashBA)
signature = privateKey.sign(
hashReversed,
padding.PKCS1v15(),
utils.Prehashed(digest) # The digest must still be specified because the digest ID is included in the signature.
)
print(signature.hex())

with the output:

8c83cad897eda249fec9eba231061d585dafc99177267e3e71bb8a3fce07cc6663bf4df7af2e1c1945d2a6bb42eb25f042228b591fc18cda82d92caae844670c
8c83cad897eda249fec9eba231061d585dafc99177267e3e71bb8a3fce07cc6663bf4df7af2e1c1945d2a6bb42eb25f042228b591fc18cda82d92caae844670c
8c83cad897eda249fec9eba231061d585dafc99177267e3e71bb8a3fce07cc6663bf4df7af2e1c1945d2a6bb42eb25f042228b591fc18cda82d92caae844670c
550ba1cd3c968fa1e24b79a939edb6740b63d2ab021fe87f0639ce978d5127792661e83f4e8fdff8124a12fe208bd70bdca9db2b9c82306f2ed018ab06363c9e

Since the PKCS#1 v1.5 signature is deterministic, cases 1 to 3 produce the same signature.



Related Topics



Leave a reply



Submit