Search code examples
pythonhashcryptographyendiannesspycryptodome

changing the hash digest to big-endian in Python


I am using the following code to create hash for a message:

from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_PSS

msg = b'\x15\x00\xea'
hash = SHA256.new(msg)
...
signer = PKCS1_PSS.new(privKey)
signature = signer.sign(hash)

However, I need that the hash digest value i.e hash.digest() to be in big-endian format. Please note that I need to pass the whole hash to the signer. I can get the hash.digest() and convert it to big-endian, but then I need to construct a variable of type hash to pass to the signer.sig() function. How can I do that?


Solution

  • 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.