aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.rst8
-rw-r--r--cryptography/__about__.py2
-rw-r--r--cryptography/hazmat/backends/commoncrypto/backend.py6
-rw-r--r--cryptography/hazmat/backends/commoncrypto/ciphers.py16
-rw-r--r--cryptography/hazmat/bindings/commoncrypto/seckey.py1
-rw-r--r--cryptography/hazmat/primitives/ciphers/modes.py9
-rw-r--r--cryptography/hazmat/primitives/constant_time.py16
-rw-r--r--docs/fernet.rst2
-rw-r--r--docs/hazmat/primitives/asymmetric/ec.rst91
-rw-r--r--docs/hazmat/primitives/symmetric-encryption.rst23
-rw-r--r--docs/spelling_wordlist.txt2
-rw-r--r--tasks.py1
-rw-r--r--tests/hazmat/backends/test_commoncrypto.py6
-rw-r--r--tests/hazmat/primitives/test_aes.py4
-rw-r--r--tests/hazmat/primitives/utils.py13
15 files changed, 126 insertions, 74 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index e057b636..a49f3f46 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,11 @@ Changelog
.. note:: This version is not yet released and is under active development.
+* **BACKWARDS INCOMPATIBLE:**
+ :class:`~cryptography.hazmat.primitives.ciphers.modes.GCM` no longer allows
+ truncation of tags by default. Previous versions of ``cryptography`` allowed
+ tags to be truncated by default, applications wishing to preserve this
+ behavior (not recommended) can pass the ``min_tag_length`` argument.
* Added :class:`~cryptography.hazmat.primitives.kdf.hkdf.HKDFExpand`.
* Added :class:`~cryptography.hazmat.primitives.ciphers.modes.CFB8` support
for :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES` and
@@ -18,7 +23,8 @@ Changelog
and
:class:`~cryptography.hazmat.backends.interfaces.TraditionalOpenSSLSerializationBackend`
support to the :doc:`/hazmat/backends/openssl`.
-* Added :class:`~cryptography.hazmat.backends.interfaces.EllipticCurveBackend`.
+* Added :doc:`/hazmat/primitives/asymmetric/ec` and
+ :class:`~cryptography.hazmat.backends.interfaces.EllipticCurveBackend`.
* Added :class:`~cryptography.hazmat.primitives.ciphers.modes.ECB` support
for :class:`~cryptography.hazmat.primitives.ciphers.algorithms.TripleDES` on
:doc:`/hazmat/backends/commoncrypto` and :doc:`/hazmat/backends/openssl`.
diff --git a/cryptography/__about__.py b/cryptography/__about__.py
index ee53902b..ccbcdfe8 100644
--- a/cryptography/__about__.py
+++ b/cryptography/__about__.py
@@ -28,4 +28,4 @@ __author__ = "The cryptography developers"
__email__ = "cryptography-dev@python.org"
__license__ = "Apache License, Version 2.0"
-__copyright__ = "Copyright 2013-2014 %s" % __author__
+__copyright__ = "Copyright 2013-2014 {0}".format(__author__)
diff --git a/cryptography/hazmat/backends/commoncrypto/backend.py b/cryptography/hazmat/backends/commoncrypto/backend.py
index f78370c4..7bab979f 100644
--- a/cryptography/hazmat/backends/commoncrypto/backend.py
+++ b/cryptography/hazmat/backends/commoncrypto/backend.py
@@ -149,7 +149,7 @@ class Backend(object):
buf,
length
)
- self._check_response(res)
+ self._check_cipher_response(res)
return self._ffi.buffer(buf)[:]
@@ -223,7 +223,7 @@ class Backend(object):
self._lib.kCCModeRC4
)
- def _check_response(self, response):
+ def _check_cipher_response(self, response):
if response == self._lib.kCCSuccess:
return
elif response == self._lib.kCCAlignmentError:
@@ -246,7 +246,7 @@ class Backend(object):
"""
if ctx[0] != self._ffi.NULL:
res = self._lib.CCCryptorRelease(ctx[0])
- self._check_response(res)
+ self._check_cipher_response(res)
ctx[0] = self._ffi.NULL
diff --git a/cryptography/hazmat/backends/commoncrypto/ciphers.py b/cryptography/hazmat/backends/commoncrypto/ciphers.py
index 2820fd0e..525500c8 100644
--- a/cryptography/hazmat/backends/commoncrypto/ciphers.py
+++ b/cryptography/hazmat/backends/commoncrypto/ciphers.py
@@ -78,7 +78,7 @@ class _CipherContext(object):
self._backend._lib.ccNoPadding, iv_nonce,
cipher.key, len(cipher.key),
self._backend._ffi.NULL, 0, 0, mode_option, ctx)
- self._backend._check_response(res)
+ self._backend._check_cipher_response(res)
self._ctx = ctx
@@ -91,7 +91,7 @@ class _CipherContext(object):
res = self._backend._lib.CCCryptorUpdate(
self._ctx[0], data, len(data), buf,
len(data) + self._byte_block_size - 1, outlen)
- self._backend._check_response(res)
+ self._backend._check_cipher_response(res)
return self._backend._ffi.buffer(buf)[:outlen[0]]
def finalize(self):
@@ -105,7 +105,7 @@ class _CipherContext(object):
outlen = self._backend._ffi.new("size_t *")
res = self._backend._lib.CCCryptorFinal(
self._ctx[0], buf, len(buf), outlen)
- self._backend._check_response(res)
+ self._backend._check_cipher_response(res)
self._backend._release_cipher_ctx(self._ctx)
return self._backend._ffi.buffer(buf)[:outlen[0]]
@@ -143,14 +143,14 @@ class _GCMCipherContext(object):
self._backend._ffi.NULL,
cipher.key, len(cipher.key),
self._backend._ffi.NULL, 0, 0, 0, self._ctx)
- self._backend._check_response(res)
+ self._backend._check_cipher_response(res)
res = self._backend._lib.CCCryptorGCMAddIV(
self._ctx[0],
mode.initialization_vector,
len(mode.initialization_vector)
)
- self._backend._check_response(res)
+ self._backend._check_cipher_response(res)
def update(self, data):
buf = self._backend._ffi.new("unsigned char[]", len(data))
@@ -160,7 +160,7 @@ class _GCMCipherContext(object):
else:
res = self._backend._lib.CCCryptorGCMDecrypt(*args)
- self._backend._check_response(res)
+ self._backend._check_cipher_response(res)
return self._backend._ffi.buffer(buf)[:]
def finalize(self):
@@ -170,7 +170,7 @@ class _GCMCipherContext(object):
res = self._backend._lib.CCCryptorGCMFinal(
self._ctx[0], tag_buf, tag_len
)
- self._backend._check_response(res)
+ self._backend._check_cipher_response(res)
self._backend._release_cipher_ctx(self._ctx)
self._tag = self._backend._ffi.buffer(tag_buf)[:]
if (self._operation == self._backend._lib.kCCDecrypt and
@@ -184,7 +184,7 @@ class _GCMCipherContext(object):
res = self._backend._lib.CCCryptorGCMAddAAD(
self._ctx[0], data, len(data)
)
- self._backend._check_response(res)
+ self._backend._check_cipher_response(res)
@property
def tag(self):
diff --git a/cryptography/hazmat/bindings/commoncrypto/seckey.py b/cryptography/hazmat/bindings/commoncrypto/seckey.py
index 38aaece8..5e4b6dac 100644
--- a/cryptography/hazmat/bindings/commoncrypto/seckey.py
+++ b/cryptography/hazmat/bindings/commoncrypto/seckey.py
@@ -23,6 +23,7 @@ typedef ... *SecKeyRef;
FUNCTIONS = """
OSStatus SecKeyGeneratePair(CFDictionaryRef, SecKeyRef *, SecKeyRef *);
+size_t SecKeyGetBlockSize(SecKeyRef);
"""
MACROS = """
diff --git a/cryptography/hazmat/primitives/ciphers/modes.py b/cryptography/hazmat/primitives/ciphers/modes.py
index e70a9db5..509b4de2 100644
--- a/cryptography/hazmat/primitives/ciphers/modes.py
+++ b/cryptography/hazmat/primitives/ciphers/modes.py
@@ -97,13 +97,16 @@ class CTR(object):
class GCM(object):
name = "GCM"
- def __init__(self, initialization_vector, tag=None):
+ def __init__(self, initialization_vector, tag=None, min_tag_length=16):
# 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
- if tag is not None and len(tag) < 4:
+ if min_tag_length < 4:
+ raise ValueError("min_tag_length must be >= 4")
+ if tag is not None and len(tag) < min_tag_length:
raise ValueError(
- "Authentication tag must be 4 bytes or longer."
+ "Authentication tag must be {0} bytes or longer.".format(
+ min_tag_length)
)
self.initialization_vector = initialization_vector
diff --git a/cryptography/hazmat/primitives/constant_time.py b/cryptography/hazmat/primitives/constant_time.py
index 4547da13..9789851a 100644
--- a/cryptography/hazmat/primitives/constant_time.py
+++ b/cryptography/hazmat/primitives/constant_time.py
@@ -13,6 +13,7 @@
from __future__ import absolute_import, division, print_function
+import hmac
import sys
import cffi
@@ -53,9 +54,18 @@ _lib = _ffi.verify(
ext_package="cryptography",
)
+if hasattr(hmac, "compare_digest"):
+ def bytes_eq(a, b):
+ if not isinstance(a, bytes) or not isinstance(b, bytes):
+ raise TypeError("a and b must be bytes.")
+
+ return hmac.compare_digest(a, b)
-def bytes_eq(a, b):
- if not isinstance(a, bytes) or not isinstance(b, bytes):
+else:
+ def bytes_eq(a, b):
+ if not isinstance(a, bytes) or not isinstance(b, bytes):
raise TypeError("a and b must be bytes.")
- return _lib.Cryptography_constant_time_bytes_eq(a, len(a), b, len(b)) == 1
+ return _lib.Cryptography_constant_time_bytes_eq(
+ a, len(a), b, len(b)
+ ) == 1
diff --git a/docs/fernet.rst b/docs/fernet.rst
index 1c4918ad..4b713a54 100644
--- a/docs/fernet.rst
+++ b/docs/fernet.rst
@@ -83,7 +83,7 @@ Specifically it uses:
* :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES` in
:class:`~cryptography.hazmat.primitives.ciphers.modes.CBC` mode with a
128-bit key for encryption; using
- :class:`~cryptography.hazmat.primitives.ciphers.PKCS7` padding.
+ :class:`~cryptography.hazmat.primitives.padding.PKCS7` padding.
* :class:`~cryptography.hazmat.primitives.hmac.HMAC` using
:class:`~cryptography.hazmat.primitives.hashes.SHA256` for authentication.
* Initialization vectors are generated using ``os.urandom()``.
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst
index 4b3c460e..0e19bb2e 100644
--- a/docs/hazmat/primitives/asymmetric/ec.rst
+++ b/docs/hazmat/primitives/asymmetric/ec.rst
@@ -1,6 +1,6 @@
.. hazmat::
-Elliptic Curve Cryptography
+Elliptic curve cryptography
===========================
.. currentmodule:: cryptography.hazmat.primitives.asymmetric.ec
@@ -13,7 +13,7 @@ Elliptic Curve Cryptography
Generate a new private key on ``curve`` for use with ``backend``.
:param backend: A
- :class:`~cryptography.hazmat.primtives.interfaces.EllipticCurve`
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurve`
provider.
:param backend: A
@@ -21,10 +21,38 @@ Elliptic Curve Cryptography
provider.
:returns: A new instance of a
- :class:`~cryptography.hazmat.primtivies.interfaces.EllipticCurvePrivateKey`
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurvePrivateKey`
provider.
+Elliptic Curve Signature Algorithms
+-----------------------------------
+
+.. class:: ECDSA(algorithm)
+
+ .. versionadded:: 0.5
+
+ The ECDSA signature algorithm first standardized in NIST publication
+ `FIPS 186-3`_, and later in `FIPS 186-4`_.
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ .. doctest::
+
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> from cryptography.hazmat.primitives.asymmetric import ec
+ >>> private_key = ec.generate_private_key(
+ ... ec.SECP384R1(), default_backend()
+ ... )
+ >>> signer = private_key.signer(ec.ECDSA(hashes.SHA256()))
+ >>> signer.update(b"this is some data I'd like")
+ >>> signer.update(b" to sign")
+ >>> signature = signer.finalize()
+
+
.. class:: EllipticCurvePrivateNumbers(private_value, public_numbers)
.. versionadded:: 0.5
@@ -33,7 +61,7 @@ Elliptic Curve Cryptography
.. attribute:: public_numbers
- :type: :class:`~cryptography.hazmat.primitives.ec.EllipticCurvePublicNumbers`
+ :type: :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicNumbers`
The :class:`EllipticCurvePublicNumbers` which makes up the EC public
key associated with this EC private key.
@@ -54,7 +82,7 @@ Elliptic Curve Cryptography
provider.
:returns: A new instance of a
- :class:`~cryptography.hazmat.primtivies.interfaces.EllipticCurvePrivateKey`
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurvePrivateKey`
provider.
@@ -92,48 +120,35 @@ Elliptic Curve Cryptography
provider.
:returns: A new instance of a
- :class:`~cryptography.hazmat.primtivies.interfaces.EllipticCurvePublicKey`
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey`
provider.
+Elliptic Curves
+---------------
-Elliptic Curve Signature Algorithms
------------------------------------
-
-.. class:: ECDSA(algorithm)
-
- .. versionadded:: 0.5
-
- The ECDSA signature algorithm first standardized in NIST publication
- `FIPS 186-3`_, and later in `FIPS 186-4`_.
+Elliptic curves provide equivalent security at much smaller key sizes than
+asymmetric cryptography systems such as RSA or DSA. For some operations they
+can also provide higher performance at every security level. According to NIST
+they can have as much as a `64x lower computational cost than DH`_.
- :param algorithm: An instance of a
- :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
- provider.
+.. note::
+ Curves with a size of `less than 224 bits`_ should not be used. You should
+ strongly consider using curves of at least 224 bits.
- .. doctest::
+Generally the NIST prime field ("P") curves are significantly faster than the
+other types suggested by NIST at both signing and verifying with ECDSA.
- >>> from cryptography.hazmat.backends import default_backend
- >>> from cryptography.hazmat.primitives import hashes
- >>> from cryptography.hazmat.primitives.asymmetric import ec
- >>> private_key = ec.generate_private_key(
- ... ec.SECP384R1(), default_backend()
- ... )
- >>> signer = private_key.signer(ec.ECDSA(hashes.SHA256()))
- >>> signer.update(b"this is some data I'd like")
- >>> signer.update(b" to sign")
- >>> signature = signer.finalize()
+Prime fields also `minimize the number of security concerns for elliptic-curve
+cryptography`_. However there is `some concern`_ that both the prime field and
+binary field ("B") NIST curves may have been weakened during their generation.
-Elliptic Curves
----------------
+Currently `cryptography` only supports NIST curves, none of which are
+considered "safe" by the `SafeCurves`_ project run by Daniel J. Bernstein and
+Tanja Lange.
All named curves are providers of
:class:`~cryptography.hazmat.primtives.interfaces.EllipticCurve`.
-There is `some concern`_ that the non-Koblitz NIST curves (identified by names
-that start with "B" or "P") may have been intentionally weakened by their
-generation process.
-
-
.. class:: SECT571K1
.. versionadded:: 0.5
@@ -243,3 +258,7 @@ generation process.
.. _`FIPS 186-3`: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
.. _`FIPS 186-4`: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
.. _`some concern`: https://crypto.stackexchange.com/questions/10263/should-we-trust-the-nist-recommended-ecc-parameters
+.. _`less than 224 bits`: http://www.ecrypt.eu.org/documents/D.SPA.20.pdf
+.. _`64x lower computational cost than DH`: http://www.nsa.gov/business/programs/elliptic_curve.shtml
+.. _`minimize the number of security concerns for elliptic-curve cryptography`: http://cr.yp.to/ecdh/curve25519-20060209.pdf
+.. _`SafeCurves`: http://safecurves.cr.yp.to/
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index abc2b076..586285b7 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -288,7 +288,7 @@ Modes
Must be the same number of bytes as the ``block_size`` of the cipher.
Do not reuse an ``initialization_vector`` with a given ``key``.
-.. class:: GCM(initialization_vector, tag=None)
+.. class:: GCM(initialization_vector, tag=None, min_tag_length=16)
.. danger::
@@ -318,13 +318,23 @@ Modes
You can shorten a tag by truncating it to the desired length but this
is **not recommended** as it lowers the security margins of the
authentication (`NIST SP-800-38D`_ recommends 96-bits or greater).
- If you must shorten the tag the minimum allowed length is 4 bytes
- (32-bits). Applications **must** verify the tag is the expected length
- to guarantee the expected security margin.
+ Applications wishing to allow truncation must pass the
+ ``min_tag_length`` parameter.
+
+ .. versionchanged:: 0.5
+
+ The ``min_tag_length`` parameter was added in ``0.5``, previously
+ truncation down to ``4`` bytes was always allowed.
:param bytes tag: The tag bytes to verify during decryption. When
encrypting this must be ``None``.
+ :param bytes min_tag_length: The minimum length ``tag`` must be. By default
+ this is ``16``, meaning tag truncation is not allowed. Allowing tag
+ truncation is strongly discouraged for most applications.
+
+ :raises ValueError: This is raised if ``len(tag) < min_tag_length``.
+
.. testcode::
import os
@@ -356,11 +366,6 @@ Modes
return (iv, ciphertext, encryptor.tag)
def decrypt(key, associated_data, iv, ciphertext, tag):
- if len(tag) != 16:
- raise ValueError(
- "tag must be 16 bytes -- truncation not supported"
- )
-
# Construct a Cipher object, with the key, iv, and additionally the
# GCM tag used for authenticating the message.
decryptor = Cipher(
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index dc123493..d90547a8 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -29,6 +29,7 @@ introspectability
invariants
iOS
Koblitz
+Lange
metadata
namespace
namespaces
@@ -39,6 +40,7 @@ preprocessors
pseudorandom
Schneier
scrypt
+Tanja
testability
Ubuntu
unencrypted
diff --git a/tasks.py b/tasks.py
index 94a95414..65e33aea 100644
--- a/tasks.py
+++ b/tasks.py
@@ -10,6 +10,7 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+
from __future__ import absolute_import, division, print_function
import getpass
diff --git a/tests/hazmat/backends/test_commoncrypto.py b/tests/hazmat/backends/test_commoncrypto.py
index 7c703f67..e2c6f4a0 100644
--- a/tests/hazmat/backends/test_commoncrypto.py
+++ b/tests/hazmat/backends/test_commoncrypto.py
@@ -51,13 +51,13 @@ class TestCommonCrypto(object):
from cryptography.hazmat.backends.commoncrypto.backend import backend
with pytest.raises(ValueError):
- backend._check_response(backend._lib.kCCAlignmentError)
+ backend._check_cipher_response(backend._lib.kCCAlignmentError)
with pytest.raises(InternalError):
- backend._check_response(backend._lib.kCCMemoryFailure)
+ backend._check_cipher_response(backend._lib.kCCMemoryFailure)
with pytest.raises(InternalError):
- backend._check_response(backend._lib.kCCDecodeError)
+ backend._check_cipher_response(backend._lib.kCCDecodeError)
def test_nonexistent_aead_cipher(self):
from cryptography.hazmat.backends.commoncrypto.backend import Backend
diff --git a/tests/hazmat/primitives/test_aes.py b/tests/hazmat/primitives/test_aes.py
index 173075d6..5bde7d3c 100644
--- a/tests/hazmat/primitives/test_aes.py
+++ b/tests/hazmat/primitives/test_aes.py
@@ -225,6 +225,6 @@ class TestAESModeGCM(object):
"gcmEncryptExtIV192.rsp",
"gcmEncryptExtIV256.rsp",
],
- lambda key: algorithms.AES(key),
- lambda iv, tag: modes.GCM(iv, tag),
+ algorithms.AES,
+ modes.GCM,
)
diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py
index 49b73f01..6428b03e 100644
--- a/tests/hazmat/primitives/utils.py
+++ b/tests/hazmat/primitives/utils.py
@@ -90,7 +90,8 @@ def aead_test(backend, cipher_factory, mode_factory, params):
cipher = Cipher(
cipher_factory(binascii.unhexlify(params["key"])),
mode_factory(binascii.unhexlify(params["iv"]),
- binascii.unhexlify(params["tag"])),
+ binascii.unhexlify(params["tag"]),
+ len(binascii.unhexlify(params["tag"]))),
backend
)
decryptor = cipher.decryptor()
@@ -108,12 +109,13 @@ def aead_test(backend, cipher_factory, mode_factory, params):
encryptor.authenticate_additional_data(binascii.unhexlify(aad))
actual_ciphertext = encryptor.update(binascii.unhexlify(plaintext))
actual_ciphertext += encryptor.finalize()
- tag_len = len(params["tag"])
- assert binascii.hexlify(encryptor.tag)[:tag_len] == params["tag"]
+ tag_len = len(binascii.unhexlify(params["tag"]))
+ assert binascii.hexlify(encryptor.tag[:tag_len]) == params["tag"]
cipher = Cipher(
cipher_factory(binascii.unhexlify(params["key"])),
mode_factory(binascii.unhexlify(params["iv"]),
- binascii.unhexlify(params["tag"])),
+ binascii.unhexlify(params["tag"]),
+ min_tag_length=tag_len),
backend
)
decryptor = cipher.decryptor()
@@ -309,6 +311,9 @@ def aead_tag_exception_test(backend, cipher_factory, mode_factory):
with pytest.raises(ValueError):
mode_factory(binascii.unhexlify(b"0" * 24), b"000")
+ with pytest.raises(ValueError):
+ mode_factory(binascii.unhexlify(b"0" * 24), b"000000", 2)
+
cipher = Cipher(
cipher_factory(binascii.unhexlify(b"0" * 32)),
mode_factory(binascii.unhexlify(b"0" * 24), b"0" * 16),