crypto.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. """A thin ctypes based wrapper for OpenSSL 1.0
  2. """
  3. import sys
  4. from ctypes import cdll, c_char_p, c_int, c_uint, c_void_p, POINTER, create_string_buffer
  5. from ctypes.util import find_library
  6. import struct
  7. libcrypto = cdll.LoadLibrary(find_library('crypto'))
  8. # Default libcrypto on OS X is too old, try the brew version
  9. if not hasattr(libcrypto, 'PKCS5_PBKDF2_HMAC') and sys.platform == 'darwin':
  10. libcrypto = cdll.LoadLibrary('/usr/local/opt/openssl/lib/libcrypto.dylib')
  11. # Default libcrypto on FreeBSD is too old, try the ports version
  12. if not hasattr(libcrypto, 'PKCS5_PBKDF2_HMAC') and sys.platform.startswith('freebsd'):
  13. libcrypto = cdll.LoadLibrary('/usr/local/lib/libcrypto.so')
  14. libcrypto.PKCS5_PBKDF2_HMAC.argtypes = (c_char_p, c_int, c_char_p, c_int, c_int, c_void_p, c_int, c_char_p)
  15. libcrypto.EVP_sha256.restype = c_void_p
  16. libcrypto.AES_set_encrypt_key.argtypes = (c_char_p, c_int, c_char_p)
  17. libcrypto.AES_ctr128_encrypt.argtypes = (c_char_p, c_char_p, c_int, c_char_p, c_char_p, c_char_p, POINTER(c_uint))
  18. _int = struct.Struct('>I')
  19. _long = struct.Struct('>Q')
  20. bytes_to_int = lambda x, offset=0: _int.unpack_from(x, offset)[0]
  21. bytes_to_long = lambda x, offset=0: _long.unpack_from(x, offset)[0]
  22. long_to_bytes = lambda x: _long.pack(x)
  23. def num_aes_blocks(length):
  24. """Return the number of AES blocks required to encrypt/decrypt *length* bytes of data
  25. """
  26. return (length + 15) // 16
  27. def pbkdf2_sha256(password, salt, iterations, size):
  28. """Password based key derivation function 2 (RFC2898)
  29. """
  30. key = create_string_buffer(size)
  31. rv = libcrypto.PKCS5_PBKDF2_HMAC(password, len(password), salt, len(salt), iterations, libcrypto.EVP_sha256(), size, key)
  32. if not rv:
  33. raise Exception('PKCS5_PBKDF2_HMAC failed')
  34. return key.raw
  35. def get_random_bytes(n):
  36. """Return n cryptographically strong pseudo-random bytes
  37. """
  38. buf = create_string_buffer(n)
  39. if not libcrypto.RAND_bytes(buf, n):
  40. raise Exception('RAND_bytes failed')
  41. return buf.raw
  42. class AES:
  43. """A thin wrapper around the OpenSSL AES CTR_MODE cipher
  44. """
  45. def __init__(self, key, iv=None):
  46. self.key = create_string_buffer(2000)
  47. self.iv = create_string_buffer(16)
  48. self.buf = create_string_buffer(16)
  49. self.num = c_uint()
  50. self.reset(key, iv)
  51. def reset(self, key=None, iv=None):
  52. if key:
  53. libcrypto.AES_set_encrypt_key(key, len(key) * 8, self.key)
  54. if iv:
  55. self.iv.raw = iv
  56. self.num.value = 0
  57. def encrypt(self, data):
  58. out = create_string_buffer(len(data))
  59. libcrypto.AES_ctr128_encrypt(data, out, len(data), self.key, self.iv, self.buf, self.num)
  60. return out.raw
  61. decrypt = encrypt