Legrandin / pycryptodome

A self-contained cryptographic library for Python
https://www.pycryptodome.org
Other
2.74k stars 492 forks source link

RSA.generate produces the same key each time #816

Closed ErnstStavroBlofeld closed 2 weeks ago

ErnstStavroBlofeld commented 2 weeks ago

Hi, the RSA.generate method produces the same key for me each time I run it. It used to run quite slowly some time ago but now it runs almost instantaneously. I tested this behaviour on multiple environments (windows+conda+python3.12.2,windows+python.3.12.4,macos+python3.10.14) and version 3.20.0 of the library.

Below is the code with isolated issue:

from Crypto.PublicKey import RSA
from Crypto.Hash.SHA256 import SHA256Hash
from Crypto import Random

for i in range(0, 5):
    key = RSA.generate(2048, Random.get_random_bytes)

    print('Random bytes', Random.get_random_bytes(32).hex())
    print('Key hash', SHA256Hash.new(key.export_key('DER')).digest().hex())

Running this yields:

Random bytes a007d92e05978d88c553b8f744943947f9a7f624a55d89961cc95f94378b294b
Key hash e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Random bytes 3038e8b66e3aaa8ea328f3eba18dd5dfbcca2be7da082c26feec4f4bb5dff9d3
Key hash e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Random bytes bedef7045e0942787c86bb24ba90e589af3f9dad87b80ee8b790f774594042ef
Key hash e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Random bytes 8ea0cbc0fd10ece2233072657b0f0d78e11eea06cf135faef138460f372e5859
Key hash e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Random bytes f9829db635dfd8e68f4828bbc2239f4e7aefdbc0078ff387675e659a574281ef
Key hash e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

I'm can't possibly imagine what might be causing this, any ideas?

Bl4omArchie commented 2 weeks ago

Hello, so I made a few tests and I ended up with this script:

for i in range(0, 5):
    key = RSA.generate(2048, Random.get_random_bytes)

    print('Random bytes', Random.get_random_bytes(32).hex())
    print('Key', SHA256Hash.new(key.export_key('DER')).digest().hex())
    print('Key', key.export_key('DER'))

In the output we can see that keys are not the same but are indeed identical once hashed. If you read the New features message of version 3.20.0, changes were made for Crypto.Hash.new() and SHA-2 and RSA keys exporting.

Conclusion: we need more investigation in the last commits of version 3.20.0 about Hash methods

Legrandin commented 2 weeks ago

The problem is that:

from Crypto.Hash.SHA256 import SHA256Hash
...
     print('Key hash', SHA256Hash.new(key.export_key('DER')).digest().hex())

should be:

 from Crypto.Hash import SHA256
 ...
     print('Key hash', SHA256.new(key.export_key('DER')).digest().hex())

In other words, the right way to create a new hash is to call SHA256.new.

SHA256Hash.new is an instance method, not a static method, and therefore its parameter is interpreted as the object instance, and not as the data to hash. In your program digest() returns the SHA256 of the empty string (which is always the same of course). More in general, the class SHA256Hash should never be used directly.

Bl4omArchie commented 2 weeks ago

Okay I didn't noticed! Maybe we should changed the semantic and add a warning message. With openSSL, for instance, when you are using RSA components directly withtout the EVP (envelop), a warning is displayed.