Amanda_Panda
Amanda_Panda

Reputation: 1186

Error with Python crypto on Windows

I'm running a basic crypto program written in Python, and while it worked fine on OS X, I cannot get it to run on Windows (either in 3.6/Anaconda that was installed with VS 2017 when I checked in the setup that I wanted Python installed, and in a standalone 3.4 binary install).

Like individually each import statement works in the interpreter, but as a whole this program doesn't work

from hashlib import sha256

from pbkdf2_ctypes import *
import hmac
import hashlib
import binascii
from os import urandom
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import getpass

masterpassword = "thisisamasterpassword"
salt = urandom(16) 
masterpassword = pbkdf2_hex(masterpassword.encode('utf-8'), salt)
password = masterpassword.decode()
salt = binascii.hexlify(salt)
salt = salt.decode()

print(masterpassword)

The result is:

C:\Users\me\Desktop>py -3.4 masterpassword.py
Traceback (most recent call last):
  File "C:\Python34\lib\site-packages\pbkdf2_ctypes.py", line 127, in <module>
    raise OSError('Library not found')
OSError: Library not found

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "masterpassword.py", line 3, in <module>
    from pbkdf2_ctypes import *
  File "C:\Python34\lib\site-packages\pbkdf2_ctypes.py", line 153, in <module>
    raise ImportError('Cannot find a compatible cryptographic library '
ImportError: Cannot find a compatible cryptographic library on your system

I also installed both an OpenSSL binary (https://slproweb.com/products/Win32OpenSSL.html) and made sure it was running under Anaconda.

Upvotes: 0

Views: 1404

Answers (1)

DoDoSmarts
DoDoSmarts

Reputation: 306

If I were to guess this code never worked on a Windows-64bit machine. The error that is raised comes from pbkdf2_ctypes in the logic to search for the crypto library; and I think it was an accidental (although sensible) assumption that libeay64.dll will be installed on 64-bit systems and libeay32.dll for 32-bit systems:

if system == 'Windows':
    if platform.architecture()[0] == '64bit':
        libname = ctypes.util.find_library('libeay64') # <--- This does not exist even on 64bit machines ... :)
        if not libname:
            raise OSError('Library not found')
        crypto = ctypes.CDLL(libname)
    else:
        libname = ctypes.util.find_library('libeay32')
        if not libname:
            raise OSError('Library libeay32 not found.')

You can try to contact someone from Glisco, but I don't think they're around anymore as their public code base has gone quite for a couple years now.

To get you going

You can either:

  1. run ctypes.util.find_library('libeay32') in python and see where your library is. Then copy libeay32.dll to libeay64.dll in the same folder. This shouldn't cause any problems because you're duplicating a file that no other program is aware of.

  2. Remove from pbkdf2_ctypes import *, and add these functions to your code that were ripped from pbkdf2_ctypes.

    import ctypes import ctypes.util

    libname = ctypes.util.find_library('libeay32') crypto = ctypes.CDLL(libname)

    def _openssl_hashlib_to_crypto_map_get(hashfunc): hashlib_to_crypto_map = {hashlib.md5: crypto.EVP_md5, hashlib.sha1: crypto.EVP_sha1, hashlib.sha256: crypto.EVP_sha256, hashlib.sha224: crypto.EVP_sha224, hashlib.sha384: crypto.EVP_sha384, hashlib.sha512: crypto.EVP_sha512} crypto_hashfunc = hashlib_to_crypto_map.get(hashfunc) if crypto_hashfunc is None: raise ValueError('Unkwnown digest %s' % hashfunc) crypto_hashfunc.restype = ctypes.c_void_p return crypto_hashfunc()

    def _openssl_pbkdf2(data, salt, iterations, digest, keylen): """OpenSSL compatibile wrapper """ c_hashfunc = ctypes.c_void_p(_openssl_hashlib_to_crypto_map_get(digest))

    c_pass = ctypes.c_char_p(data)
    c_passlen = ctypes.c_int(len(data))
    c_salt = ctypes.c_char_p(salt)
    c_saltlen = ctypes.c_int(len(salt))
    c_iter = ctypes.c_int(iterations)
    c_keylen = ctypes.c_int(keylen)
    c_buff = ctypes.create_string_buffer(keylen)
    
    crypto.PKCS5_PBKDF2_HMAC.argtypes = [ctypes.c_char_p, ctypes.c_int,
                                     ctypes.c_char_p, ctypes.c_int,
                                     ctypes.c_int, ctypes.c_void_p,
                                     ctypes.c_int, ctypes.c_char_p]
    
    crypto.PKCS5_PBKDF2_HMAC.restype = ctypes.c_int
    err = crypto.PKCS5_PBKDF2_HMAC(c_pass, c_passlen,
                        c_salt, c_saltlen,
                        c_iter,
                        c_hashfunc,
                        c_keylen,
                        c_buff)
    return (err, c_buff)
    

    def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): if hashfunc is None: hashfunc = hashlib.sha1 err, c_buff = _openssl_pbkdf2(data, salt, iterations, hashfunc, keylen)

    if err == 0:
        raise ValueError('wrong parameters')
    return c_buff.raw[:keylen]
    

    def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None): return binascii.hexlify(pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc))

Upvotes: 1

Related Questions