aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2018-12-10 12:13:31 +0800
committerAlex Gaynor <alex.gaynor@gmail.com>2018-12-09 23:13:31 -0500
commit3c68250ad9bfb275c760fcce4c72c78c99b57c34 (patch)
tree06c6f5b1e513d9d16ec25ab25ae927b47172a7ca
parentc2d16370f00e42fae13e492c0b1c7b3a83a5e495 (diff)
downloadcryptography-3c68250ad9bfb275c760fcce4c72c78c99b57c34.tar.gz
cryptography-3c68250ad9bfb275c760fcce4c72c78c99b57c34.tar.bz2
cryptography-3c68250ad9bfb275c760fcce4c72c78c99b57c34.zip
allow bytes-like for key/iv/data for symmetric encryption (#4621)
* allow bytearrays for key/iv for symmetric encryption * bump pypy/cffi requirements * update docs, fix some tests * old openssl is naught but pain * revert a typo * use trusty for old pypy * better error msg again * restore match
-rw-r--r--.travis.yml4
-rw-r--r--CHANGELOG.rst1
-rw-r--r--docs/glossary.rst6
-rw-r--r--docs/hazmat/primitives/symmetric-encryption.rst69
-rw-r--r--docs/installation.rst2
-rw-r--r--pyproject.toml2
-rw-r--r--setup.py6
-rw-r--r--src/cryptography/hazmat/backends/openssl/ciphers.py21
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/algorithms.py4
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/modes.py14
-rw-r--r--src/cryptography/utils.py7
-rw-r--r--tests/hazmat/primitives/test_aes.py42
-rw-r--r--tests/hazmat/primitives/test_chacha20.py12
13 files changed, 141 insertions, 49 deletions
diff --git a/.travis.yml b/.travis.yml
index 2a636ad1..b8ecb7d6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,9 +31,9 @@ matrix:
env: TOXENV=py37
- python: 3.7
env: TOXENV=py37-idna
- - python: pypy-5.3
+ - python: pypy-5.4
env: TOXENV=pypy-nocoverage
- # PyPy 5.3 isn't available for xenial
+ # PyPy 5.4 isn't available for xenial
dist: trusty
- python: pypy2.7-5.10.0
env: TOXENV=pypy-nocoverage
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 25c7c8c3..54004b48 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -12,6 +12,7 @@ Changelog
version 2.1, but this version removes the default ``idna`` dependency as
well. If you still need this deprecated path please install cryptography
with the ``idna`` extra: ``pip install cryptography[idna]``.
+* **BACKWARDS INCOMPATIBLE:** The minimum supported PyPy version is now 5.4.
* Added support for :class:`~cryptography.hazmat.primitives.hashes.SHA512_224`
and :class:`~cryptography.hazmat.primitives.hashes.SHA512_256` when using
OpenSSL 1.1.1.
diff --git a/docs/glossary.rst b/docs/glossary.rst
index ce08dbaa..95b893c8 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -94,6 +94,11 @@ Glossary
bit key, you can calculate the number of bytes by dividing by 8. 128
divided by 8 is 16, so a 128 bit key is a 16 byte key.
+ bytes-like
+ A bytes-like object contains binary data and supports the
+ `buffer protocol`_. This includes ``bytes``, ``bytearray``, and
+ ``memoryview`` objects.
+
U-label
The presentational unicode form of an internationalized domain
name. U-labels use unicode characters outside the ASCII range and
@@ -101,3 +106,4 @@ Glossary
.. _`hardware security module`: https://en.wikipedia.org/wiki/Hardware_security_module
.. _`idna`: https://pypi.org/project/idna/
+.. _`buffer protocol`: https://docs.python.org/3/c-api/buffer.html
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index 31f240c4..4924ddd6 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -92,8 +92,9 @@ Algorithms
AES is both fast, and cryptographically strong. It is a good default
choice for encryption.
- :param bytes key: The secret key. This must be kept secret. Either ``128``,
+ :param key: The secret key. This must be kept secret. Either ``128``,
``192``, or ``256`` :term:`bits` long.
+ :type key: :term:`bytes-like`
.. class:: Camellia(key)
@@ -101,8 +102,9 @@ Algorithms
It is considered to have comparable security and performance to AES but
is not as widely studied or deployed.
- :param bytes key: The secret key. This must be kept secret. Either ``128``,
+ :param key: The secret key. This must be kept secret. Either ``128``,
``192``, or ``256`` :term:`bits` long.
+ :type key: :term:`bytes-like`
.. class:: ChaCha20(key)
@@ -120,15 +122,17 @@ Algorithms
ChaCha20 is a stream cipher used in several IETF protocols. It is
standardized in :rfc:`7539`.
- :param bytes key: The secret key. This must be kept secret. ``256``
+ :param key: The secret key. This must be kept secret. ``256``
:term:`bits` (32 bytes) in length.
+ :type key: :term:`bytes-like`
- :param bytes nonce: Should be unique, a :term:`nonce`. It is
+ :param nonce: Should be unique, a :term:`nonce`. It is
critical to never reuse a ``nonce`` with a given key. Any reuse of a
nonce with the same key compromises the security of every message
encrypted with that key. The nonce does not need to be kept secret
and may be included with the ciphertext. This must be ``128``
:term:`bits` in length.
+ :type nonce: :term:`bytes-like`
.. note::
@@ -161,12 +165,13 @@ Algorithms
Nonetheless, Triple DES is not recommended for new applications because it
is incredibly slow; old applications should consider moving away from it.
- :param bytes key: The secret key. This must be kept secret. Either ``64``,
+ :param key: The secret key. This must be kept secret. Either ``64``,
``128``, or ``192`` :term:`bits` long. DES only uses ``56``, ``112``,
or ``168`` bits of the key as there is a parity byte in each component
of the key. Some writing refers to there being up to three separate
keys that are each ``56`` bits long, they can simply be concatenated
to produce the full key.
+ :type key: :term:`bytes-like`
.. class:: CAST5(key)
@@ -177,8 +182,9 @@ Algorithms
a variable key length cipher and supports keys from 40-128 :term:`bits` in
length.
- :param bytes key: The secret key, This must be kept secret. 40 to 128
+ :param key: The secret key, This must be kept secret. 40 to 128
:term:`bits` in length in increments of 8 bits.
+ :type key: :term:`bytes-like`
.. class:: SEED(key)
@@ -188,8 +194,9 @@ Algorithms
(KISA). It is defined in :rfc:`4269` and is used broadly throughout South
Korean industry, but rarely found elsewhere.
- :param bytes key: The secret key. This must be kept secret. ``128``
+ :param key: The secret key. This must be kept secret. ``128``
:term:`bits` in length.
+ :type key: :term:`bytes-like`
Weak ciphers
------------
@@ -206,8 +213,9 @@ Weak ciphers
susceptible to attacks when using weak keys. The author has recommended
that users of Blowfish move to newer algorithms such as :class:`AES`.
- :param bytes key: The secret key. This must be kept secret. 32 to 448
+ :param key: The secret key. This must be kept secret. 32 to 448
:term:`bits` in length in increments of 8 bits.
+ :type key: :term:`bytes-like`
.. class:: ARC4(key)
@@ -215,9 +223,10 @@ Weak ciphers
initial stream output. Its use is strongly discouraged. ARC4 does not use
mode constructions.
- :param bytes key: The secret key. This must be kept secret. Either ``40``,
+ :param key: The secret key. This must be kept secret. Either ``40``,
``56``, ``64``, ``80``, ``128``, ``192``, or ``256`` :term:`bits` in
length.
+ :type key: :term:`bytes-like`
.. doctest::
@@ -238,8 +247,9 @@ Weak ciphers
is susceptible to attacks when using weak keys. It is recommended that you
do not use this cipher for new applications.
- :param bytes key: The secret key. This must be kept secret. ``128``
+ :param key: The secret key. This must be kept secret. ``128``
:term:`bits` in length.
+ :type key: :term:`bytes-like`
.. _symmetric-encryption-modes:
@@ -256,13 +266,14 @@ Modes
**Padding is required when using this mode.**
- :param bytes initialization_vector: Must be :doc:`random bytes
+ :param initialization_vector: Must be :doc:`random bytes
</random-numbers>`. They do not need to be kept secret and they can be
included in a transmitted message. Must be the same number of bytes as
the ``block_size`` of the cipher. Each time something is encrypted a
new ``initialization_vector`` should be generated. Do not reuse an
``initialization_vector`` with a given ``key``, and particularly do not
use a constant ``initialization_vector``.
+ :type initialization_vector: :term:`bytes-like`
A good construction looks like:
@@ -295,12 +306,13 @@ Modes
**This mode does not require padding.**
- :param bytes nonce: Should be unique, a :term:`nonce`. It is
+ :param nonce: Should be unique, a :term:`nonce`. It is
critical to never reuse a ``nonce`` with a given key. Any reuse of a
nonce with the same key compromises the security of every message
encrypted with that key. Must be the same number of bytes as the
``block_size`` of the cipher with a given key. The nonce does not need
to be kept secret and may be included with the ciphertext.
+ :type nonce: :term:`bytes-like`
.. class:: OFB(initialization_vector)
@@ -309,11 +321,12 @@ Modes
**This mode does not require padding.**
- :param bytes initialization_vector: Must be :doc:`random bytes
+ :param initialization_vector: Must be :doc:`random bytes
</random-numbers>`. They do not need to be kept secret and they can be
included in a transmitted message. Must be the same number of bytes as
the ``block_size`` of the cipher. Do not reuse an
``initialization_vector`` with a given ``key``.
+ :type initialization_vector: :term:`bytes-like`
.. class:: CFB(initialization_vector)
@@ -322,11 +335,12 @@ Modes
**This mode does not require padding.**
- :param bytes initialization_vector: Must be :doc:`random bytes
+ :param initialization_vector: Must be :doc:`random bytes
</random-numbers>`. They do not need to be kept secret and they can be
included in a transmitted message. Must be the same number of bytes as
the ``block_size`` of the cipher. Do not reuse an
``initialization_vector`` with a given ``key``.
+ :type initialization_vector: :term:`bytes-like`
.. class:: CFB8(initialization_vector)
@@ -336,11 +350,12 @@ Modes
**This mode does not require padding.**
- :param bytes initialization_vector: Must be :doc:`random bytes
+ :param initialization_vector: Must be :doc:`random bytes
</random-numbers>`. They do not need to be kept secret and they can be
included in a transmitted message. Must be the same number of bytes as
the ``block_size`` of the cipher. Do not reuse an
``initialization_vector`` with a given ``key``.
+ :type initialization_vector: :term:`bytes-like`
.. class:: GCM(initialization_vector, tag=None, min_tag_length=16)
@@ -368,12 +383,13 @@ Modes
**This mode does not require padding.**
- :param bytes initialization_vector: Must be unique, a :term:`nonce`.
+ :param initialization_vector: Must be unique, a :term:`nonce`.
They do not need to be kept secret and they can be included in a
transmitted message. NIST `recommends a 96-bit IV length`_ for
performance critical situations but it can be up to 2\ :sup:`64` - 1
:term:`bits`. Do not reuse an ``initialization_vector`` with a given
``key``.
+ :type initialization_vector: :term:`bytes-like`
.. note::
@@ -495,11 +511,11 @@ Modes
**This mode does not require padding.**
- :param bytes tweak: The tweak is a 16 byte value typically derived from
+ :param tweak: The tweak is a 16 byte value typically derived from
something like the disk sector number. A given ``(tweak, key)`` pair
should not be reused, although doing so is less catastrophic than
in CTR mode.
-
+ :type tweak: :term:`bytes-like`
Insecure modes
--------------
@@ -544,7 +560,8 @@ Interfaces
.. method:: update(data)
- :param bytes data: The data you wish to pass into the context.
+ :param data: The data you wish to pass into the context.
+ :type data: :term:`bytes-like`
:return bytes: Returns the data that was encrypted or decrypted.
:raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
@@ -567,7 +584,8 @@ Interfaces
requirement and you will be making many small calls to
``update_into``.
- :param bytes data: The data you wish to pass into the context.
+ :param data: The data you wish to pass into the context.
+ :type data: :term:`bytes-like`
:param buf: A writable Python buffer that the data will be written
into. This buffer should be ``len(data) + n - 1`` bytes where ``n``
is the block size (in bytes) of the cipher and mode combination.
@@ -629,7 +647,8 @@ Interfaces
.. method:: authenticate_additional_data(data)
- :param bytes data: Any data you wish to authenticate but not encrypt.
+ :param data: Any data you wish to authenticate but not encrypt.
+ :type data: :term:`bytes-like`
:raises: :class:`~cryptography.exceptions.AlreadyFinalized`
.. class:: AEADEncryptionContext
@@ -747,7 +766,7 @@ Interfaces used by the symmetric cipher modes described in
.. attribute:: initialization_vector
- :type: bytes
+ :type: :term:`bytes-like`
Exact requirements of the initialization are described by the
documentation of individual modes.
@@ -759,7 +778,7 @@ Interfaces used by the symmetric cipher modes described in
.. attribute:: nonce
- :type: bytes
+ :type: :term:`bytes-like`
Exact requirements of the nonce are described by the documentation of
individual modes.
@@ -771,7 +790,7 @@ Interfaces used by the symmetric cipher modes described in
.. attribute:: tag
- :type: bytes
+ :type: :term:`bytes-like`
Exact requirements of the tag are described by the documentation of
individual modes.
@@ -785,7 +804,7 @@ Interfaces used by the symmetric cipher modes described in
.. attribute:: tweak
- :type: bytes
+ :type: :term:`bytes-like`
Exact requirements of the tweak are described by the documentation of
individual modes.
diff --git a/docs/installation.rst b/docs/installation.rst
index 2d9db667..5b2854d9 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -11,7 +11,7 @@ Supported platforms
-------------------
Currently we test ``cryptography`` on Python 2.7, 3.4+, and
-PyPy 5.3+ on these operating systems.
+PyPy 5.4+ on these operating systems.
* x86-64 CentOS 7.x
* macOS 10.12 Sierra, 10.11 El Capitan
diff --git a/pyproject.toml b/pyproject.toml
index 461675f0..7d64f993 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -3,5 +3,5 @@
requires = [
"setuptools>=18.5",
"wheel",
- "cffi>=1.7,!=1.11.3; python_implementation != 'PyPy'",
+ "cffi>=1.8,!=1.11.3; python_implementation != 'PyPy'",
]
diff --git a/setup.py b/setup.py
index bd98fd5e..5b29d32e 100644
--- a/setup.py
+++ b/setup.py
@@ -44,12 +44,12 @@ with open(os.path.join(src_dir, "cryptography", "__about__.py")) as f:
VECTORS_DEPENDENCY = "cryptography_vectors=={0}".format(about['__version__'])
# `setup_requirements` must be kept in sync with `pyproject.toml`
-setup_requirements = ["cffi>=1.7,!=1.11.3"]
+setup_requirements = ["cffi>=1.8,!=1.11.3"]
if platform.python_implementation() == "PyPy":
- if sys.pypy_version_info < (5, 3):
+ if sys.pypy_version_info < (5, 4):
raise RuntimeError(
- "cryptography is not compatible with PyPy < 5.3. Please upgrade "
+ "cryptography is not compatible with PyPy < 5.4. Please upgrade "
"PyPy to use this library."
)
diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py
index e0ee06ee..fe5715b2 100644
--- a/src/cryptography/hazmat/backends/openssl/ciphers.py
+++ b/src/cryptography/hazmat/backends/openssl/ciphers.py
@@ -56,13 +56,15 @@ class _CipherContext(object):
)
if isinstance(mode, modes.ModeWithInitializationVector):
- iv_nonce = mode.initialization_vector
+ iv_nonce = self._backend._ffi.from_buffer(
+ mode.initialization_vector
+ )
elif isinstance(mode, modes.ModeWithTweak):
- iv_nonce = mode.tweak
+ iv_nonce = self._backend._ffi.from_buffer(mode.tweak)
elif isinstance(mode, modes.ModeWithNonce):
- iv_nonce = mode.nonce
+ iv_nonce = self._backend._ffi.from_buffer(mode.nonce)
elif isinstance(cipher, modes.ModeWithNonce):
- iv_nonce = cipher.nonce
+ iv_nonce = self._backend._ffi.from_buffer(cipher.nonce)
else:
iv_nonce = self._backend._ffi.NULL
# begin init with cipher and operation type
@@ -105,7 +107,7 @@ class _CipherContext(object):
ctx,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
- cipher.key,
+ self._backend._ffi.from_buffer(cipher.key),
iv_nonce,
operation
)
@@ -131,8 +133,10 @@ class _CipherContext(object):
"unsigned char *", self._backend._ffi.from_buffer(buf)
)
outlen = self._backend._ffi.new("int *")
- res = self._backend._lib.EVP_CipherUpdate(self._ctx, buf, outlen,
- data, len(data))
+ res = self._backend._lib.EVP_CipherUpdate(
+ self._ctx, buf, outlen,
+ self._backend._ffi.from_buffer(data), len(data)
+ )
self._backend.openssl_assert(res != 0)
return outlen[0]
@@ -215,7 +219,8 @@ class _CipherContext(object):
def authenticate_additional_data(self, data):
outlen = self._backend._ffi.new("int *")
res = self._backend._lib.EVP_CipherUpdate(
- self._ctx, self._backend._ffi.NULL, outlen, data, len(data)
+ self._ctx, self._backend._ffi.NULL, outlen,
+ self._backend._ffi.from_buffer(data), len(data)
)
self._backend.openssl_assert(res != 0)
diff --git a/src/cryptography/hazmat/primitives/ciphers/algorithms.py b/src/cryptography/hazmat/primitives/ciphers/algorithms.py
index 21d9ecf0..1f49fd9d 100644
--- a/src/cryptography/hazmat/primitives/ciphers/algorithms.py
+++ b/src/cryptography/hazmat/primitives/ciphers/algorithms.py
@@ -13,7 +13,7 @@ from cryptography.hazmat.primitives.ciphers.modes import ModeWithNonce
def _verify_key_size(algorithm, key):
# Verify that the key is instance of bytes
- utils._check_bytes("key", key)
+ utils._check_byteslike("key", key)
# Verify that the key size matches the expected key size
if len(key) * 8 not in algorithm.key_sizes:
@@ -153,7 +153,7 @@ class ChaCha20(object):
def __init__(self, key, nonce):
self.key = _verify_key_size(self, key)
- utils._check_bytes("nonce", nonce)
+ utils._check_byteslike("nonce", nonce)
if len(nonce) != 16:
raise ValueError("nonce must be 128-bits (16 bytes)")
diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py
index d2444580..ad91a6e1 100644
--- a/src/cryptography/hazmat/primitives/ciphers/modes.py
+++ b/src/cryptography/hazmat/primitives/ciphers/modes.py
@@ -88,7 +88,7 @@ class CBC(object):
name = "CBC"
def __init__(self, initialization_vector):
- utils._check_bytes("initialization_vector", initialization_vector)
+ utils._check_byteslike("initialization_vector", initialization_vector)
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
@@ -101,7 +101,7 @@ class XTS(object):
name = "XTS"
def __init__(self, tweak):
- utils._check_bytes("tweak", tweak)
+ utils._check_byteslike("tweak", tweak)
if len(tweak) != 16:
raise ValueError("tweak must be 128-bits (16 bytes)")
@@ -131,7 +131,7 @@ class OFB(object):
name = "OFB"
def __init__(self, initialization_vector):
- utils._check_bytes("initialization_vector", initialization_vector)
+ utils._check_byteslike("initialization_vector", initialization_vector)
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
@@ -144,7 +144,7 @@ class CFB(object):
name = "CFB"
def __init__(self, initialization_vector):
- utils._check_bytes("initialization_vector", initialization_vector)
+ utils._check_byteslike("initialization_vector", initialization_vector)
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
@@ -157,7 +157,7 @@ class CFB8(object):
name = "CFB8"
def __init__(self, initialization_vector):
- utils._check_bytes("initialization_vector", initialization_vector)
+ utils._check_byteslike("initialization_vector", initialization_vector)
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
@@ -170,7 +170,7 @@ class CTR(object):
name = "CTR"
def __init__(self, nonce):
- utils._check_bytes("nonce", nonce)
+ utils._check_byteslike("nonce", nonce)
self._nonce = nonce
nonce = utils.read_only_property("_nonce")
@@ -195,7 +195,7 @@ class GCM(object):
# len(initialization_vector) must in [1, 2 ** 64), but it's impossible
# to actually construct a bytes object that large, so we don't check
# for it
- utils._check_bytes("initialization_vector", initialization_vector)
+ utils._check_byteslike("initialization_vector", initialization_vector)
if len(initialization_vector) == 0:
raise ValueError("initialization_vector must be at least 1 byte")
self._initialization_vector = initialization_vector
diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py
index 3d45a771..65a4ee71 100644
--- a/src/cryptography/utils.py
+++ b/src/cryptography/utils.py
@@ -30,6 +30,13 @@ def _check_bytes(name, value):
raise TypeError("{0} must be bytes".format(name))
+def _check_byteslike(name, value):
+ try:
+ memoryview(value)
+ except TypeError:
+ raise TypeError("{0} must be bytes-like".format(name))
+
+
def read_only_property(name):
return property(lambda self: getattr(self, name))
diff --git a/tests/hazmat/primitives/test_aes.py b/tests/hazmat/primitives/test_aes.py
index 4ceccf15..90a6b3b0 100644
--- a/tests/hazmat/primitives/test_aes.py
+++ b/tests/hazmat/primitives/test_aes.py
@@ -455,3 +455,45 @@ class TestAESModeGCM(object):
).decryptor()
with pytest.raises(ValueError):
decryptor.finalize_with_tag(b"tagtooshort")
+
+ def test_buffer_protocol(self, backend):
+ data = bytearray(b"helloworld")
+ enc = base.Cipher(
+ algorithms.AES(bytearray(b"\x00" * 16)),
+ modes.GCM(bytearray(b"\x00" * 12)),
+ backend
+ ).encryptor()
+ enc.authenticate_additional_data(bytearray(b"foo"))
+ ct = enc.update(data) + enc.finalize()
+ dec = base.Cipher(
+ algorithms.AES(bytearray(b"\x00" * 16)),
+ modes.GCM(bytearray(b"\x00" * 12), enc.tag),
+ backend
+ ).decryptor()
+ dec.authenticate_additional_data(bytearray(b"foo"))
+ pt = dec.update(ct) + dec.finalize()
+ assert pt == data
+
+
+@pytest.mark.parametrize(
+ "mode",
+ [
+ modes.CBC(bytearray(b"\x00" * 16)),
+ modes.CTR(bytearray(b"\x00" * 16)),
+ modes.OFB(bytearray(b"\x00" * 16)),
+ modes.CFB(bytearray(b"\x00" * 16)),
+ modes.CFB8(bytearray(b"\x00" * 16)),
+ modes.XTS(bytearray(b"\x00" * 16)),
+ ]
+)
+@pytest.mark.requires_backend_interface(interface=CipherBackend)
+def test_buffer_protocol_alternate_modes(mode, backend):
+ data = bytearray(b"sixteen_byte_msg")
+ cipher = base.Cipher(
+ algorithms.AES(bytearray(b"\x00" * 32)), mode, backend
+ )
+ enc = cipher.encryptor()
+ ct = enc.update(data) + enc.finalize()
+ dec = cipher.decryptor()
+ pt = dec.update(ct) + dec.finalize()
+ assert pt == data
diff --git a/tests/hazmat/primitives/test_chacha20.py b/tests/hazmat/primitives/test_chacha20.py
index 33730d91..7c475c0f 100644
--- a/tests/hazmat/primitives/test_chacha20.py
+++ b/tests/hazmat/primitives/test_chacha20.py
@@ -44,6 +44,18 @@ class TestChaCha20(object):
computed_ct = encryptor.update(pt) + encryptor.finalize()
assert binascii.hexlify(computed_ct) == vector["ciphertext"]
+ def test_buffer_protocol(self, backend):
+ key = bytearray(os.urandom(32))
+ nonce = bytearray(os.urandom(16))
+ cipher = Cipher(
+ algorithms.ChaCha20(key, nonce), None, backend
+ )
+ enc = cipher.encryptor()
+ ct = enc.update(bytearray(b"hello")) + enc.finalize()
+ dec = cipher.decryptor()
+ pt = dec.update(ct) + dec.finalize()
+ assert pt == b"hello"
+
def test_key_size(self):
chacha = algorithms.ChaCha20(b"0" * 32, b"0" * 16)
assert chacha.key_size == 256