borg/attic/crypto.py

78 lines
2.8 KiB
Python
Raw Normal View History

"""A thin ctypes based wrapper for OpenSSL 1.0
"""
2013-06-27 19:56:14 +00:00
import sys
2013-06-24 20:41:05 +00:00
from ctypes import cdll, c_char_p, c_int, c_uint, c_void_p, POINTER, create_string_buffer
from ctypes.util import find_library
import struct
libcrypto = cdll.LoadLibrary(find_library('crypto'))
2013-06-27 19:10:29 +00:00
# Default libcrypto on OS X is too old, try the brew version
2013-06-27 19:56:14 +00:00
if not hasattr(libcrypto, 'PKCS5_PBKDF2_HMAC') and sys.platform == 'darwin':
2013-06-27 19:10:29 +00:00
libcrypto = cdll.LoadLibrary('/usr/local/opt/openssl/lib/libcrypto.dylib')
# Default libcrypto on FreeBSD is too old, try the ports version
2013-07-28 12:55:48 +00:00
if not hasattr(libcrypto, 'PKCS5_PBKDF2_HMAC') and sys.platform.startswith('freebsd'):
libcrypto = cdll.LoadLibrary('/usr/local/lib/libcrypto.so')
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)
libcrypto.EVP_sha256.restype = c_void_p
libcrypto.AES_set_encrypt_key.argtypes = (c_char_p, c_int, c_char_p)
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))
2013-08-17 10:35:43 +00:00
libcrypto.RAND_bytes.argtypes = (c_char_p, c_int)
libcrypto.RAND_bytes.restype = c_int
_int = struct.Struct('>I')
_long = struct.Struct('>Q')
bytes_to_int = lambda x, offset=0: _int.unpack_from(x, offset)[0]
bytes_to_long = lambda x, offset=0: _long.unpack_from(x, offset)[0]
long_to_bytes = lambda x: _long.pack(x)
def num_aes_blocks(length):
"""Return the number of AES blocks required to encrypt/decrypt *length* bytes of data
"""
return (length + 15) // 16
def pbkdf2_sha256(password, salt, iterations, size):
"""Password based key derivation function 2 (RFC2898)
"""
key = create_string_buffer(size)
rv = libcrypto.PKCS5_PBKDF2_HMAC(password, len(password), salt, len(salt), iterations, libcrypto.EVP_sha256(), size, key)
if not rv:
raise Exception('PKCS5_PBKDF2_HMAC failed')
return key.raw
def get_random_bytes(n):
"""Return n cryptographically strong pseudo-random bytes
"""
buf = create_string_buffer(n)
2013-08-17 10:35:43 +00:00
if libcrypto.RAND_bytes(buf, n) < 1:
raise Exception('RAND_bytes failed')
return buf.raw
class AES:
"""A thin wrapper around the OpenSSL AES CTR_MODE cipher
"""
def __init__(self, key, iv=None):
self.key = create_string_buffer(2000)
self.iv = create_string_buffer(16)
self.buf = create_string_buffer(16)
self.num = c_uint()
self.reset(key, iv)
def reset(self, key=None, iv=None):
if key:
libcrypto.AES_set_encrypt_key(key, len(key) * 8, self.key)
if iv:
self.iv.raw = iv
self.num.value = 0
def encrypt(self, data):
out = create_string_buffer(len(data))
libcrypto.AES_ctr128_encrypt(data, out, len(data), self.key, self.iv, self.buf, self.num)
return out.raw
decrypt = encrypt