Skip to content

Commit

Permalink
Use cryptography instead of pycrypto
Browse files Browse the repository at this point in the history
pycrypto seems to be unmaintained these days
(cf. pycrypto/pycrypto#238).

Fixes: s3ql#65.
  • Loading branch information
Nikratio committed Jan 14, 2019
1 parent 46bc0c5 commit e21825a
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 17 deletions.
3 changes: 3 additions & 0 deletions Changes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Unreleased Changes
* Command line options specified in the authinfo file (in particular
--backend-options) are now parsed correctly.

* S3QL now uses python-cryptography instead of the (no longer
maintained) pycrypto module.

2018-12-28, S3QL 2.33

* Fixed a sporadic test failure in t5_cache.py.
Expand Down
2 changes: 1 addition & 1 deletion rst/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ that is not the case.
* The following Python modules:

* `setuptools <https://pypi.python.org/pypi/setuptools>`_, version 1.0 or newer.
* `pycrypto <https://www.dlitz.net/software/pycrypto/>`_
* `cryptography <https://cryptography.io/en/latest/installation/>`_
* `defusedxml <https://pypi.python.org/pypi/defusedxml/>`_
* `requests <https://pypi.python.org/pypi/requests/>`_ (optional,
required for OAuth2 authentication with Google Storage)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def main():
compile_args.append('-Wno-unused-function')

required_pkgs = ['apsw >= 3.7.0',
'pycrypto',
'cryptography',
'requests',
'defusedxml',
'dugong >= 3.4, < 4.0',
Expand Down
45 changes: 30 additions & 15 deletions src/s3ql/backends/comprenc.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from ..common import ThawError, freeze_basic_mapping, thaw_basic_mapping
from ..inherit_docstrings import (copy_ancestor_docstring, prepend_ancestor_docstring,
ABCDocstMeta)
from Crypto.Cipher import AES
from Crypto.Util import Counter
import cryptography.hazmat.primitives.ciphers as crypto_ciphers
import cryptography.hazmat.backends as crypto_backends
import bz2
import hashlib
import hmac
Expand All @@ -30,14 +30,28 @@
# Used only by adm.py
UPGRADE_MODE=False

crypto_backend = crypto_backends.default_backend()

def sha256(s):
return hashlib.sha256(s).digest()

def aes_cipher(key):
def aes_encryptor(key):
'''Return AES cipher in CTR mode for *key*'''

cipher = crypto_ciphers.Cipher(
crypto_ciphers.algorithms.AES(key),
crypto_ciphers.modes.CTR(nonce=bytes(16)),
backend=crypto_backend)
return cipher.encryptor()

def aes_decryptor(key):
'''Return AES cipher in CTR mode for *key*'''

return AES.new(key, AES.MODE_CTR,
counter=Counter.new(128, initial_value=0))
cipher = crypto_ciphers.Cipher(
crypto_ciphers.algorithms.AES(key),
crypto_ciphers.modes.CTR(nonce=bytes(16)),
backend=crypto_backend)
return cipher.decryptor()

class ComprencBackend(AbstractBackend, metaclass=ABCDocstMeta):
'''
Expand Down Expand Up @@ -160,7 +174,8 @@ def _verify_meta(self, key, metadata):
raise CorruptedObjectError('Object content does not match its key (%s vs %s)'
% (stored_key, key))

buf = aes_cipher(meta_key).decrypt(meta_buf)
decryptor = aes_decryptor(meta_key)
buf = decryptor.update(meta_buf) + decryptor.finalize()
meta = thaw_basic_mapping(buf)
if UPGRADE_MODE:
meta['needs_reupload'] = update_required
Expand Down Expand Up @@ -246,9 +261,10 @@ def open_write(self, key, metadata=None, is_compressed=False):
nonce = struct.pack('<d', time.time()) + key.encode('utf-8')
meta_key = sha256(self.passphrase + nonce + b'meta')
data_key = sha256(self.passphrase + nonce)
encryptor = aes_encryptor(meta_key)
meta_raw['encryption'] = 'AES_v2'
meta_raw['nonce'] = nonce
meta_raw['data'] = aes_cipher(meta_key).encrypt(meta_buf)
meta_raw['data'] = encryptor.update(meta_buf) + encryptor.finalize()
meta_raw['object_id'] = key
meta_raw['signature'] = checksum_basic_mapping(meta_raw, meta_key)
else:
Expand Down Expand Up @@ -309,7 +325,8 @@ def _copy_or_rename(self, src, dest, rename, metadata=None):
meta_buf = freeze_basic_mapping(meta_old)
else:
meta_buf = freeze_basic_mapping(metadata)
meta_raw['data'] = aes_cipher(meta_key).encrypt(meta_buf)
encryptor = aes_encryptor(meta_key)
meta_raw['data'] = encryptor.update(meta_buf) + encryptor.finalize()
meta_raw['object_id'] = dest
meta_raw['signature'] = checksum_basic_mapping(meta_raw, meta_key)
elif metadata is None:
Expand Down Expand Up @@ -489,7 +506,7 @@ def __init__(self, fh, key):
self.fh = fh
self.obj_size = 0
self.closed = False
self.cipher = aes_cipher(key)
self.encryptor = aes_encryptor(key)
self.hmac = hmac.new(key, digestmod=hashlib.sha256)

def write(self, data):
Expand All @@ -508,7 +525,7 @@ def write(self, data):

buf = struct.pack(b'<I', len(data)) + data
self.hmac.update(buf)
buf2 = self.cipher.encrypt(buf)
buf2 = self.encryptor.update(buf)
assert len(buf2) == len(buf)
self.fh.write(buf2)
self.obj_size += len(buf2)
Expand All @@ -522,7 +539,7 @@ def close(self):
buf = struct.pack(b'<I', 0)
self.hmac.update(buf)
buf += self.hmac.digest()
buf2 = self.cipher.encrypt(buf)
buf2 = self.encryptor.update(buf)
assert len(buf) == len(buf2)
self.fh.write(buf2)
self.obj_size += len(buf2)
Expand Down Expand Up @@ -563,7 +580,7 @@ def __init__(self, fh, key, metadata=None):
self.remaining = 0 # Remaining length of current packet
self.metadata = metadata
self.hmac_checked = False
self.cipher = aes_cipher(key)
self.decryptor = aes_decryptor(key)
self.hmac = hmac.new(key, digestmod=hashlib.sha256)

def _read_and_decrypt(self, size):
Expand All @@ -576,13 +593,11 @@ def _read_and_decrypt(self, size):
if not buf:
raise CorruptedObjectError('Premature end of stream.')

# Work around https://bugs.launchpad.net/pycrypto/+bug/1256172
# cipher.decrypt refuses to work with anything but bytes
if not isinstance(buf, bytes):
buf = bytes(buf)

len_ = len(buf)
buf = self.cipher.decrypt(buf)
buf = self.decryptor.update(buf)
assert len(buf) == len_

return buf
Expand Down

0 comments on commit e21825a

Please sign in to comment.