Why do I need to import both Hash from Crypto and PKCS1_OAEP from Crypto.Cipher for Hash.MD5 to work?

With Both PKCS1_OAEP and Hash

from Crypto.Cipher import PKCS1_OAEP
from Crypto import Hash

Hash.MD5

That code works without issue.

With Just Hash

from Crypto import Hash

Hash.MD5

That code gives me the following error:

Traceback (most recent call last):
  File "test.py", line 3, in <module>
    Hash.MD5
AttributeError: module 'Crypto.Hash' has no attribute 'MD5'

With Just PKCS1_OAEP

This doesn’t work either:

from Crypto.Cipher import PKCS1_OAEP

Hash.MD5

That gives this error:

Traceback (most recent call last):
  File "test.py", line 3, in <module>
    Hash.MD5
NameError: name 'Hash' is not defined

I feel like it ought to work with just from Crypto import Hash but that’s clearly not the case. Any ideas?

(I’m running Python 3.8.3)

With Both PKCS1_OAEP and Hash

from Crypto.Cipher import PKCS1_OAEP
from Crypto import Hash

Hash.MD5

That code works without issue.

With Just Hash

from Crypto import Hash

Hash.MD5

That code gives me the following error:

Traceback (most recent call last):
 File "test.py", line 3, in <module>
   Hash.MD5
AttributeError: module 'Crypto.Hash' has no attribute 'MD5'

Without knowing what the “Crypto” package is or where you got it, I
would be guessing that the “Crypto.Cipher” module does some kind of
registration which enables or defines “.MD5” in the the “Hash” module.
Without that import, such registration does not happen.

I’d be very surprised if this were not mentioned in its docs; side
effects like that are pretty important.

With Just PKCS1_OAEP

This doesn’t work either:

from Crypto.Cipher import PKCS1_OAEP

Hash.MD5

That gives this error:

Traceback (most recent call last):
 File "test.py", line 3, in <module>
   Hash.MD5
NameError: name 'Hash' is not defined

Well, that’s perfectly normal. You haven’t imported the name “Hash”, so
it is unknown.

I feel like it ought to work with just from Crypto import Hash but that’s clearly not the case. Any ideas?

Aye, that would be a normal expectation. My theory above is just a
guess, since I know nothing about your “Crypto” package.

Cheers,
Cameron Simpson cs@cskk.id.au

I’m using PyCryptodome

Thank you. I’ve tried your examples.

I believe the problem is that Crypto.Hash is itself a package, not a
module. This means that there an MD5 module inside Crypto.Hash.

When you import Crypto.Hash, it runs the file
…/site-packages/Crypto/Hash/init.py. That will define various
names (possibly none - it is often an empty placeholder), but won’t
inherently import all the modules beside it.

So when you “import Crypto.Hash” you will get the name “Hash” containing
the things defined by the init.py file from the Hash package. That
does not imply there’s a Hash.MD5 name.

Conversely, “from Crypto.Hash import MD5” imports the name MD5 from the
Crypto.Hash package (collection of modules) i.e. the Crypto.Hash.MD5
module itself, and gives it the name “MD5” in your own namespace.

There’s an example of that form under “API principles” here:

https://www.pycryptodome.org/en/latest/src/hash/hash.html

which goes:

from Crypto.Hash import SHA256

I agree this is an unfortunate user experience (you being the user).

Cheers,
Cameron Simpson cs@cskk.id.au