Source code for swiftly.dencrypt
"""
Encryption routines for Swiftly.
Requires PyCrypto 2.6.1 or greater.
<https://www.dlitz.net/software/pycrypto/>
Copyright 2013 Gregory Holt
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import hashlib
try:
import Crypto.Cipher.AES
import Crypto.Random
AES256CBC_Support = True
except ImportError:
AES256CBC_Support = False
#: Constant that can be used a preamble for algorithm detection.
AES256CBC = '\x00'
[docs]def aes_encrypt(key, stdin, preamble=None, chunk_size=65536,
content_length=None):
"""
Generator that encrypts a content stream using AES 256 in CBC
mode.
:param key: Any string to use as the encryption key.
:param stdin: Where to read the contents from.
:param preamble: str to yield initially useful for providing a
hint for future readers as to the algorithm in use.
:param chunk_size: Largest amount to read at once.
:param content_length: The number of bytes to read from stdin.
None or < 0 indicates reading until EOF.
"""
if not AES256CBC_Support:
raise Exception(
'AES256CBC not supported; likely pycrypto is not installed')
if preamble:
yield preamble
# Always use 256-bit key
key = hashlib.sha256(key).digest()
# At least 16 and a multiple of 16
chunk_size = max(16, chunk_size >> 4 << 4)
iv = Crypto.Random.new().read(16)
yield iv
encryptor = Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_CBC, iv)
reading = True
left = None
if content_length is not None and content_length >= 0:
left = content_length
while reading:
size = chunk_size
if left is not None and size > left:
size = left
chunk = stdin.read(size)
if not chunk:
if left is not None and left > 0:
raise IOError('Early EOF from input')
# Indicates how many usable bytes in last block
yield encryptor.encrypt('\x00' * 16)
break
if left is not None:
left -= len(chunk)
if left <= 0:
reading = False
block = chunk
trailing = len(block) % 16
while trailing:
size = 16 - trailing
if left is not None and size > left:
size = left
chunk = stdin.read(size)
if not chunk:
if left is not None and left > 0:
raise IOError('Early EOF from input')
reading = False
# Indicates how many usable bytes in last block
chunk = chr(trailing) * (16 - trailing)
elif left is not None:
left -= len(chunk)
if left <= 0:
reading = False
block += chunk
trailing = len(block) % 16
yield encryptor.encrypt(block)
[docs]def aes_decrypt(key, stdin, chunk_size=65536):
"""
Generator that decrypts a content stream using AES 256 in CBC
mode.
:param key: Any string to use as the decryption key.
:param stdin: Where to read the encrypted data from.
:param chunk_size: Largest amount to read at once.
"""
if not AES256CBC_Support:
raise Exception(
'AES256CBC not supported; likely pycrypto is not installed')
# Always use 256-bit key
key = hashlib.sha256(key).digest()
# At least 16 and a multiple of 16
chunk_size = max(16, chunk_size >> 4 << 4)
iv = stdin.read(16)
while len(iv) < 16:
chunk = stdin.read(16 - len(iv))
if not chunk:
raise IOError('EOF reading IV')
decryptor = Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_CBC, iv)
data = ''
while True:
chunk = stdin.read(chunk_size)
if not chunk:
if len(data) != 16:
raise IOError('EOF reading encrypted stream')
data = decryptor.decrypt(data)
trailing = ord(data[-1])
if trailing > 15:
raise IOError(
'EOF reading encrypted stream or trailing value corrupted '
'%s' % trailing)
yield data[:trailing]
break
data += chunk
if len(data) > 16:
# Always leave at least one byte pending
trailing = (len(data) % 16) or 16
yield decryptor.decrypt(data[:-trailing])
data = data[-trailing:]