crypto.pyx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. """A thin OpenSSL wrapper
  2. This could be replaced by PyCrypto or something similar when the performance
  3. of their PBKDF2 implementation is comparable to the OpenSSL version.
  4. """
  5. from libc.string cimport memcpy
  6. from libc.stdlib cimport malloc, free
  7. API_VERSION = 1
  8. cdef extern from "openssl/rand.h":
  9. int RAND_bytes(unsigned char *buf,int num)
  10. cdef extern from "openssl/aes.h":
  11. ctypedef struct AES_KEY:
  12. pass
  13. int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key)
  14. void AES_ctr128_encrypt(const unsigned char *in_, unsigned char *out,
  15. size_t length, const AES_KEY *key,
  16. unsigned char *ivec,
  17. unsigned char *ecount_buf,
  18. unsigned int *num)
  19. cdef extern from "openssl/evp.h":
  20. ctypedef struct EVP_MD:
  21. pass
  22. const EVP_MD *EVP_sha256()
  23. int PKCS5_PBKDF2_HMAC(const char *password, int passwordlen,
  24. const unsigned char *salt, int saltlen, int iter,
  25. const EVP_MD *digest,
  26. int keylen, unsigned char *out)
  27. import struct
  28. _int = struct.Struct('>I')
  29. _long = struct.Struct('>Q')
  30. bytes_to_int = lambda x, offset=0: _int.unpack_from(x, offset)[0]
  31. bytes_to_long = lambda x, offset=0: _long.unpack_from(x, offset)[0]
  32. long_to_bytes = lambda x: _long.pack(x)
  33. def num_aes_blocks(length):
  34. """Return the number of AES blocks required to encrypt/decrypt *length* bytes of data
  35. """
  36. return (length + 15) // 16
  37. def pbkdf2_sha256(password, salt, iterations, size):
  38. """Password based key derivation function 2 (RFC2898)
  39. """
  40. cdef unsigned char *key = <unsigned char *>malloc(size)
  41. if not key:
  42. raise MemoryError
  43. try:
  44. rv = PKCS5_PBKDF2_HMAC(password, len(password), salt, len(salt), iterations, EVP_sha256(), size, key)
  45. if not rv:
  46. raise Exception('PKCS5_PBKDF2_HMAC failed')
  47. return key[:size]
  48. finally:
  49. free(key)
  50. def get_random_bytes(n):
  51. """Return n cryptographically strong pseudo-random bytes
  52. """
  53. cdef unsigned char *buf = <unsigned char *>malloc(n)
  54. if not buf:
  55. raise MemoryError
  56. try:
  57. if RAND_bytes(buf, n) < 1:
  58. raise Exception('RAND_bytes failed')
  59. return buf[:n]
  60. finally:
  61. free(buf)
  62. cdef class AES:
  63. """A thin wrapper around the OpenSSL AES CTR_MODE cipher
  64. """
  65. cdef AES_KEY key
  66. cdef unsigned char _iv[16]
  67. cdef unsigned char buf[16]
  68. cdef unsigned int num
  69. def __cinit__(self, key, iv=None):
  70. self.reset(key, iv)
  71. def reset(self, key=None, iv=None):
  72. if key:
  73. AES_set_encrypt_key(key, len(key) * 8, &self.key)
  74. if iv:
  75. memcpy(self._iv, <unsigned char *>iv, 16)
  76. self.num = 0
  77. @property
  78. def iv(self):
  79. return self._iv[:16]
  80. def encrypt(self, data):
  81. cdef int n = len(data)
  82. cdef unsigned char *out = <unsigned char *>malloc(n)
  83. if not out:
  84. raise MemoryError
  85. try:
  86. AES_ctr128_encrypt(data, out, len(data), &self.key, self._iv, self.buf, &self.num)
  87. return out[:n]
  88. finally:
  89. free(out)
  90. decrypt = encrypt