CodEdo
CodEdo

Reputation: 91

RSA signed data verification not working PyCryptodome

Sorry for the very specific problem, but I am really going crazy here. I am trying to make a module to simply import when I need to sign something or verify a signature but I encountered a problem, the verificator simply returns true wether I enter signed data or anything else, here is the code:

RSA_Handler.py

from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_v1_5
import pickle

def sign(data, exported_key):
    key = RSA.importKey(exported_key)
    signed_data = []
    signed_data.append(data)
    signed_data.append(PKCS1_v1_5.new(key).sign(SHA256.new(pickle.dumps(data))))
    return signed_data


def verify(signed_data, exported_key):
    data = signed_data[0]
    signature = signed_data[1]
    key = RSA.importKey(exported_key)
    h = SHA256.new(pickle.dumps(data))
    try:
        PKCS1_v1_5.new(key).verify(h, signature)
        return True

    except(ValueError, TypeError):
        return False

test01.py

from RSA_Handler import *
import pickle
import os


with open("keys.txt", "rb") as rb:
    keys = rb.read()




signed = sign("hello", keys)
trueorfalse = verify(["this will return"," true whatever I enter"], keys)

print(trueorfalse)

Upvotes: 1

Views: 1343

Answers (2)

Topaco
Topaco

Reputation: 49121

I can reproduce the problem (at least for the latest version of PyCryptodome, namely 3.9.8). It seems that the behavior depends on the padding type. In case of the module PKCS1_v1_5, which is currently used in the posted code, the verification does not raise a ValueError in case of an invalid signature, but returns the result as return value.

This means that your verify() function always returns True, because even in case of a mismatching signature neither a ValueError is raised nor the returned value is evaluated.

To solve the issue, your verify() function must be changed as follows:

def verify(signed_data, exported_key):
    data = signed_data[0]
    signature = signed_data[1]
    key = RSA.importKey(exported_key)
    h = SHA256.new(pickle.dumps(data))
    return PKCS1_v1_5.new(key).verify(h, signature)

which can be tested with:

# Signing
key = RSA.generate(1024)
keyPriv = key.exportKey()
signed = sign(b'Some data', keyPriv)

# Verifying
#signed[0] = b'Some data'             # Succeeds
signed[0] = b'Some other data'        # Fails
keyPub = key.publickey().exportKey()
verified = verify(signed, keyPub)
print(verified)

For PSS padding, i.e. for the module pss a ValueError is raised in case of an invalid signature. I.e. if you switch to this padding by replacing

from Crypto.Signature import PKCS1_v1_5

with

from Crypto.Signature import pss 

and also PKCS1_v1_5 with pss in the rest of the code, the logic in your verify() function can remain unchanged.

EDIT:

As explained in the answer from SquareRootOfTwentyThree, PKCS1_v1_5 is an outdated module and instead the module pkcs1_15 has to be used, which as expected generates a ValueError in case of an invalid signature, according to the documentation, here.

Upvotes: 2

SquareRootOfTwentyThree
SquareRootOfTwentyThree

Reputation: 7766

You are importing the obsolete module PKCS1_v1_5, which is not actually documented in in PyCryptodome. In your code, you must have instead:

from Crypto.Signature import import pkcs1_15

The PyCryptodome documentation (https://www.pycryptodome.org/en/latest/src/signature/pkcs1_v1_5.html) shows examples of the right way to verify PKCS#1 v1.5 signatures.

The old module you are using (that is, PKCS1_v1_5) is there purely for backward compatibility with PyCrypto, where it behaves in the same way you are observing (i.e. without exceptions - which is not that great, the new module is better).

Upvotes: 0

Related Questions